
(*
================================================================================

    This file is part of OpenTemplot2024, a computer program for the design of model railway track.
    Copyright (C) 2024  Martin Wynne and OpenTemplot contributors.    email: martin@85a.uk

    This program is free software: you may redistribute it and/or modify
    it under the terms of the GNU General Public Licence as published by
    the Free Software Foundation, either version 3 of the Licence, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    See the GNU General Public Licence for more details.

    You should have received a copy of the GNU General Public Licence
    along with this program. See the file: licence.txt

    Or if not, refer to the web site: https://www.gnu.org/licenses/

================================================================================

   This file was saved from Delphi5

   This file was derived from Templot2 version 245a

*)


(*
================================================================================
	Changes applied
================================================================================
	SC 17-SEP-2024 556
		calc_fill_chair_outline modified to always generate chair labels,
		irrespective of state of brick_form.Showing,
 sc 17-sep-2024 556.

 SC 22-SEP-2024 556
  assign print_chair_labels_font
 sc 22-sep-2024 556.
	
================================================================================
*)

unit math_unit;

{$MODE Delphi}

{$ALIGN OFF}

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ExtCtrls, ComCtrls, MaskEdit, FileCtrl, Clipbrd,

  bgkeeps_unit, pad_unit;

type
  Tmath_form = class(TForm)
    edit_panel: TPanel;
    big_label: TLabel;
    blue_corner_panel: TPanel;
    size_updown: TUpDown;
    colour_panel: TPanel;
    colour_patch: TImage;
    math_editbox: TEdit;
    ok_button: TButton;
    datestamp_label: TLabel;
    font_button: TButton;
    cancel_button: TButton;
    reveal_timer: TTimer;
    overwrite_label: TLabel;
    procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
    procedure FormActivate(Sender: TObject);
    procedure colour_panelClick(Sender: TObject);
    procedure size_updownClick(Sender: TObject; Button: TUDBtnType);
    procedure FormCreate(Sender: TObject);
    procedure font_buttonClick(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure reveal_timerTimer(Sender: TObject);
    procedure overwrite_labelClick(Sender: TObject);
    procedure math_editboxKeyPress(Sender: TObject; var Key: Char);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  Ttimbcentre_wait=record
                     pex1:Tpex;
                     pex2:Tpex;

                     pex_ms:Tpex;        // 237b  centre-line at stock rails..
                     pex_ts:Tpex;

                     kq:extended;
                     shove_code:integer;
                     valid:boolean;
                   end;//record

  Tmake_trans_data=record                         // data record for the make transition from current function.
                     old_rad1_orgx:extended;
                     old_rad1_orgy:extended;
                     rad_1st:extended;

                     old_rad2_orgx:extended;
                     old_rad2_orgy:extended;
                     rad_2nd:extended;

                     cen_apart:extended;

                     peg1x:extended;
                     peg1y:extended;
                     peg2x:extended;
                     peg2y:extended;
                   end;//record


  Tdummy_vehicle_info=record          // 0.98.a  dummy vehicle for clearance checks

                       // all in prototype inches...

               dv_start:       extended;   // to first axle/bogie-pin from CTRL-0
               dv_length:      extended;   // body length
               dv_width:       extended;
               dv_clearance:   extended;
               dv_wheelbase:   extended;
            end;

  Tpeg_indicator_string=string[2];  // 208b

const

  pt_templates_help_str:string='    `0Plain  Track  Templates`9'
  +'||Plain track templates are implemented within Templot as the approach track to an invisible dummy turnout.'
  +'||This has several important consequences which are not immediately obvious :'

  +'||1. The various settings for turnouts will continue to be accepted, but will be ignored unless and until you select the `0TEMPLATE > INSERT TURNOUT IN PLAIN TRACK`1 menu item.'

  +'||2. Plain track is handed LEFT and RIGHT just as for a turnout. This is important when entering the curving radius positive or negative,'
  +' and when using the `0TOOLS > MAKE DOUBLE TRACK`1 functions. The current hand is always shown in the information panel, and can be swapped over without disturbing the alignment'
  +' by selecting the `0TEMPLATE > INVERT HANDING`1 menu item.'

  +'||3. For mouse action adjustments to the overall length of a plain track template, either the `0F3`2 or the `0F4`2 mouse action can be used.'

  +'||4. The ends of a length of plain track are at the fixing peg positions `0PEG ON DATUM`1 (`0CTRL-0`2, zero) at the left-hand datum end on startup; and `0PEG ON JOINT`1 (`0CTRL-1`2) at the right-hand end on startup.'

  +'||5. The sleeper spacing and numbering, and the rail length joints, commence from the right-hand (`0CTRL-1`2) end on startup. For easier recognition this end is marked across on the screen (but not on the printed templates).'

  +'||To swap the plain track length end-for-end on the existing alignment, so that the numbering and joints commence from the opposite end, click the `0TEMPLATE > SWAP END-FOR-END`1 menu item (`0CTRL-N`2).'
  +'||To change the plain track rail lengths and sleeper spacings select the `0REAL > PLAIN TRACK OPTIONS > RAIL LENGTHS AND SLEEPER SPACINGS...`1 menu item.'
  +'||To change the sleeper sizes and randomizing settings select the `0REAL > TIMBERING >`1 menu items.'

  +'||When you change to plain track using the `0TEMPLATE > CONVERT TO PLAIN TRACK`1 menu item, your current turnout or half-diamond is replaced with a length of plain track on the same alignment.'
  +' You can restore a turnout with the same settings by selecting the `0TEMPLATE > INSERT TURNOUT IN PLAIN TRACK`1 menu item.'
  +' The current peg position is used to locate the switch front rail joint (`0CTRL-1`2) position of the inserted turnout.'
  +' If the peg is not in a sensible position for this, the peg is reset to the datum (`0CTRL-0`2).'

  +'||( Note that if the turnout included any blanking, this will be cancelled and the full length of the turnout template will be converted to plain track.'
  +' Blanking is not available for plain track, as the same effect is more easily obtained by simply changing the overall length.)'

  +'||You may need to click the `0TEMPLATE > SWAP FACING-TRAILING`1 menu item (`0CTRL-N`2) to get the inserted turnout correctly aligned and facing in the required direction.'
  +'||You can remove the inserted turnout by clicking the `0TEMPLATE > CONVERT TO PLAIN TRACK`1 menu item.';

  make_trans_help_str:string='php/201    `0Make  Transition  Link  from`9|    `0Control Template to Background Template`9'

    +'||For diagrams and a tutorial sequence showing the use of this function, please refer to the Templot Companion pages on the Templot web site or click `0more information online`a above.'

    +'||This function creates a new control template on a transition curve alignment, which can be used to link two existing templates.'
    +'||The 1st radius and radial centres are taken from the existing control template. The 2nd radius and radial centres are taken from the selected background template.'
    +'||The existing control template is automatically stored on the background.'

    +'||N.B. In order to produce a usable result in all circumstances, this function uses a lower level of calculation precision than other Templot functions.'
    +' Occasionally you may notice small discrepancies in the rail alignments when viewed zoomed-in to high magnification on the screen. This will be of no consequence in practical track planning.'

    +'||There are two possible transition curves in any given case, one of each hand. In many cases it is not obvious which hand is required.'
    +' If the first one tried is not correct, the dialog which appears allows a template of the opposite hand to be easily created instead.'

    +'||For this function to work the following conditions must be fulfilled:'
    +'||1. One of the original templates can be straight, but not both.'
    +'||2. The track centre-lines must not intersect (cross), even if extended.'
    +'||(This means that no part of the imaginary full circle or straight line which would be created by extending the track centre-line of one original template may intersect (cross)'
    +' any part of the imaginary full circle or straight line which would be created by extending the track centre-line of the other original template.)'
    +'||3. If the two imaginary circles are completely separate, the result will be an S-curve transition.'
    +'||4. If the smaller imaginary circle is completely contained within the larger imaginary circle, the result will be a C-curve transition.'
    +'||5. If the two circles are just touching (for example if the two templates have been aligned at a common boundary using the peg and notch functions), the result will be a zero-length transition at the boundary.'
    +'||6. The resulting transition must not be more gentle than the current `0MAX SPIRAL`1 setting permits. For a C-curve transition, this means that there must be a sufficient difference between the two radii.'
    +' For more information click the `0PROGRAM > EXPERT > TRANSITION MATHS > MAX SPIRAL...`1 item on the `0PROGRAM PANEL`1 menus.'

    +'||The new control template will have a temporary 1 chain (66ft) (scale) length of 1st radius before the transition start position, and a similar length of 2nd radius beyond the transition end position.'

    +'||The final task is then to extend or shorten the new template at each end, and/or do likewise with the original templates, in order to position the template boundaries and create a full linking template between them.'
    +'||You can do this using the `0peg/align tools > extend/shorten the control template >`1 functions. (Or you can adjust the lengths of the templates manually using the `0F4`2 overall length mouse action.)'

    +'||Often when using this function the original templates are dummy templates created solely for the purpose. These are then deleted and the new transition template is extended at each end to replace them.'
    +' If the `0delete the original templates`1 box is ticked, the templates will be deleted automatically when you click the `0yes - continue`1 green bar to accept the new transition curve template.'

    +'||If one of the original templates is itself containing a transition curve a dialog will appear asking how you want to proceed. The new transition curve can be matched to 1st or 2nd radius on the original template,'
    +' or matched to the radius at the current position of its fixing peg. If this is within the transition zone, you will need to shorten the'
    +' original template to this position in order to make a correct boundary with the new control template. Use the `0DO > SNAP TO PEG`1 menu item, possibly preceded by `0TEMPLATE > SWAP END-FOR-END`1.'
    +' Repeat `0TEMPLATE > SWAP END-FOR-END`1 on the original template if necessary after creating the new transition (for example if the original template is a turnout).'

    +'||This function is not available if one of the original templates contains a slew. If a slew is needed it can be applied after creating the transition curve.'

    +'||For more information about using transition curves, click the button below.';

  output_mode_help_str:string='Output Mode<br><br>Sorry notes not yet available.';

  ladder_help_str:string='    `0Make  Ladder  Crossover`9'
  +'||A `0ladder`3 track is one which crosses multiple running lines diagonally, comprised of a string of diamond-crossings and/or slips.'
  +'||In Templot a ladder is created by clicking the `0TOOLS > MAKE LADDER CROSSOVER >`1 menu items repeatedly, once for each diamond-crossing.'
  +' This makes it possible to change the adjacent track spacing for each diamond-crossing as necessary -- click the `0GEOMETRY > ADJACENT TRACK CENTRES...`1 menu item.'
  +'||If slip roads are needed, they are added to the diamond-crossings afterwards.'
  +'||There are two types of ladder track: '

  +'||1. In a `0regular`3 ladder, the diagonal-road has the same radius as the main-road (or both are straight), and all crossing angles are therefore equal.'
  +' Regular ladder tracks are comprised of `0regular`3 diamond-crossings, and are commonly seen in station throats, often as single or double slips.'

  +'||2. In a `0curviform`3 ladder, the diagonal-road radius and main-road radius differ, and all the crossing angles therefore differ also.'
  +' Most commonly the diagonal-road is more sharply curved than the main roads, creating short diamond-crossings.'
  +' The diagonal road follows the initial curve of the turnout road from the running line (or can in some cases be straight across curved main roads).'
  +' Curviform ladders are comprised of `0irregular`3 diamond-crossings, and are often used to form double-junctions, and to gain access to goods yards, engine sheds and industrial sidings.'
  +' Slips are less common in curviform ladders because of the usually shorter crossing angles.'

  +'||The type of ladder is determined by the type of V-crossing which is set for the control template.'
  +' To change it click the `0REAL > V-CROSSING Options >`1 menu options. Change the V-crossing type as required before making the ladder crossover.'
  +'||For a `0regular`3 ladder set a `0regular`3 or `0generic`3 type of V-crossing.'
  +'|For a `0curviform`3 ladder set a `0curviform`3 type of V-crossing.'

  +'||<I>Experienced users:</I> It is possible to create a curviform ladder from an initial template having a regular or generic type of V-crossing. This makes it possible to adjust the radius of the ladder track independently of the turnout.'
  +' To do this use the `0tools > make double-track TS`z and `0tools > make branch track`1 functions. Adjust the branch track to the desired radius,'
  + 'and then use the `0peg/align tools > make diamond-crossing at intersection`1 function.';

var
  math_form: Tmath_form;

//________________________________________

  list_factor_x, list_factor_y:extended;

  trans_terms:integer=8;
  trans_auto:boolean=True;

  omit_neg_brackets:boolean=False;
  zero_supp:integer=1;

   adjust_trans_rad:integer=2;

   spot_mod:integer=0;
   zoom_mod:integer=0;
   datum_mod:integer=0;

   shove_along_mod:integer=0;
   shove_throw_mod:integer=0;
   shove_crab_mod:integer=0;
   shove_length_mod:integer=0;
   shove_width_mod:integer=0;
   shove_twist_mod:integer=0;

   trans_length_mod:integer=0;
   trans_start_mod:integer=0;

   slew_factor_mod:integer=0;
   slew_amount_mod:integer=0;
   slew_length_mod:integer=0;
   slew_start_mod:integer=0;

   length_mod:integer=0;
   approach_mod:integer=0;
   blanking_mod:integer=0;
   pt_blanking_mod:integer=0;    // 229a
   turnout_road_mod:integer=0;   // 209a
   main_road_mod:integer=0;      // 217a

   roll_mod:integer=0;
   roam_mod:integer=0;

   dv_mod:integer=0;
   dv_clear_mod:integer=0;

   adjacent_ts_mod:integer=0;     // 213a
   adjacent_ms_mod:integer=0;

   shift_mod:integer=0;
   slide_mod:integer=0;
   snake_mod:integer=0;

   zoffset_mod:integer=0;

   locator_mod:integer=0;   // 227a

   bunch_mod:integer=0;
   twist_mod:integer=0;
   orbit_mod:integer=0;
   both_mod:integer=0;
   sx_mod:integer=0;
   curving_mod:integer=0;
   swell_mod:integer=0;

   entry_straight_mod:integer=0;  // 0.91.b

   plat_ts_start_mod:integer=0;  // 0.93.a ...
   plat_ts_length_mod:integer=0;
   plat_ts_width1_mod:integer=0;
   plat_ts_width2_mod:integer=0;
   plat_ts_skew1_mod:integer=0;  // 207a..
   plat_ts_skew2_mod:integer=0;

   plat_ms_start_mod:integer=0;
   plat_ms_length_mod:integer=0;
   plat_ms_width1_mod:integer=0;
   plat_ms_width2_mod:integer=0;
   plat_ms_skew1_mod:integer=0;  // 207a..
   plat_ms_skew2_mod:integer=0;

   edge_ts_start_mod:integer=0;  // 215a ...
   edge_ts_length_mod:integer=0;

   edge_ms_start_mod:integer=0;
   edge_ms_length_mod:integer=0;

   gaunt_offset_mod:integer=0;  // SHIFT+F12  // 0.93.a ex 081
   gaunt_radius_mod:integer=0;  // 217b

   vee_mod:integer=0;           // F9         // 0.93.a ex 081
   hdk_mod:integer=0;           // F10        // 0.93.a ex 081

   check_diffs_len_mod:integer=0;    // check rails 0.94.a
   check_diffs_flare_mod:integer=0;  // check rails 0.94.a
   check_diffs_gap_mod:integer=0;    // check rails 0.94.a

   switch_modify_mode:boolean;

   mouse_labels:boolean;

   peg_rail:integer=8;         // move peg along rail 8 = main-road centre-line (aq=24).

   tvjp_stockjx:extended=0;         // 227a   x to exit joint
   tvjp_veejx:extended=0;           // 227a   x to exit joint
   mvjp_veejx:extended=0;           // 227a   x to exit joint
   mvjp_stockjx:extended=0;         // 227a   x to exit joint

   tvjp_ncx:extended=0;          // 227a   x to nominal end centre-line  (old tvjpx)
   tvjp_stocknx:extended=0;      // 227a   x to nominal rail end
   tvjp_veenx:extended=0;        // 227a   x to nominal rail end
   mvjp_nx:extended=0;           // 227a   x to nominal rail end

   pegx:extended=0;
   pegy:extended=0;

      // 227a ...

   locator_rail:integer=0;       // move locator free

   locatorx:extended=0;
   locatory:extended=0;
   locatork:extended=0;

   locator_now_x:integer=0;
   locator_now_y:integer=0;

   locatorx_now:extended=0;
   locatory_now:extended=0;



   notchx_now, notchy_now:extended;
   xring_now, yring_now:extended;
   ring_dia_now:extended;

   ruler_startx_now, ruler_starty_now, ruler_endx_now, ruler_endy_now:extended;   // 0.78.a

   shapes_shiftx_now:extended=0;
   shapes_shifty_now:extended=0;

   clip_size_now:extended=0;     // mm 229a ..
   clip_angle_now:extended=0;

   pt_blank_now:extended=0;     // mm 229a

   shifted_already_x:extended=0;
   shifted_already_y:extended=0;

   shapes_scaled_already:extended=1.0;

   shapes_rot_now:extended=0;
   shapes_rotated_already:extended=0;

   screenx_zoom_now:extended;
   xorg_now, turnoutx_now, startx_now:extended;
   tb_roll_percent_now, tb_roll_factor:extended;
   y_datum_now:extended;
   tst_now, os_now:extended;

   fixed_sl_now, min_sl_offset, min_sl:extended;  // 0.91.b

   slew_s_now, slew_l_now, slew_now, slew2_kmax_now: extended;

   screenx_spot_now:extended;

   out_factor_now:extended;

   endx_now:extended;
   nomrad_now:extended;
   curvature_now:extended;
   ends_apart_now:extended;
   k3n_now:extended;
   xshift_now, yshift_now, kform_now:extended;
   arm_angle_now,pegangle_now:extended;
   swing_0,swing_9,total_swing:extended;

      // 0.93.a ...

   platform_ms_start_width_ins_now:extended;
   platform_ms_end_width_ins_now:extended;

   platform_ts_start_width_ins_now:extended;
   platform_ts_end_width_ins_now:extended;

   platform_ms_start_mm_now:extended;
   platform_ms_length_mm_now:extended;

   platform_ts_start_mm_now:extended;
   platform_ts_length_mm_now:extended;

   platform_ms_start_skew_mm_now:extended=0;  // 207a
   platform_ms_end_skew_mm_now:extended=0;    // 207a
   platform_ts_start_skew_mm_now:extended=0;  // 207a
   platform_ts_end_skew_mm_now:extended=0;    // 207a

   trackbed_ms_start_mm_now:extended;      // 215a
   trackbed_ms_length_mm_now:extended;     // 215a

   trackbed_ts_start_mm_now:extended;      // 215a
   trackbed_ts_length_mm_now:extended;     // 215a

   turnout_road_endx_now:extended=0;  // 209a

   main_road_endx_now:extended=0;  // 217a


   xlabels_now:extended=0;  // 0.82.d
   ylabels_now:extended=0;  // 0.82.d

   pegx_now:extended=0;
   pegy_now:extended=0;
   udpegx_now:extended=0;

   k3n:extended=6.0;     // V-crossing unit angle
   hdkn:extended=6.0;    // K-crossing unit angle  //^^^ 0.93.a

   sl:extended=12; // 1mm flangeway 1:6

   osk_match:extended=5;  // 226a  outside slip match K-angle for OSXP OSMP peg positions

   hide_current_flag:boolean=False;
   current_is_showing:boolean=False;

   trace_mouse:boolean=True;         // trace mouse actions with redraws.

   min_rad_warn:integer=0;

           // 0.75.a 14-10-01...

  bn_wide:extended=0.75;      // full-size inches.  3/4" blunt nose BH default.
  bn_to_a:extended=4.0;       // full-size inches - 4" to A timber centre.
  wingj_sp:extended=25;       // full-size inches - 25" timber spacing at wing rail joint.
  mvj_sp:extended=12.5;       // full-size inches - 12.5" spacing to vee point rail joint.

  wingtimb_sp:extended=30;    // full-size inches - 30" timber spacing for wing rail front part of crossing (up to "A").
  veetimb_sp:extended=30;     // full-size inches - 30" timber spacing for vee point rail part of crossing (on from "A").

             // number of timbers spanned by vee rail including the "A" timber...

  vee_spco1:byte=4;  // less than 1:4
  vee_spco2:byte=5;  // up to 1:6.3/4
  vee_spco3:byte=6;  // up to 1:10
  vee_spco4:byte=7;  // up to 1:15
  vee_spco5:byte=8;  // up to 1:20
  vee_spco6:byte=9;  // over 1:20 improvised.

             // number of timbers spanned by wing rail front from joint but excluding the "A" timber...

  wing_spco1:byte=1;    // less than 1:4
  wing_spco2:byte=3;    // up to 1:6.3/4
  wing_spco3:byte=3;    // up to 1:10
  wing_spco4:byte=4;    // up to 1:15
  wing_spco5:byte=5;    // up to 1:20
  wing_spco6:byte=6;    // over 1:20


               // for half-diamond, 0.77.a 28-08-02...

    kck1_long:extended=185;   // inches full-size 15'5" k-crossing check rails, BH 1:6 and shorter.
    kck2_long:extended=197;   // 16'5" over 1:6

        // 0.95.a K wing rails ..

    k_custom_wing_long:extended=185;   // inches full-size 15'5" k-crossing wing rails, BH 1:6.5 - 1:8
    k_custom_point_long:extended=144;  // inches full-size 12' k-crossing point rails   NYI
    use_k_custom_wing_rails:boolean=False;
    use_k_custom_point_rails:boolean=False;

    k_flare_len:extended=36; // 0.94.a  was kck_fl_long:extended=36;  // inches full-size flare length on k-crossing check rails.

             // in mm...

    kckdsx:extended=0;        // x to start of DS check rail, gf. aq=28.
    kckdsox:extended=0;       // ditto oe.                        aq=29.

    kckdsflx:extended=0;      // x to DS flare bend, gf.
    kckdsflox:extended=0;     // ditto oe.

    kckdsflendx:extended=0;   // x to end of DS check rail, gf.
    kckdsflendox:extended=0;  // ditto oe.

            //--

    kckmsx:extended=0;        // x to start of MS check rail, gf. aq=26.
    kckmsox:extended=0;       // ditto oe.                        aq=27.

    kckmsflx:extended=0;      // x to MS flare bend, gf.
    kckmsflox:extended=0;     // ditto oe.

    kckmsflendx:extended=0;   // x to end of MS check rail, gf.
    kckmsflendox:extended=0;  // ditto oe.

    g:extended;

    nodraw,
    fw,k1n,h,h_inches,p,ph,k2n,toel,hdk,slwide,

    jt_slwide,   // 212a

    trtscent,trmscent,ckl_ms,ckl_ts,veelong,

    xing_flare_len, // mm  // 0.94.a  was xing_fl,
    fw_end          // mm  // 0.94.a  was fwe,

    :extended;

    flare_type:integer=0;   // 0.94.a was flared_ends:integer=0;        // 0=straight bent  1=straight machined.

    knuckle_code:integer=0;         // 214a   0=normal, -1=sharp, 1=use knuckle_radius
    knuckle_radius:extended=72.0;   // 214a   custom setting - default to 72 inches full size

    knuck_rad:extended=33.0;      // 214a   mm used in calcs. 6ft rad at 5.5mm/ft
    wing_bend_offset:extended=0;  // 214a   mm offset at end of knuckle curve  -- used for marks.

    tb_end_offset:extended=0;     // 232a


    k5_mw,          // 0.94.a  was k5, ...
    k5_me,
    k5_mr,
    k5_tw,
    k5_te,
    k5_tr,
    k5_mk,
    k5_dk,

    jbend_mw,       // 0.94.a  was jbend, ...
    jbend_me,
    jbend_mr,
    jbend_tw,
    jbend_te,
    jbend_tr,
    jbend_mk,
    jbend_dk,


    flen_mw,       // 0.94.a ...
    flen_me,
    flen_mr,
    flen_tw,
    flen_te,
    flen_tr,
    flen_mk,
    flen_dk,

    flen_mk_oe,
    flen_dk_oe,

    ftimbspmax,j,k1,kpl,k2,k3,swrad,equiv_rad,th,tradius,csradius,
    k2sint,k3sint,k1sinsw,k2sinsw,k2cost,k3cost,k1cossw,ofb,ifb,
    joggle_deep,joggle_long,k2cossw,tl,scl,pl,lh,lh_inches,

    ipx,  // 208a

    fpx,dpx,tcpx,tcpy,txpx,txpy,txpk,trpx,trpy,mcpx,mxpx,mrpx,tvjpx,tvjpk,tvjp_nk,pegx_on_pad,pegy_on_pad:extended;

    thdpx:extended;  // 226c

    pegangle:extended=0;
    arm_angle:extended=0;

    now_peg_x, now_peg_y, now_peg_k, now_peg600:extended;    // 0.79.a

    udpegx:extended=0; udpegy:extended=0; udpegangle:extended=0;

    toex,toeoxhd,toemidx,setx,setox,plx,plox,plx3,plox11,stox,heelx,heelox,
    tx,bnx,bnxx,atx,wingendx_ms,csrfpx,csrfpox,torgx,sworgx,bnox,vendx,vendox,
    csrendx,csrendox,k4,ckendx,ckflarex,ckx_ms,ckfwx,ckflarox,ckendox,ckox,

    ckfwox,wingx,wingox,wingcx,wingcox,wflarex,wflarox,wingendox,
    wingcfpx,wingcfpox,wl,wfp,k6,k7,flarecx,flarecox,flcendx,
    flcendox,cuckrad,cuckflrad_tw,cuckflrad_te,cuckfpx,cuckfpox,cucklb,k10,cuckla,

    wingx_plus,wingx_minus,wingox_plus,wingox_minus,      // 214a for knuckle radius
    wingcx_plus,wingcx_minus,wingcox_plus,wingcox_minus,

    k11,  // 0.96.a

    k8,k9,cuckfwx,cuckfwox,cuckx,cuckox,cuckflx,cuckflox,cuckendx,
    cuckendox,{stxmax,}ys,xb,xe,xs,ks,rsw,
    gmi,gmo,tbl,tbq,tbred,yns,ynsred,
    tbnumy_screen,tbnumy_output,xtba,frackeq,xtb,xclose,xtbswend,tbints,tb_xing_end,
    xclosespace,maintimb,xns,xfs,yfs,yfsred,keq,yeq,
    randend,randmod,ynsnorm,yfsnorm,eqendmod,randangle,
    randk,rety,retr,retx,retrorgx,retrorgy,
    retrmod,retcent,torgy,sworgy,min_radius,
    fbtip,fbsetx,fbsetox,fbtoex,fbplx,fbplox,fbplx3,fbplox11,fbstox,fbheelx,fbheelox

    :extended;     // all above

   xtbz:extended=0;
   xtbzz:extended=0;

   total_template_timber_length:extended=0; // 0.96.a

   total_template_sleeper_length:extended=0;  // 226a

   template_number_of_timbers:integer=0;     // 226a
   template_number_of_sleepers:integer=0;    // 226a

   smallest_radius:extended=1.0E8-5000;      // 208a for box data    init=max_rad ="straight"

      // was turnout_road  208g mod for easier searching..

   turnout_road_i:integer=0;       // -1=crossover,  0=normal, 1=long, 2=adjustable,  3=minimum
   main_road_i:integer=0;          // -1=crossover,  0=normal,         2=adjustable,  3=minimum     // 217a

   centre_lines:boolean=True;                // track centre-lines on.
   gen_centre_lines_were_off:boolean=False;  // flag used in copy_keep.

   timber_fill_overdraw_generated:boolean=False;  // 227d

   reduced_ends:boolean=True;        // sleeper reduced end marks wanted.

   guide_marks, joint_marks, timber_marks, rad_ends:boolean;

   random_flag:boolean=False;        // no timber randomising.

   shove_index:integer=0;

   other_k3n_i:integer;

   clrad1,clrad2,ssrad1,ssrad2:extended;
   xt1,yt1,xt0,xt2,yt2:extended;

   os,ts1,tst{,ycurv}:extended;
   r1,r2,tos,t0,t1,t2:extended;
   orbit_rad:extended;
   xtrans1,ytrans1,xtrans2,ytrans2:extended;
   trans_k:extended;

   rad1_orgx:extended=0;     // radial centres on pad.
   rad1_orgy:extended=0;
   rad2_orgx:extended=0;
   rad2_orgy:extended=0;

  slew_s:extended=0;           // slewing dims 7-7-99...
  slew_l:extended=600;         // 600 mm default startup slew length
  slew_t:extended=0;           // slewing angle at centre of slewing zone (used to mark slewed over rad centres).
  slew:extended=0;             // total amount of slew

  slew2_kmax:extended=2.0;    // kmax radians for slew mode 2 (tanh).
  slew2_rot:extended=0;       // rotation needed to straighten tanh curve (in trig units).
  slew2_ymax:extended=0;
  slew2_ymin:extended=0;

  slew_angle:extended=0;       // twist introduced by the slew.

  length_in_degs:extended=0;
  length_in_mm:extended=0;

  saved_pegging_shiftx:extended=0;
  saved_pegging_shifty:extended=0;
  saved_pegging_rot:extended=0;

  old_notchx:extended=0;      // 21-1-01.
  old_notchy:extended=0;
  old_notch_angle:extended=0;

  approach_last_xtb:extended=0;
  exit_last_xtb:extended=0;

  pre_examine_peg_x:extended=0;
  pre_examine_peg_y:extended=0;
  pre_examine_peg_screen:extended=0;
  examined_peg_x:extended=0;
  examined_peg_y:extended=0;
  examined_peg_screen:extended=0;

  snap_proximity_limit:extended=36;   // F7 snap limit 36" prototype  0.79.a  29-05-06

  gen_platforms:boolean=True;     // 0.93.a  generator switch for trackbed and platforms

  adjacent_edges:boolean=True;    // 0.93.a   False=adjacent tracks,  True=trackbed edges and platform edges.

           // trackbed ...

  draw_ms_trackbed_edge:boolean=False;
  draw_ts_trackbed_edge:boolean=False;

       // 215a ...

  trackbed_ms_start_mm:extended=0;            // 215a
  trackbed_ms_length_mm:extended=0;           // 215a  set to template end in strails()

  trackbed_ts_start_mm:extended=0;            // 215a
  trackbed_ts_length_mm:extended=0;           // 215a  set to template end in strails()

  trackbed_ms_width_ins:Single=90;     // Single   215a  7ft-6in full-size  half-trackbed width  MS
  trackbed_ts_width_ins:Single=90;     // Single   215a  7ft-6in full-size  half-trackbed width  TS

  cess_ms_width_ins:Single=27;         // Single   215a  2ft-3in full-size cess width MS
  cess_ts_width_ins:Single=27;         // Single   215a  2ft-3in full-size cess width TS

  draw_ms_trackbed_cess_edge:boolean=False;       // 215a
  draw_ts_trackbed_cess_edge:boolean=False;       // 215a

           // platforms ...

  platform_ms_front_edge_ins:extended=57;          //  215a  from centre       was 2ft-4.3/4in from rail
  platform_ts_front_edge_ins:extended=57;          //  215a  from centre       was 2ft-4.3/4in from rail

  platform_ms_start_width_ins:extended=144;             // 12ft defaults
  platform_ms_end_width_ins:extended=144;               // 12ft

  platform_ts_start_width_ins:extended=144;             // 12ft
  platform_ts_end_width_ins:extended=144;               // 12ft


  platform_ms_start_skew_mm:extended=0;      // 207a
  platform_ms_end_skew_mm:extended=0;        // 207a

  platform_ts_start_skew_mm:extended=0;      // 207a
  platform_ts_end_skew_mm:extended=0;        // 207a


  platform_ms_start_mm:extended=0;
  platform_ms_length_mm:extended=0;     // set to template end

  platform_ts_start_mm:extended=0;
  platform_ts_length_mm:extended=0;     // set to template end


  draw_ms_platform:boolean=False;

  draw_ms_platform_rear_edge:boolean=True;
  draw_ms_platform_start_edge:boolean=True;
  draw_ms_platform_end_edge:boolean=True;

  draw_ts_platform:boolean=False;

  draw_ts_platform_rear_edge:boolean=True;
  draw_ts_platform_start_edge:boolean=True;
  draw_ts_platform_end_edge:boolean=True;

  turnout_i:integer=0;           // free length

  switch_free:boolean=True;
  xing_free:boolean=True;
  generate_nearest:boolean=False;   // 0.76.a 29-4-02.

  timbers_equalized:boolean=False;  // 0.93.a    was True  // False=square-on,   True=equalized.
  equalizing_fixed:boolean=False;   // False=incremental, True=constant.
  square_on_angled:boolean=False;   // False=square-on to main road, True=square-on to turnout road.
  no_timbering:boolean=False;       // True = omit all timbering.

  exp_chairing:boolean=False;       // 214a  True = draw experimental chairing

  timbering_brick:boolean=False;    // 234a  template is member of a timbering brick  (set in bricklaying mode)

  waybeams_template:boolean=False;  // 235a

  use_all_bridge_chairs:boolean=False; // 235a

  use_all_SC_chairs:boolean=False;  // 237a

  use_all_RG_chairs:boolean=False;  // 234a
  use_all_FG_chairs:boolean=False;  // 234a

  use_custom_switch_opening:boolean=False;  // 233a

  key_direction:integer=0;           // 233a  chair keys   0=random/towards joint, 1=forwards, -1=backwards
  keys_towards_joint:boolean=True;   // 233a  plain track keys driven towards rail joints

  current_jaw_options_set:Tjaw_options;          // 239a



  make_trans_data:Tmake_trans_data;  // data for make transition from current function.

  exittb_i:integer;

  udpeg_rail:integer=8;             // rail for user-defined peg position.

  notch_linked_code:integer=0;      // for notch linking.
  undo_index:integer=0;             // rollbacks...
  notch_index:integer=0;

  repeat_last_index:integer=25;  // curving F6 default repeat mouse action.

  label_mousedown_X:integer=0;
  label_mousedown_Y:integer=0;

  beginner_mousedown_X:integer=0;  // 0.93.a
  beginner_mousedown_Y:integer=0;  // 0.93.a

  link_mousedown_X:integer=0;  // 0.93.a
  link_mousedown_Y:integer=0;  // 0.93.a

  zoff_now_x, zoff_now_y:integer;         // 0.91.c moved for MouseDown.
  allow_left_button_pan:boolean=False;    // 0.91.c

  outer_edges, gauge_faces, midline:boolean;

  include_front_timbers:boolean=True;    // 218a ..
  include_switch_timbers:boolean=True;
  include_closure_timbers:boolean=True;
  include_xing_timbers:boolean=True;

  approach_rails_only:boolean=False;  // 218a  default sleepered

  rolled_in_sleepered:boolean=False;  // 223a  default rails only


  f5_quarter_steps:boolean=True;       // mouse adjusts crossing angles in 1/4 steps.
  f9_quarter_steps:boolean=False;   // F9 mouse action any V-crossing angle.
  f10_quarter_steps:boolean=False;  // F10 mouse action any K-crossing angle.

  f9_gaunt_rad:boolean=True;       //  217a  F9 keeps gaunt radius fixed (modify offset)               (for tandem inserted crossings).
  f9_hd_rad:boolean=False;         //  218b  F9 keeps half_diamond radius fixed (modify K-xing angle)  (for tandem inserted crossings).

  f6_swing_fixed:boolean=False;     // 0.91.b   F6 curving option.

  printer_setup_done:boolean=False;

  mouse_action_button_down:boolean=False;     // flag to indicate he is holding the button down.

  keep_added:boolean=False;       // flag for the MAKE tools.

  udpeg_valid:boolean=False;          // no user-defined peg position data yet set.

  select_centre:boolean=False;

  mouse_draw_lines:boolean=False;

  draw_template_direct:boolean=False;   //  233b

  zoom_rectangle:boolean=False;
  group_fence_rectangle:boolean=False;
  output_boundary_rectangle:boolean=False;  // 0.93.a
  drawn_shape_rectangle:boolean=False;      // 214a

  ms_ends:boolean=True;             // 0.93.a default ends-in-line.

  program_help_str:string='program help';

  current_diff_code:integer=0;  // 0.94.a check rails ...
  null_diff:Tcheck_end_diff;    // used for errors
  mouse_diff:Tcheck_end_diff;   // current diff for mouse action

  retain_diffs_on_make:boolean=False;
  retain_diffs_on_mint:boolean=False;

  retain_shoves_on_make:boolean=False;
  retain_shoves_on_mint:boolean=False;

  retain_entry_straight_on_mint:boolean=False;  // 212b
  retain_entry_straight_on_make:boolean=False;  // 212b

  hidden_on_store:integer=0;    // 205c  >0 = control template was hidden on storing to background - delay wanted

  reveal_limit_count:integer=0; // 206a  0=time limit - abandon slide and jump it back
  slide_on_store:integer=0;     // 206a  >0 = control template was shifted on storing to background - slide wanted
  stored_xshift:extended=0;     // 206a
  stored_yshift:extended=0;     // 206a
  slide_inc:extended=0;         // 206a


  wheel_zoom_code:integer=0;    // 0.97.d  0=zoom on centre  1=zoom on mouse location  2=jump mouse location (and mouse cursor) to centre, then zoom on centre

                // init current dummy vehicle dims, prototype inches  0.98.a ...

  cdvi:Tdummy_vehicle_info=(dv_start:111;      // to first axle/bogie-pin from CTRL-0  111" = 9ft-3in
                            dv_length:780;     // body length 780" = 65ft
                            dv_width:111;      // body width 111" = 9ft-3in
                            dv_clearance:6;    // clearance on width 6" per vehicle per side
                            dv_wheelbase:558); // wheelbase / bogie centres 558" = 46ft-6in

                // position of dummy vehicle corners (control template) ...

  dv_corners_calc:Tdummy_vehicle_corners=(pt1:(x:0; y:0);
                                           pt2:(x:0; y:0);
                                           pb1:(x:0; y:0);
                                           pc1:(x:0; y:0);
                                           pb2:(x:0; y:0);
                                           pc2:(x:0; y:0);
                                           b1:(x:0; y:0);
                                           b2:(x:0; y:0);
                                           b3:(x:0; y:0);
                                           b4:(x:0; y:0);
                                           c1:(x:0; y:0);
                                           c2:(x:0; y:0);
                                           c3:(x:0; y:0);
                                           c4:(x:0; y:0);
                                           m1:(x:0; y:0);
                                           m2:(x:0; y:0);
                                           o1:(x:0; y:0);
                                           o2:(x:0; y:0);
                                           o3:(x:0; y:0);
                                           o4:(x:0; y:0) );        // 0.98.a

  dv_envelopes:array[0..dv_envelopes_c] of Tdv_envelopes; // up to 8 envelopes     215c
  dv_envelopes_index:integer=-1;                          // init none yet 215c

  radius_for_obtain:extended=1000;  // 205e  for obtain to the control   1000mm arbitrary init

  curviform_timbering:boolean=False;  // 215a

  simplified_short_angle:boolean=True;  // 228a

  no_spacings_confirm:boolean=False;   // 215b

  default_shoved_tcol:integer=$005060C0;   // 226a  colour on trackpad
  default_shoved_ocol:integer=$00B0C0D0;   // 226a  colour in output

  default_shoved_tcol_infill:integer=0;   // 226a  solid style trackpad
  default_shoved_ocol_infill:integer=0;   // 226a  solid style output

  default_shoved_trackpad:boolean=True;   // 226b
  default_shoved_bgnd:boolean=False;      // 226b

  default_shoved_output:boolean=True;     // 226b
  default_shoved_export:boolean=False;    // 226b

  inherited_mark_col:integer=clRed;      // 227a
  inherited_map_col:integer=clFuchsia;   // 227a

  inherited_use_mapping:boolean=False;   // 227a
  inherited_use_marker:boolean=False;    // 227a

  generic_was_short_swapped:boolean=False;  // 228a


  force_eq:boolean=False;  // 239b

  ms_ccl_done_clx:extended=0; // 243a
  ms_ccr_done_clx:extended=0; // 243a

  ts_ccl_done_clx:extended=0; // 243a
  ts_ccr_done_clx:extended=0; // 243a

  slide_adjust_radius:boolean=False;     // 244d
  slide_adjust_centres:boolean=False;    // 244d
  slide_adust_pegy_start:extended=0;     // 244d
  slide_adust_nomrad_start:extended=0;   // 244d
  slide_adust_trtscent_start:extended=0; // 244d

  bit_settings:integer=0;   // MW 04-08-2024    global for Tmark...


  function xy_to_dwg100(pin:Tpex):TPex;        // this function and next prepare x,y data for lists.
  function xy_to_list(pin:Tpex):TPoint;        // prepare x,y data for list.
  function blank_start(x:extended):extended;   // 17-10-02 0,76.a  blanking mods.

  function SGN(x:extended):extended;
  function SGZ(x:extended):extended;

  function round_float(x:extended; n:integer):extended;
  function round_str(x:extended; n:integer):string;
  function rad_str(r:extended; dp:integer):string; // get radius as a string.

  function limits(min,max,d:extended; var return_code:integer):extended;
  function limits_i(min,max,i:integer):integer;

  function max_i(n,m:integer):integer;              // return greatest of 2 integers.

  function max(a,b:extended):extended;              // return greatest of 2 floats.
  function min(a,b:extended):extended;              // return smallest of 2 floats.

  function remove_esc_str(msg_str:string):string;
  function insert_crlf_str(msg_str:string):string;
  function remove_invalid_str(msg_str:string):string;     // remove characters invalid in filenames.

  function lower_case_filename(name_str:string):string;   // 0.79.a lower case all filenames and change spaces to underscores.

  function invalid_85a_file_name(str:string):boolean;

  function space_lead(s:string):string;                   // replace any leading 0 chars in string with a space.
  function remove_space_str(msg_str:string):string;       // remove all space characters from string.
  function remove_multispace_str(msg_str:string):string;  // reduce multiple spaces in string to singles.
  function captext(d:extended):string;

  function fact(a:integer):extended;        // return (a! = a factorial)

  function transcalcs(draw_in_progress,auto_terms:boolean; k, tsn:extended; var xn, yn, tn, rn:extended):boolean; //  Transition equations.
  function calc_transition(rad1,rad2,zonel:extended; var cen1x,cen1y,cen2x,cen2y,apartl,kval:extended):boolean;

  function set_font(fname:string; fsize:integer; fstyle:TFontStyles; fcolour:integer):TFont;

  function time_now_modified(def:integer):integer; // modify Delphi float time format to integer;

  function check_limit(bunch,shear:boolean; var p:TPoint):boolean;  // safety check on one pair of drawing co-ordinates.
  function check_limits(var p1,p2:TPoint):boolean;                  // safety check on both pairs of drawing co-ordinates.

  function check_draw_dim_l(d:integer):boolean;         // check on a single drawing dimension.
  function check_draw_dim_w(d:integer):boolean;         // 0.93.a mods

  function extract_tbnumber_str(var tbnum_str:string):string;   // return next timber numbering string from the acummulated string.

  procedure normalize_angle(var k:extended);
  function k_ram_str(k:extended):string;                                             // get k angle as 1:n RAM string
  procedure dotransform(krot,xrot,yrot:extended; pin:Tpex; var pout:Tpex);

  function rad_tanp1_p2(p1,p2:Tpex; tn:extended; var rad,swing:extended):boolean;    // 15-9-99.

  function intarray_create(max_index:integer; zero:boolean):Pointer; // max_index is integers, not bytes.
  function intarray_get(p:Pointer; index:integer):integer;           // return value at this index.
  function intarray_max(p:Pointer):integer;                          // return max index for this array.

  procedure intarray_set(p:Pointer; index:integer; d:integer);       // enter new value d at this index.
  procedure intarray_free(p:Pointer);

  procedure memory_alert;             // do memory fail message.

  function calc_geo_radius(rout,xp,yp,kp:extended; var rin,kin,krin,gpx:extended):boolean;

  function print_control_template(pdf:boolean):integer;  // 0.91.d
  function print_entire_pad(pdf:boolean):integer;        // 0.91.d

  procedure mouse_action_selected(caption_str,mouse_str,trail_str:string);
  procedure unlock_both;

  procedure cancel_paper_bunching;

  procedure tick_not_normal;  // not printing 100%, change menu ticks.

  procedure pad_mouse_move(shift_state:TShiftState; X,Y:integer);
  procedure pad_mouse_down(mouse_button:TMouseButton; shift_state:TShiftState; X,Y:integer);

  procedure click_bgnd_to_current(bgnd_options:boolean);
  procedure click_bgnd_to_selected;

  function check_peg_on_main:boolean;

  procedure align_current_over_bgnd(index:integer; facing_facing, clicked:boolean);

  procedure snake_onto_bgnd_peg(index:integer; facing_facing, alerts:boolean);

  procedure get_cpi;     // copy/calculate current prototype info from current list.

  procedure toggle_radius_lamp;
  procedure toggle_ring_lamp;

  procedure pad_mouse_up(mouse_button:TMouseButton; shift_state:TShiftState; X,Y:integer);

  procedure swap_hand;

  procedure select_switch;

  procedure reset_defaults;

  procedure pad_all_grey;
  procedure pad_bright;
  procedure pad_silver;
  procedure pad_normal;
  procedure pad_caption(text:string);

  procedure set_xing_k_i;     // set selector list index to match current k3n.

  procedure docurving(transform_flag,slew_flag:boolean; xs,ys:extended; var xc,yc,tn,rn:extended);

  procedure rotate_turnout(k:extended;draw:boolean);    // rotate turnout k radians around peg.
  procedure peg_curve;                                  // do curving calcs for the current peg position.

  function check_radius(warn:boolean; var r:extended):boolean;    // return true if curving radius was within limits.

  procedure clear_transform_data;

  procedure invert_curving;
  procedure invert_handing;
  procedure swap_end_for_end;

  procedure crop_approach;

  procedure make_double_track_calcs(side:integer);

  procedure enable_slewing(mode:integer; do_peg_calcs_first:boolean);

  procedure insert_half_diamond;

  procedure convert_to_regular_half_diamond;  // 0.93.a

  procedure convert_to_turnout;

  procedure convert_to_or_from_gaunt(to_gaunt:boolean);  // 0.93.a ex 081

  function make_mirror_on_peg:boolean;

  function make_diamond_crossing:boolean;
  function make_crossover(simple,allow_curviform,for_slip:boolean):boolean;  // 215a  209c

  function snap_onto_bgnd_pegs(always:boolean):boolean;  // 0.79.a   27-05-06

  procedure quick_gauge_click(i:integer);   // quick change of gauge and scale.

  procedure lengths_click(degs:boolean);

  function check_black_white:boolean;    // return True if he cancels (called from change printer colours).

  procedure fix_radius(rad:extended; click:boolean);     // set up fixed-radius curving.

  function clrad_at_x(x:extended):extended;      // return the track centre-line radius at this xs.

  procedure transition_clicked(trans_code:integer);
  procedure make_transition_click(trans_hand:integer);
  function make_transition_from_current_calcs:integer;

  function do_auto_trans_swing_adjust(old_rad2_orgx, old_rad2_orgy:extended):integer;

  function do_degs_length_adjust(length_in_degs:extended; var length_in_mm:extended):integer;

  function set_black_and_white:boolean;

  procedure pt_convert(show_it:boolean);   // convert turnout to plain track on same alignment.

  function check_infill_ok(str:string):boolean;   // ensure generator is on if he wants timber infill.

  procedure adjacent_redraw;     //  do a re-draw after selecting/deselecting adjacent rails.
  procedure normalize_kform;

  procedure keep_colours1;           // scheme 1 colours for the keep form...
  procedure keep_colours2;           // scheme 2 colours for the keep form...
  procedure keep_colours3;           // scheme 3 colours for the keep form...

  procedure railedges(gf,oe,cl:boolean);

  procedure no_rails_warn;

  function pad_view_fit_bgnd(who,brick_col:integer):boolean;  // zoom to fit background templates.

  procedure explode_shrink(new_screenx:extended; loop,wheel:boolean);     // mouse wheel mode added 0.97.d

  procedure calc_peg_pad_pos;        // calculate position of peg on screen.

  procedure calc_locator_pad_pos;    // calculate position of locator on screen.    227a

  procedure new_notch(notch_data:Tnotch; link_group:boolean);    // set new notch position on pad.

  procedure shift_radial_centre(to_notch:boolean);

  function get_peg_for_notch:Tnotch;
  function get_current_notch:Tnotch;

  procedure shift_rotate_group(x_move,y_move,k_rot:extended; egg_timer:boolean);

  procedure do_group_link_to_notch;
  procedure do_linked_notch;         // link notch to control template.

  procedure peg_indicator_click;

  procedure normal_adjust_menu_entry_click;

  procedure make_double_track(side:integer);   // change to plain track in place of the current adjacent track.

  procedure make_pt_geo_rad(int_ext:integer; click:boolean);

  function make_separate_approach(click:boolean):boolean;

  function normalize_transition:boolean; // normalize to template extents if zone outside them.

  procedure set_plain_track(pt,clear_blanking:boolean);

  function shove_number_clicked(X,Y:integer):boolean;  // screen co-ords of a click, is it on a timber number?

  function chair_label_clicked(X,Y:integer):boolean;  // screen co-ords of a click, is it on a chair label?  MW 03-08-2024  555a

  procedure mouse_on_chair_label(X,Y:integer);        // flag mark if mouse currently over it   MW 05-08-2024  555a

  procedure mouse_on_timber_number(X,Y:integer);      // highlight timber number if mouse currently over it.

  function symbol_clicked(X,Y:integer):boolean;  // 227a is it on a symbol?

  function checkrail_label_clicked(X,Y:integer):boolean;  // 0.94.a screen co-ords of a click, is it on a check-rail label?

  procedure mouse_on_check_label(X,Y:integer);            // 0.94.a highlight check-rail label if mouse currently over it.


  procedure clear_current_name;

  function snake_onto_this_peg(notch_info:Tnotch; keep_dims:Tkeep_dims; facing_facing, alerts:boolean):boolean;      //227d

  function aq25offset(xs:extended; var k:extended):extended;   // return offset at xs on turnout road centre-line.

  function curved_onto_calc(org_rad, onto_rad:extended):extended;
  function equiv_rad_calc(curved_rad,onto_rad:extended):extended;    // 217a

  function aq3offset(xtb:extended; var k:extended):extended;   //  general utility - return offset at any xtb on aq=3 (curved stock rail).
  function aq2offset(xtb:extended; var k:extended):extended;   //  general utility - return offset at any xtb on aq=2 (curved turnout rail).

  procedure shift_all_group(ask_sync_shapes:boolean);  // add current xshift_keeps, yshift_keeps to all selected keeps,
  procedure twist_all_group(ask_sync_shapes:boolean);  // rotate all group templates.

  function check_shove:boolean;    // check some timbers there for shove / select.

  procedure fix_approach_length(snap:boolean; lmod:extended);
  procedure fix_exit_length(lmod:extended);

  procedure mirror_keeps_x;     // X mirror a group of templates about the notch.

  procedure insert_turnout;

  procedure set_trans_position_from_ctrl_0(os_offset,zone_len:extended);
  procedure set_slew_position_from_ctrl_0(start_offset, zone_len:extended);

  procedure new_label_position;

  procedure warn_group_colour;

  procedure snap_current_to_bgnd(notch_pos:Tnotch; facing_to_trailing:boolean);   // 0.79.a

  procedure match_rolled_lengths(ctrl:integer);

  procedure show_switch_info(full_size_mm,already_showing:boolean);  // 208a

  function calc_switch(sw_info:Tswitch_info; h_diamond,current_calc:boolean):integer;  // calculate switch - return error code,

  function set_csi_from_switch_info(sw_info:Tswitch_info):boolean;  // set current switch from supplied info.

  function find_shove(str:string; create_new:boolean):integer;     // find str in current shove list, or create an empty slot for it.

  procedure show_and_redraw(on_idle, allow_rollback_to_this:boolean);  // F12 reset.
  procedure redraw_pad(on_idle, allow_rollback_to_this:boolean);

  procedure cancel_adjusts(new_action:boolean);  //  cancel all adjusts and restore flags, etc..

  procedure clear_shovedata;             // clear any current timber shoves.

  procedure clear_symboldata;     // clear any current symbols.   227a

  procedure clear_check_diffs;           // clear any check-rail end diffs. 0.94.a

  procedure delete_null_shove_entries;   // remove any unshoved entries from current shove list.

  procedure shove_along_mouse_action;
  procedure shove_throw_mouse_action;
  procedure shove_crab_mouse_action;
  procedure shove_length_mouse_action;
  procedure shove_width_mouse_action;
  procedure shove_twist_mouse_action;

  procedure fill_kd(var keep_info:Ttemplate_info);  // copy control template data to the keep record.
  procedure copy_keep(keep_info:Ttemplate_info);    // get control template data from a keep.

  procedure update_lengths(var box_kd:Tkeep_dims);            // update stored plain track settings to match the control template.
  procedure update_timbering(var box_kd:Tkeep_dims);          // update stored timbering to match the control template.
  procedure update_customize_xing(var box_kd:Tkeep_dims);     // 214b  update V-crossing customizing
  procedure update_trackbed_edges(var box_kd:Tkeep_dims);     // update stored trackbed edges settings to match the control template.
  procedure update_centre_lines(var box_kd:Tkeep_dims);               // update stored track centre-line settings to match the control template.
  procedure update_centre_line_offset_options(var box_kd:Tkeep_dims); // update stored centre-line offset options to match the control template.
  procedure update_rail_section(var box_kd:Tkeep_dims);               // update stored rail-section data to match the control template.
  procedure update_radius_warning(var box_kd:Tkeep_dims);     // update stored radius warning limit to match the control template.

  procedure update_chair_options(var keep_info:Ttemplate_info);      // update stored chair options to match the control template.     221a

  procedure copy_template_info_from_to(clear_arrays:boolean; var from_info, to_info:Ttemplate_info);

  procedure alert_no_bgnd;
  procedure alert_no_unused;
  procedure alert_no_library;
  function alert_no_group:boolean;    // return False if any get selected.

  procedure mint_new_current(locked_length:integer; show_it:boolean);  // 208d locked_length added

  procedure normalize_transforms;     // update shifts so that xform, yform can be zero,

  procedure gocalc(calcs_code, mode:integer);    //  a new turnout wanted - let's do some calcs.

  procedure shift_onto_notch(click,min_rot:boolean); // 0.93.a ex 081

  function outoflist(aq,nl,xy:integer):integer;

  function calc_snap_peg_data(code:integer):Tnotch;      // 0.79.a  27-05-06

  procedure do_hide_current;      // set flags to hide control template.

  procedure unlink_group;

  procedure caption_add(text:string);       // replace '...' in pad caption with '=' + text.

  procedure redraw(on_idle:boolean);           // do a screen redraw.

  procedure obtain_switch(n:integer);        // obtain control template switch from template in list.
  procedure obtain_plain_track(n:integer);   // obtain control template plain-track settings from template in list.

  function gauge_dims(from_list,mod_rads,mod_gauge_data:boolean):extended;    // set up gauge and scale from selected item in list.

  procedure set_y_datum;          // set up default y position.

  procedure rescale_notch(ratio:extended);

  procedure printer_setup(cal_in_progress, black_white_setup:boolean);

  // OT2024  function ask_impact_matrix(prindex:integer; prstr:string):boolean;    // ask if printer is dot-matrix impact type.

  procedure mouse_symbol_click;  // symbol clicked on action form.

  procedure action_label_click;

  function mouse_x(X:integer):extended;     // return x mm at this pad X pixels.
  function mouse_y(X,Y:integer):extended;   // return y mm at this pad Y pixels.

  function check_grey_paper:boolean;     // return True if the paper colour is near to mid grey.
  function check_dark_paper:boolean;     // return True if the paper colour is black or very dark.

  procedure set_grid_spacings(calling_form:TForm);

  procedure set_six_foot_ring;
  procedure get_ring_size;
  procedure get_ring_location;

  procedure do_railedges;      // called from rail_options_unit

  procedure error_b6_lh_reset;  // 0.93.a

  procedure set_pad_start_colours(change_flag:boolean);

  function calcturnout:boolean;             // calc all the turnout dimensions.

  procedure update_rollback_register;       // maintain the roll-back register.

  procedure enable_peg_positions;  // enable/disable the peg options (for Ctrl-# KB shortcuts).

  procedure do_info_colours; // indicate if control template visible and accessible...


  procedure init_rollbacks;    // init all roll-backs.
  procedure templot_init;      // this routine runs only once.


  procedure check_pad_views;   // 0.91.c  process pad view rollbacks once per second.

  procedure mouse_action_release(cancelling_adjusts:boolean);   // 0.79.a  moved 0.91.c

  procedure do_nothing;

  procedure update_ruler_div;   // 0.93.a

  function check_control_template_is_valid(cancel_str:string):boolean;   // 0.93.a

  function store_and_background(click,zero_it:boolean):boolean;  // 0.93.a click=True means direct click by user.

  procedure extend_template_from_zero;  // 0.93.a

  procedure debug(str:string; abc:extended); // 0.93.a

  function get_checkrail_diff(code:integer):Tcheck_end_diff;    // 0.94.a

  procedure set_checkrail_diff(code:integer; this_diff:Tcheck_end_diff); // 0.94.a

  procedure draw_dummy_vehicle_on_control_template(for_env:boolean; on_canvas:TCanvas);   // 0.98.a

  function get_current_diffed_dims:string;

  procedure action_panel_hint(hint_str:string);   // 205c  set hollow-triangle mouse action hint

  function create_id_number_str(idnum,hand:integer; startx,turnoutx,ipx,fpx:extended; plain_track,half_diamond,any_omitted:boolean):string;    // 208a

  function get_store_beginner_help:string;  // 208a

  function get_arc_radius(p1,p2,p3:Tpex):extended; // 208a

  function get_arc_centre(p1,p2,p3:Tpex; var arc_centre:Tpex):boolean;  // 208a

  function make_branch_crossover:boolean;     // 209c

  function do_show_modal(modal_form:TForm):TModalResult;   // 212a Wine bug
  procedure show_modal_message(msg:string);                // 212a Wine bug

  procedure retain_on_make;  // 213a

  function ram_clm_str(ram_kn:extended):string;

  procedure companion_help(topic_url_str:string);     // 214a

  function count_substrings(sub_str,text_str:string):integer;      // 215a

  function calculate_turnout_radius(curving_rad:extended; var heel_notch,mid_notch,xing_notch:Tnotch):extended;        // 218a
  function calculate_turnout_radius_beyond(curving_rad:extended; var xing_notch,mid_notch,end_notch:Tnotch):extended;  // 218b

  function calculate_return_curve_radius(var exit_notch,mid_notch,trp_notch:Tnotch):extended;  // 218d

  function calc_peg_dims(code:integer; var pegx,pegy,pegangle:extended):Tpeg_indicator_string; // 218a moved to interface for make tandem

  function tpex_from_tnotch(notch:Tnotch):Tpex;
  function get_nearer_pex(p0,p1,p2:Tpex):Tpex;
  function get_notch_distance(n1,n2:Tnotch):extended;
  function get_snap_peg_xy_data(code:integer):Tnotch;

  procedure shift_group_into_positive_quadrant(warn:boolean);    // 219a

  procedure set_current_notch(notch_data:Tnotch);

  procedure default_template;      // a short straight plain track template  223c

  function count_character_in_string(ch:char; str:string):integer;   // 226c

  function do_truncx(x:extended):integer;      // 226d
  function do_fracx(x:extended):extended;      // 226d

  function hold_the_control:Ttemplate_info;                                               // 227a

  function set_3D_chair_jaw_defaults(customized:boolean; length:integer):Tjaw_options;   // 239a

  procedure unhold_the_control(var hold_ti:Ttemplate_info);   // 227a

  procedure shove_delete(index:integer; var shoved_timbers:Tshoved_timbers);    // 227a   // array of Tshoved_timber

  //procedure copy_normal_chairs_array(clear_array:boolean; var from_array,to_array:Tnormal_chairs);   // 242b

  procedure copy_shove_array(clear_array:boolean; var from_array,to_array:Tshoved_timbers);
  procedure copy_symbols_array(clear_array:boolean; var from_array,to_array:Tsymbols);           // 227a
  procedure copy_timbers_array(clear_array:boolean; var from_array,to_array:Ttimber_counts);     // 227a

  procedure copy_jaws_array(clear_array:boolean; var from_array,to_array:Tjaws);         // 237c
  procedure copy_seats_array(clear_array:boolean; var from_array,to_array:Tseats);       // 237c
  procedure copy_keys_array(clear_array:boolean; var from_array,to_array:Tkeys);         // 237c
  procedure copy_jmods_array(clear_array:boolean; var from_array,to_array:Tjmods);       // 237c



//______________________________________________________________________________

implementation

{$BOOLEVAL ON}


{$R *.lfm}

uses

  ShellAPI, Math, Printers,

  alert_unit, control_room, help_sheet, colour_unit, dxf_unit,
  mint_unit, info_unit, plain_track_unit, edit_memo_unit, keep_select,
  print_settings_unit, panning_unit, action_unit, preview_unit, gauge_unit,
  grid_unit, xing_select, jotter_unit, shove_timber, entry_sheet, bgnd_unit,
  switch_select, wait_message, print_unit, enter_timber,
  calibration_unit, export_unit, platform_unit, data_memo_unit,
  math2_unit, check_diffs_unit, rail_options_unit, file_viewer, chairs_unit,
  trackbed_unit, create_tandem, track_spacing_unit, gaps_unit,
  brick_unit, heave_chairs, pdf_laz_unit;

const

  grid_help_all_str:string='        Grid  Spacings'
   +'||The grid line spacings entered here will apply to both the screen trackpad and the printed templates.'
   +'||The grid lines are intended only to provide a "graph-paper" background to aid planning and alignment;'
   +' changing these spacings has no effect on the design of the templates or their position on the drawing.'

   +'||It is possible to use different spacings for the horizontal and vertical grid lines'
   +' ( so that the grid boxes are no longer squares ), but the units for both must be the same (i.e. you'
   +' can''t for example have mm dimensions for one direction and inches for the other).'

   +'||If the current trackpad zoom setting causes the grid lines to be too closely spaced, Templot will omit some of them'
   +' from the screen.'

   +'||If the current print size setting causes the grid lines to be too closely spaced, Templot will omit some of them'
   +' from the printed output.'

   +'||The minimum grid spacing is 0.1 mm (or 0.004") which is useful when zooming in to examine fine detail.';

var

  last_code_generated:integer=0;
  int_size:integer;                 // size in bytes of integers (4 at present);
  temp_font:TFont;

  temp_float:extended=0;  // 244e
  temp_angle:extended=0;  // 244e

   no_rad_confirm:boolean=False;

   switch_type:integer=-1;        // type of switch.  0 = curved planing or straight switch; -1 = semi-curved switch;  1 = double-curved switch.

   k4_limit:extended=Pi/3;           // 0.93.a turnout road mustn't swing more than 60 degrees (for straight turnout).

   sep_limit:extended=0.0001;      // make transition rad centres, separation limit 0.0001 mm for a match (arbitrary).

   shovetimbx_now:extended=0;
   shovex_now:extended=0;
   shovek_now:extended=0;
   shoveo_now:extended=0;
   shovel_now:extended=0;
   shovew_now:extended=0;
   shovec_now:extended=0;

   yd:extended;                      // y datum for drawing.

   wing_tbcount:integer;

   switch_only:integer;              // = X div form width.

   f7_snap_allow:boolean=True;       // 0.82.a  allow F7 snapping (Shift key toggle).

   f6_swing_angle:extended=0;        // 0.91.b  F6 options.

   in_progress:boolean;            // gocalc no re-entry flag.

   tbn:integer;

   number_point_screen,number_point_output:Tpex;   // 208a

   length_now, xing_now, curving_now:integer;
   shift_now_x, shift_now_y:integer;

   labels_now_x:integer=0;     // 0.82.d
   labels_now_y:integer=0;     // 0.82.d

   ring_now_x, ring_now_y:integer;
   ringdia_now_y:integer;
   peg_now_x, peg_now_y:integer;
   notch_now_x, notch_now_y:integer;
   ruler1_now_x, ruler1_now_y, ruler2_now_x, ruler2_now_y:integer;

   shovetimb_now:integer;
   shove_now_x, shove_now_y:integer;

   approach_now, blanking_now, roll_now, roam_now, zoom_now, spot_now, datum_now,
   trans_length_now, trans_start_now,
   slew_start_now, slew_length_now,
   slew_amount_now:integer;
   slew_factor_now:integer;
   porg_now_x,porg_now_y:integer;

   entry_straight_now:integer;  // 0.91.b

   turnout_road_now_X:integer=0;  // 209a
   main_road_now_X:integer=0;     // 217a

      // 0.93.a

   platform_ms_start_width_now_Y:integer;
   platform_ms_end_width_now_Y:integer;

   platform_ts_start_width_now_Y:integer;
   platform_ts_end_width_now_Y:integer;

   platform_ms_start_now_X:integer;
   platform_ms_length_now_X:integer;

   platform_ts_start_now_X:integer;
   platform_ts_length_now_X:integer;

   platform_ms_start_skew_now_X:integer=0;   // 207a
   platform_ms_end_skew_now_X:integer=0;     // 207a
   platform_ts_start_skew_now_X:integer=0;   // 207a
   platform_ts_end_skew_now_X:integer=0;     // 207a


   trackbed_ms_start_now_X:integer;      // 215a ...
   trackbed_ms_length_now_X:integer;
   trackbed_ts_start_now_X:integer;
   trackbed_ts_length_now_X:integer;


   gaunt_now:integer=0;
   veexing_now:integer=0;
   hdxing_now:integer=0;

   gaunt_offset_now_mm:extended=0;
   vee_now:extended=0;
   hdkn_now:extended=0;

   gaunt_rad_now:integer=0;      // 217b
   gaunt_curvature_now:extended=0;  // 217b

   rect_now_X:integer=0;
   rect_now_Y:integer=0;

   output_rectangle_x_now:extended=0;
   output_rectangle_y_now:extended=0;


   dv_now:integer=0;              // 0.98.a  dummy vehicle
   dv_clear_now:integer=0;
   dv_start_now:extended=0;
   dv_copy_start_now:extended=0;  // 215c
   dv_clearance_now:extended=0;

   ts_adj_now:integer=0;          // 213a
   trtscent_pi_now:extended=0;

   ms_adj_now:integer=0;
   trmscent_pi_now:extended=0;


   mouse_diffs_now_X:integer=0;       // 0.94.a ...
   mouse_diffs_now_Y:integer=0;

   mouse_check_len_mm_now:extended=0;
   mouse_check_flare_mm_now:extended=0;
   mouse_check_gap_mm_now:extended=0;

   diff_dir:extended=1.0;  // +/-1 mouse diffing direction

   printsize_now_X:integer;

   shapes_now_x,shapes_now_y:integer;

   clip_size_now_X:integer;   // 229a ..
   clip_angle_now_Y:integer;

   pt_blanking_now:integer=0; // 229a

   rectangle_in_progress:boolean=False;
   rectangle_exists:boolean=False;

   mouse_drawing_in_progress:boolean=False;

   mouse_down_x:extended=0;
   mouse_down_y:extended=0;

   draw_mouse_down_X:integer=0;
   draw_mouse_down_Y:integer=0;

   temp_mouse_X:integer=0;
   temp_mouse_Y:integer=0;

   toggle_1:integer=0;     // flashing lamps toggles.
   toggle_2:integer=0;

   twist_dir:extended=1;
   orbit_dir:extended=1;

  opposite_way:boolean=False;                 // reverse sense of mouse actions for turnout facing opposite way.

  panel_mousedown_X:integer=0;
  panel_mousedown_Y:integer=0;

  now_pen,prior_pen:integer;

  done_rails:boolean=False;

  pad_pegx, pad_pegy:integer;               // peg screen co-ords.
  rad_centx, rad_centy:integer;             // screen co-ords for curving rad centre.
  pad_locatorx, pad_locatory:integer;       // 227a locator screen co-ords.

  peg_arm1:Tmark;
  peg_arm2:Tmark;

  timbcentre_wait:Ttimbcentre_wait;

  highlighted_timbnum_X:integer=0;
  highlighted_timbnum_Y:integer=0;
  highlighted_timbnum_str:string='';      // flag no timber highlighted.

  highlighted_round_rect_x1:integer=0;    // 0.94.a for check-rail label locations
  highlighted_round_rect_y1:integer=0;
  highlighted_round_rect_x2:integer=0;
  highlighted_round_rect_y2:integer=0;

  timb_str:string='';  // globals for Tmark...
  dxf_code:integer=0;

  current_switch_name:string='';

  auto_spiral_adjust:boolean=True;   // for make double track.

  wing_reach1:extended=48;   // full-size inches - size 1 wing rail reach length (forward from "A").
  wing_reach2:extended=78;   // full-size inches - size 2 wing rail reach length (forward from "A").

  ck_working1:extended=78;   // full-size inches - size 1 check rail working length (back from "A").
  ck_working2:extended=108;  // full-size inches - size 2 check rail working length (back from "A").
  ck_working3:extended=138;  // full-size inches - size 3 check rail working length (back from "A").

  ck_ext1:extended=48;       // full-size inches - size 1 check rail extension length (forward from "A").
  ck_ext2:extended=78;       // full-size inches - size 2 check rail extension length (forward from "A").

  check_ms_wklen:extended=108;   // control template check MS lengths
  check_ms_extlen:extended=48;
  wgl_ms_len:extended=48;        // ditto wing.

  check_ts_wklen:extended=108;   // control template check TS lengths
  check_ts_extlen:extended=48;
  wgl_ts_len:extended=48;        // ditto wing.

  wgl_ms:extended=0;            // wing reach in mm.
  wgl_ts:extended=0;            // ditto wing.

  kckl_dk:extended=0;           // K check lengths in mm (half-length)
  kckl_mk:extended=0;

  ckmidx_ms:extended=0;
  cktsmid_offset:extended=0;

  ts_ck_pull_back:extended=0;    // 237c for equalized chairing..
  ck_shift:extended=0;

  min_diff:extended=0;    // 0.94.a mouse diffs limits
  max_diff:extended=0;


    sliptipsl:extended;    // approx slip switch tips from hd-toe for timber extension calcs. 0.77.b  10-11-02.
    sliprad:extended;      // approx slip radius (mm).

    datumx_on_pad,datumy_on_pad:extended;

    sw_front_inches:extended=65;   // f.s. inches. (arbitrary);

    shovex:extended=0;
    shovek:extended=0;
    shoveo:extended=0;
    shovel:extended=0;
    shovew:extended=0;
    shovec:extended=0;

    view_delay_count:integer=0;     // 0.91.c
    pad_view_now:Tpad_view_data;    // 0.91.c

    _i:integer;        // more 32-bit integers (these 2 were 16-bit in GFA)

    done_calcs:boolean=False;  // 0.93.a


    rmin_mm:extended=0;  // 0.98.a moved to global for dummy vehicle calcs.


    old_mouse_move_X:integer=0;   // 205e
    old_mouse_move_Y:integer=0;   // 205e


    //-----------------------------------------------------------------------
    //      arrays...

    seg_termx:array[0..4, 0..11] of extended;        // only aq=2, 3, 10, 11 actually used.

    //_________________________________________________________________________________________

    form_overwrite_mode:boolean=False;  // 214a

    making_a_diamond:boolean=False;  // 215a

    chair_count:integer=0; // 233d

      // for 3D DXF ...

    ms_checkx1:extended=0;
    ms_checkx2:extended=0;
    ms_checkx3:extended=0;
    ms_checkx4:extended=0;

    ts_checkx1:extended=0;
    ts_checkx2:extended=0;
    ts_checkx3:extended=0;
    ts_checkx4:extended=0;

    ms_wingx1:extended=0;
    ms_wingx2:extended=0;

    ts_wingx1:extended=0;
    ts_wingx2:extended=0;

    mouse_is_over_chair_label_mark:integer=-1;     // MW 05-08-2024  555a


    function f28000(aq:integer; xs,ys:extended):integer;forward;
    function f29000(aq:integer; pc:Tpex):integer;forward;       //  Put xc,yc (in pc) in rail-data array.

    function intolist(aq,nl,xy,d:integer):integer;forward;

    function new_calc_draw(on_canvas:TCanvas; calcs_code, mode:integer):boolean;forward;   // calc and draw all rail lines and marks.

    function turnoutst_main(aq:integer;xb,xe,xs:extended):extended;forward;
    function turnoutst_wing(aq:integer; {fl,}xb,xe,xs:extended):extended;forward;
    function turnoutst_check(aq:integer; {fl,}xb,xe,xs:extended):extended;forward;

    function flarerad(aq:integer; {fl,k5,}xs:extended{; flinout:integer}):extended;forward;   // calc instantaneous radius in curved flares.

    function cuflare(fl,flk,flb,fle,xs:extended; fld:integer):extended;forward;

    function radcurve(aq:integer; {fl,k5,}xs:extended{; flway:integer}):extended;forward;  // 0.93.a ex 081

    function returncurve(xs,radmod:extended):extended;forward;
    function calc_curving:boolean;forward;

    function convert_point(p:Tpex):Tpoint;forward;     // input in mm f.p. , convert to 1/100ths mm. integer.

    function do_auto_trans_start_adjust(old_rad1_orgx, old_rad1_orgy, old_rad2_orgx, old_rad2_orgy:extended):integer;forward;
    function do_auto_trans_length_adjust(apart_len_wanted:extended):integer;forward;

    function pad_marks_current(on_canvas:TCanvas; ink:boolean):boolean;forward;   // draw all the marks (control template). (not rail ends)

    function oneline(aq:integer):boolean;forward; //  Calc a rail-line.

    procedure doradcurve(aq:integer; {fl,}xb,xe:extended);forward;
    procedure dostr(aq:integer;xb,xe,ys:extended);forward;
    procedure dostr_joggle(aq:integer;xb,xe,ys:extended);forward;

    procedure turnroad_cl;forward;                           // turnout road track centre-line.

    procedure k_checkrail_ds(aq:integer);forward;    // K-crossing check rail, DS.

    procedure stflare(aq:integer; fl,flk,xb,xe,yfl:extended; fldir:integer; flsgn:extended; machined_end:boolean);forward;
    procedure stckrail(aq:integer);forward;
    procedure cuckrail(aq:integer);forward;

    function strails(aq:integer):boolean;forward;  // 093a ex 081  all straight rails.

    procedure knuckle(aq:integer);forward;     // knuckle radius  214a

    procedure retrails(aq:integer);forward;
    procedure cuwing(aq:integer);forward;
    procedure curails(aq:integer);forward;
    procedure stxrail(aq:integer);forward;
    procedure stwing(aq:integer);forward;
    procedure stcurail(aq:integer);forward;
    procedure stvee(aq:integer);forward;
    procedure cuvee(aq:integer);forward;
    procedure docrossing(aq:integer;xb,xe:extended; flway:integer);forward;
    procedure doreturn(aq:integer; xb,xe,radmod:extended);forward;

    procedure init_rotate(x,y:extended; rad_centre:boolean);forward;      // set up transform constants for rotates.

    function show_a_line(on_canvas:TCanvas; aq, pen_width:integer; erasing:boolean):boolean;forward;  //  draw a rail-line on the screen.

    procedure trail_xing(X:integer);forward;
    procedure trail_curving(Y:integer);forward;
    procedure trail_swell(Y:integer);forward;

    procedure init_turnout(gauge_index:integer);forward;
    procedure do_railends;forward;                                         // calc rail end marks.
    procedure draw_rail_endmarks(on_canvas:TCanvas; ink:boolean);forward;  // draw the rail ends.
    procedure guidemarks;forward;

    procedure add_check_labels;forward;          // 0.94.a

    procedure calctimbers;forward;
    procedure toetimber(joint:boolean);forward;
    procedure plain_sleepers(xtb:extended; dir:integer;full_length,retcurve:boolean);forward;
    procedure switchtimbers;forward;
    procedure dotimber(retcurve:boolean; joint_timber,key_towards:integer);forward;
    procedure drawtimbol(retcurve:boolean; joint_timber,key_towards:integer);forward;

    procedure tbnumber(retcurve:boolean);forward;
    procedure endsleeper(x:extended;full_length,retcurve:boolean; joint_sleeper,key_towards,dir:integer);forward;

    procedure drawtimbcl(retcurve:boolean);forward;                                                 // mark timber centre-line
    procedure drawtimber(full_length,retcurve:boolean; joint_timber,key_towards:integer);forward;   // mark timber outline.

    procedure timberend(size:integer);forward;

    function xdims:boolean;forward;    // 0.93.a as 081   //  calculate rail x dimensions from origin.

    procedure update_check_diff_panels;forward;  // 0.94.a

    procedure trail_check_len_mm(X:integer);forward;    // 0.94.a  check rail diffs
    procedure trail_check_flare_mm(X:integer);forward;  // 0.94.a  check rail diffs
    procedure trail_check_gap_mm(Y:integer);forward;    // 0.94.a  check rail diffs

    function pad_X(x:extended):integer;forward;   // return pad X pixels at this x mm.
    function pad_Y(y:extended):integer;forward;   // return pad Y pixels at this y mm.

    function draw_xing_label(k,xlabel,yms,yts,ymid,xnote,ynote:extended):Tpex;forward;  // 211b mark the crossing labels

    function curve_point(p:Tpex):Tpex;forward;    // here 227a for symbols

    procedure calc_fill_symbols;forward;    //  227a calc the droppers and gaps

    function shape_clicked(X,Y:integer):boolean;forward;  // 229a is it on a target clip shape?

    procedure do_draw_template_direct(x1,y1,x2,y2:extended);forward;   // 233b  create new control template

//______________________________________________________________________________

procedure do_nothing;

begin
end;
//_____________________________________________________________________________________

procedure debug(str:string; abc:extended);

begin
  show_modal_message(str+' = '+FloatToStr(abc));
end;
//______________________________________________________________________________

procedure Tmath_form.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);

begin
  if Key=VK_PAUSE then Application.Minimize;    //  hide TEMPLOT on PAUSE key.

  if Key=VK_INSERT      // 214a
     then begin
            form_overwrite_mode:= NOT form_overwrite_mode;
            if form_overwrite_mode=True
               then overwrite_label.Caption:='OVR'
               else overwrite_label.Caption:='INS';

            Key:=0;
          end;
end;
//___________________________________________________________________________________

procedure Tmath_form.FormActivate(Sender: TObject);

begin
  math_editbox.SetFocus;
end;
//__________________________________________________________________________________________

function count_substrings(sub_str,text_str:string):integer;      // 215a

begin
  if (Length(sub_str)=0) or (Length(text_str)=0) or (Pos(sub_str,text_str)=0)
     then RESULT:=0
     else RESULT:=( Length(text_str)-Length(StringReplace(text_str,sub_str,'',[rfReplaceAll])) ) div Length(sub_str);
end;
//______________________________________________________________________________

procedure redraw(on_idle:boolean);           // do a screen redraw.

begin
  if on_idle=True
     then begin
            calcs_done_and_valid:=False;      // calcs have not been done.
            data_changed:=True;               // for on_idle re-draw.
          end
     else begin
            gocalc(2,0);

            if (do_rollback=True) and (pad_form.Active=True)
               then update_rollback_register;                 // maintain the roll-back register.
            do_rollback:=True;                                // default setting for next call.
          end;
end;
//_______________________________________________________________________________________

procedure show_and_redraw(on_idle, allow_rollback_to_this:boolean);  // F12 reset.

begin
  cancel_adjusts(False);
  normal_adjust_menu_entry_click;
  hide_current_flag:=False;
  save_hide:=False;

  with pad_form do begin
    show_control_template_menu_entry.Checked:=True;   // 209c   radio item
    show_control_template_popup_entry.Checked:=True;  // 209c   radio item
  end;//with

  do_rollback:=allow_rollback_to_this;
  redraw(on_idle);
end;
//______________________________________________________________________________________

procedure redraw_pad(on_idle, allow_rollback_to_this:boolean);

begin
  do_rollback:=allow_rollback_to_this;
  redraw(on_idle);
end;
//______________________________________________________________________________________

procedure reset_defaults;            // establish any resettable defaults.
                                     // come here before every re-calc (from turnout_dims).
begin

  if out_factor=0 then out_factor:=1;                        // default full size print.

  if ABS(aspect_distortion_factor)<minfp then aspect_distortion_factor:=1; // div zero so no distortion.
  if ABS(x_distortion_factor)<minfp then x_distortion_factor:=1;
  if ABS(y_distortion_factor)<minfp then y_distortion_factor:=1;

  list_factor_x:=x_distortion_factor*aspect_distortion_factor;      // for data lists. need to do calc once only per redraw.
  list_factor_y:=y_distortion_factor/aspect_distortion_factor;

  if incx=def_req then incx:=SQRT(9*scale);      // default step size is SQRT of 9ft scale,
                                                 // = 6 mm steps for 4mm scale;
                                                 // = 8 mm approx steps for 7mm scale.
  turnoutx_max:=xy_pts_c*incx;                   // limit overall length.

  if turnoutx>turnoutx_max then turnoutx:=turnoutx_max;
  if xorg>turnoutx then xorg:=turnoutx;

  if g=def_req then g:=56.5*inscale;             // default 4' 8.5" gauge.

                  // set up all the proto-info gauge defaults...

  with cpi do begin

    if gauge_pi=def_req then gauge_pi:=56.5*inscale;     // default 4' 8.5" gauge.

    if fw_pi=def_req then fw_pi:=1.75*inscale;           // default 1.75" dead-scale flangeway.
    if fwe_pi=def_req then fwe_pi:=fw_pi+1.75*inscale;   // default 1.75" flare-out.

               // new check and wing dimensioning : v:0.71.a 25-5-01...

    if wing_ms_reach1_pi=def_req then wing_ms_reach1_pi:=48;   // size 1 wing rails 48" reach default from centre of "A" timber.
    if wing_ms_reach2_pi=def_req then wing_ms_reach2_pi:=78;   // size 2 wing rails 78" reach default from centre of "A" timber.

    if ck_ms_working1_pi=def_req then ck_ms_working1_pi:=78;  // revert 228b   full-size 108 inches - size 1 MS check rail working length (back from "A").  226a was 78"
    if ck_ms_working2_pi=def_req then ck_ms_working2_pi:=108; // full-size 108 inches - size 2 MS check rail working length (back from "A").
    if ck_ms_working3_pi=def_req then ck_ms_working3_pi:=138; // full-size 138 inches - size 3 MS check rail working length (back from "A").

    if ck_ms_ext1_pi=def_req then ck_ms_ext1_pi:=48;   // full-size 48 inches - size 1 MS check rail extension length (forward from "A").
    if ck_ms_ext2_pi=def_req then ck_ms_ext2_pi:=78;   // full-size 78 inches - size 2 MS check rail extension length (forward from "A").

    if xing_fl_pi=def_req then xing_fl_pi:=36*inscale;     // default 3'0" flare on wing/check rails (not h-d).

    if railtop_pi=def_req then railtop_pi:=2.75*inscale;          // default rail top width, scale 2.75".
    if railbottom_pi=def_req then railbottom_pi:=5.5*inscale;     // default flatbottom base, scale 5.5" (FB-109, BS-110A, BS-113A). 11-5-01.

    if rail_height_pi=def_req
       then begin
              if rail_section=2 then rail_height_pi:=6.25     // default rail height, scale 6.25" (flatbottom). 16-5-01.
                                else rail_height_pi:=5+23/32; // default rail height, scale 5.23/32" (BS-95R bullhead). 16-5-01.
            end;

     if rail_seat_height_pi=def_req   // 235a
       then begin
              if rail_section=2 then rail_seat_height_pi:=5/8     // default rail seat FB baseplate.          30-08-2022
                                else rail_seat_height_pi:=1.75;   // default rail seat (chair thickness) BH.  30-08-2022
            end;

    if rail_inclination_pi=def_req then rail_inclination_pi:=0.0499584;    // radians (1:20).

    if trtscent_pi=def_req then trtscent_pi:=134*inscale;   // 11' 2" track centres turnout-side
    if trmscent_pi=def_req then trmscent_pi:=134*inscale;   // ditto main-side

    if retcent_pi=def_req then retcent_pi:=trtscent_pi;     // return curve track centres

    if tbwide_pi=def_req then tbwide_pi:=12;             // 12" wide turnout timbers.
    if slwide_pi=def_req then slwide_pi:=10;             // 10" wide plain sleepers.

    if jt_slwide_pi=def_req_single then jt_slwide_pi:=10;       // 10" wide joint sleepers.   212a

    if tb_end_offset_pi=def_req then tb_end_offset_pi:=0;       // 232a

    if xtimbsp_pi=def_req then xtimbsp_pi:=30;       // !!! disused in 0.75.a 14-10-01. 30" timber spacing at crossing.
    if ftimbspmax_pi=def_req then ftimbspmax_pi:=30;     // 30" ditto for closure space.

    if tb_pi=def_req
       then begin                                             // TT or 00 short sleepers...
              if gauge_i=t_TT3_i then tb_pi:=24;   // in mm.
              if gauge_i=t_TTI_i then tb_pi:=24;
              if gauge_i=t_TTF_i then tb_pi:=24;

              if gauge_i=t_TMS_i then tb_pi:=25;
              if gauge_i=t_TMF_i then tb_pi:=25;

              if gauge_i=t_00XF_i    then tb_pi:=32;   // 239a
              if gauge_i=t_00SF_i    then tb_pi:=32;
              if gauge_i=t_00MF_i    then tb_pi:=32;
              if gauge_i=t_003D_i    then tb_pi:=32;   // 555a
              if gauge_i=t_00IF_i    then tb_pi:=32;       // 234a
              if gauge_i=t_00BF_i    then tb_pi:=32;
              if gauge_i=t_00H0_i    then tb_pi:=32;
              if gauge_i=t_00DGF_i   then tb_pi:=32;
              if gauge_i=t_00DGI_i   then tb_pi:=32;
              if gauge_i=t_00BRMSB_i then tb_pi:=32;

              if gauge_i=t_s2_irish_i then begin eight_foot_six:=False; nine_foot:=True; end;     // 227a
              if gauge_i=t_em_irish_i then begin eight_foot_six:=False; nine_foot:=True; end;     // 227a
              if gauge_i=t_s4_irish_i then begin eight_foot_six:=False; nine_foot:=True; end;     // 227a
              if gauge_i=t_s_irish_i  then begin eight_foot_six:=False; nine_foot:=True; end;     // 227a
              if gauge_i=t_s7_irish_i then begin eight_foot_six:=False; nine_foot:=True; end;     // 227a

              if tb_pi<>def_req   // TT or 00..
                 then begin
                        eight_foot_six:=False;
                        nine_foot:=False;
                        pad_form.sleepers_otherlength_menu_entry.Checked:=True;   // radio item.
                      end
                 else begin
                        if eight_foot_six=True then tb_pi:=102*inscale;  // 8'6" timbers.
                        if nine_foot=True then tb_pi:=108*inscale;       // 9' timbers.
                        if tb_pi=def_req then tb_pi:=old_tb_pi*inscale;  // non-standard - adjust to the scale change.
                      end;
            end;

    if min_radius_pi=def_req then min_radius_pi:=152*scale;  //  152 ft. minimum radius warning.
                                                       //  = 608 mm (24" approx) in 4mm scale.
                                                       //  = 1064 mm (42" approx) in 7 mm scale.

                                // mods 11-5-01 v:0.71.a...

    if random_end_pi=def_req then random_end_pi:=0;         //  amount of timber-end randomising.
    if random_angle_pi=def_req then random_angle_pi:=0;     //  amount of timber_angle randomising.

  end;//with

  get_cpi;                                       // and use these values.

  if randend=def_req then randend:=0;            // no end randomizing.            *
  if randangle=def_req then randangle:=0;        // equalizing angle not randomised *

  if fixed_sl=def_req then fixed_sl:=fw*2*k3n;

  if ring_dia=def_req then ring_dia:=134*inscale-g-railtop*2;   // spacing ring dia. (6ft way)

  if peg_arm_length=def_req then peg_arm_length:=9*inscale;     // 227a default 9"    was 3ft.

  rings[0,2]:=ring_dia;
  rings[0,3]:=ring_dia+railtop*2;                               // outer diameter.

end;
//______________________________________________________________________________

procedure reset_trans;     // reset transition and curving defaults.

begin
  nomrad:=660*scale;    // default curving radius. (660ft / 10 chains)        *

  nomrad1:=max_rad;     // first transition radius ("straight")
  nomrad2:=nomrad;      // second transition radius (as current)

  os:=0;                // length of first radius (straight) (nil)
  tst:=66*scale;        // transition length (66ft - 1 chain)
end;
//______________________________________________________________________________

procedure clear_shovedata;     // clear any current timber shoves.

begin
  SetLength(current_shoved_timbers,0);

  with shove_timber_form do begin      // 244a ...

    tn_snibs_checkbox.Checked:=True;
    tf_snibs_checkbox.Checked:=True;
    mn_snibs_checkbox.Checked:=True;
    mf_snibs_checkbox.Checked:=True;

    ts_flange_checkbox.Checked:=True;
    ms_flange_checkbox.Checked:=True;
    near_flange_checkbox.Checked:=True;
    far_flange_checkbox.Checked:=True;

    snib_link_near_checkbox.Checked:=False;
    snib_link_far_checkbox.Checked:=False;

  end;//with

  heave_form_reset_all(True);     // 244a  True=no_timber_selected

end;
//______________________________________________________________________________

procedure clear_symboldata;     // clear any current symbols.   227a

begin
  SetLength(current_symbols,0);
end;
//______________________________________________________________________________

procedure clear_check_diffs;  // 0.94.a  clear any current check rail diffs

begin

  with ccd do begin         // check-rail diffs (mouse modifiers)

    end_diff_mw.len_diff:=0;
    end_diff_mw.flr_diff:=0;
    end_diff_mw.gap_diff:=0;
    end_diff_mw.type_diff:=0; // byte

    end_diff_me.len_diff:=0;
    end_diff_me.flr_diff:=0;
    end_diff_me.gap_diff:=0;
    end_diff_me.type_diff:=0; // byte

    end_diff_mr.len_diff:=0;
    end_diff_mr.flr_diff:=0;
    end_diff_mr.gap_diff:=0;
    end_diff_mr.type_diff:=0; // byte

    end_diff_tw.len_diff:=0;
    end_diff_tw.flr_diff:=0;
    end_diff_tw.gap_diff:=0;
    end_diff_tw.type_diff:=0; // byte

    end_diff_te.len_diff:=0;
    end_diff_te.flr_diff:=0;
    end_diff_te.gap_diff:=0;
    end_diff_te.type_diff:=0; // byte

    end_diff_tr.len_diff:=0;
    end_diff_tr.flr_diff:=0;
    end_diff_tr.gap_diff:=0;
    end_diff_tr.type_diff:=0; // byte

    end_diff_mk.len_diff:=0;
    end_diff_mk.flr_diff:=0;
    end_diff_mk.gap_diff:=0;
    end_diff_mk.type_diff:=0; // byte

    end_diff_dk.len_diff:=0;
    end_diff_dk.flr_diff:=0;
    end_diff_dk.gap_diff:=0;
    end_diff_dk.type_diff:=0; // byte

  end;//with
end;
//______________________________________________________________________________

function get_checkrail_diff(code:integer):Tcheck_end_diff;    // 0.94.a

begin
  with null_diff do begin   // return for invalid code

    len_diff:=0;   // length differ  inches f-s
    flr_diff:=0;   // flare length   inches f-s
    gap_diff:=0;   // end gap        model mm

    type_diff:=0   //byte   // 0=no diff   1=change to bent flare    2=change to machined flare
  end;//with

  case code of

      501: RESULT:=ccd.end_diff_mw;
      502: RESULT:=ccd.end_diff_me;
      503: RESULT:=ccd.end_diff_mr;
      504: RESULT:=ccd.end_diff_tw;
      505: RESULT:=ccd.end_diff_te;
      506: RESULT:=ccd.end_diff_tr;
      507: RESULT:=ccd.end_diff_mk;
      508: RESULT:=ccd.end_diff_dk;

     else  RESULT:=null_diff;  // ???

  end;//case
end;
//______________________________________________________________________________

procedure set_checkrail_diff(code:integer; this_diff:Tcheck_end_diff);

begin

  case code of

      501: ccd.end_diff_mw:=this_diff;
      502: ccd.end_diff_me:=this_diff;
      503: ccd.end_diff_mr:=this_diff;
      504: ccd.end_diff_tw:=this_diff;
      505: ccd.end_diff_te:=this_diff;
      506: ccd.end_diff_tr:=this_diff;
      507: ccd.end_diff_mk:=this_diff;
      508: ccd.end_diff_dk:=this_diff;

  end;//case
end;
//______________________________________________________________________________

function set_3D_chair_jaw_defaults(customized:boolean; length:integer):Tjaw_options;   // 239a

 // 239a  init 3D chair jaw defaults...   Tjaw_options = array of array[0..5] of boolean;     6 options each jaw type

var
  i:integer;

begin
  SetLength(RESULT,length);

  for i:=0 to HIGH(RESULT) do begin         // chair jaws data ...

    RESULT[i][0]:=True;   //  export inner gauging jaw
    RESULT[i][1]:=False;  //  don't export outer jaw (slots only)

    RESULT[i][2]:=True;   //  outer jaw is loose
    RESULT[i][3]:=False;  //  export chair base with slots
    RESULT[i][4]:=True;   //  export rail seat

    RESULT[i][5]:=False;  //  don't export key (slots only)

  end;//next

     // over-rides..

  if customized=False
     then begin

            RESULT[3][0]:=False;   //  no inner gauging jaw on P
            RESULT[5][0]:=False;   //  no inner gauging jaw on 1P..2P

            RESULT[5][1]:=True;    //  outside jaw on switch rail is solid 1P..2P
          end;
end;
//______________________________________________________________________________

procedure templot_init;         // this routine runs only once.

var
 i,n:integer;
 printers_count:integer;
 temp:extended;

begin

  if initdone_flag=True then EXIT;

  for n:=0 to aq_max_c do begin

     xy_p[n,0]:=nil;     // (x) pointer to integer arrays containing x rail data in 1/100 of a mm.
     xy_p[n,1]:=nil;     // (y) ditto.

     nlnow_array[n]:=0;     //  current index into each aq array.
     nlmax_array[n]:=0;     //  max nlnow so far used for each aq.
     nldim_array[n]:=0;     //  array length (max index) for each aq.

  end;//for

  paper_way:=1;             // use portrait paper orientation.

  try
    Printer.Title:='Templot';
    printers_count:=Printer.Printers.Count;        // first read of Printers creates the string list.

    if printers_count<1                            // do we have any printers ?
       then begin
              no_printer_available:=True;
              show_margins:=0;
              pad_form.page_outlines_printer_menu_entry.Enabled:=False;
              pad_form.page_outlines_off_menu_entry.Checked:=True;

              alert(2,'    no  printer',
                      'Templot is unable to locate a printer driver on this system. On some systems this may prevent Templot starting.'
                     +'||It is not necessary for a printer to be physically present, all that is needed is a printer driver program from the Windows CD-ROM.'
                     +'||To install a printer driver, from the Windows taskbar click START > SETTINGS > PRINTERS > ADD PRINTER.',
                      '','','','','','O K',0);
            end

       else begin
              no_printer_available:=False;

              with Printer do begin                   // create and init info for each printer...
                for n:=0 to printers_count-1 do begin
                  if printer_list.AddObject(Printers.Strings[n],Tprint_cal.Create)<>n then run_error(181);  // we come here only once.

                    with Tprint_cal(printer_list.Objects[n]).cal_data do begin
                      printer_impact:=-1;                                      // type not yet known.
                      printer_calibrated:=False;
                      printer_head_factor:=1.0;
                      printer_roller_factor:=1.0;
                    end;//with

                end;//for
              end;//with

              if page_info(True,True,False,0)=True     //  read the printer details from API.
                 then Printer.Orientation:=poPortrait
                 else begin                            // no printer or no driver.
                        no_printer_available:=True;
                        show_margins:=0;
                        pad_form.page_outlines_printer_menu_entry.Enabled:=False;
                        pad_form.page_outlines_off_menu_entry.Checked:=True;          // radio item
                      end;
            end;
  except
    no_printer_available:=True;
    show_margins:=0;
    pad_form.page_outlines_printer_menu_entry.Enabled:=False;
    pad_form.page_outlines_off_menu_entry.Checked:=True;          // radio item
  end;//try

      // reset startup to sketchboard defaults for page outlines (0.93.a)...

  page_width:=18000;  // 180mm 0.93.a in 1/100 mm. between trim margins (actual printer),
  page_length:=26000; // 260mm 0.93.a these sizes are used for the page outlines on the pad view.

  time_now_modified(Random($7FFFFFFF)); // init timestamp code for keeps. (this sets last_code_generated).

  init_gauge_list;                      // go fill the gauge/scale list data.

  data_changed:=True;                   // to get the endless loop started o.k.
  trans_terms:=8;                       // default 8 terms in transition expansion.
  trans_auto:=True;                     // but use the auto terms option instead.


    // init colours and line styles in the DXF combo boxes ...

  dxf_form.combo_colour_defaults_button.Click;

  with dxf_form do begin

    rails_style_combo.ItemIndex:=0;       // CONTINUOUS solid lines
    adjrails_style_combo.ItemIndex:=0;
    tkclines_style_combo.ItemIndex:=3;    // centre-line style.
    timbout_style_combo.ItemIndex:=0;
    timbout_kerf_style_combo.ItemIndex:=0;
    sleeperend_style_combo.ItemIndex:=0;  // reduced ends solid.
    timbcent_style_combo.ItemIndex:=3;    // centre-line style.
    gmarks_style_combo.ItemIndex:=0;
    radmarks_style_combo.ItemIndex:=0;
    radcentres_style_combo.ItemIndex:=0;
    joints_style_combo.ItemIndex:=0;
    solid_bgnd_style_combo.ItemIndex:=0;
    dot_bgnd_style_combo.ItemIndex:=2;    // short dashed style.
    chairs_style_combo.ItemIndex:=0;
    chair_bolts_style_combo.ItemIndex:=0;
    sockets_style_combo.ItemIndex:=0;
    sockets_kerf_style_combo.ItemIndex:=0;
    sprues_style_combo.ItemIndex:=0;
    flanges_style_combo.ItemIndex:=0;
    sole_plates_style_combo.ItemIndex:=0;
    webs_style_combo.ItemIndex:=0;
    splints_style_combo.ItemIndex:=0;
    clips_style_combo.ItemIndex:=0;

  end;//with DXF

  with mint_form do begin
    turnout_combo.ItemIndex:=6;     // B-6 default.
    radius_combo.ItemIndex:=12;     // 10 chains radius.
  end;//with

  keep_canvas_clear;                   // set up keeps box drawing colours.

  xform:=0;                            // zero transform data.
  yform:=0;
  kform:=0;
  xshift:=0;
  yshift:=0;

    label_modx:=0;    // 211b labels not used for control template, but retained for when stored again
    label_mody:=0;    // 211b ditto

  re_org_x:=0;                         //  no re-origination.
  re_org_y:=0;

  out_factor:=1;                       //  output scaling 100 %

  x_distortion_factor:=1;              //  no distortions.
  y_distortion_factor:=1;
  aspect_distortion_factor:=1;

  x_distortion_factor:=1;
  y_distortion_factor:=1;

  x_coning_distortion_factor:=0;
  y_coning_distortion_factor:=0;

  x_skewing_distortion_factor:=0;
  y_skewing_distortion_factor:=0;

  mirror_x:=100;                      // mirror does conversion to 1/100th mm.
  mirror_y:=100;

  distortions:=0;                //  warning flag bits.

  guide_marks:=True;             // these flags determine whether the marks are calculated,
  rad_ends:=True;                // and added to the list. pad_guides etc., below determine whether they are subsequently drawn.
  timber_marks:=True;
  joint_marks:=True;

  mouse_labels:=True;  // mouse labels on.
  trace_mouse:=True;   // trace mouse actions with redraws.

  generate_nearest:=False;   // 0.76.a 29-4-02.   flag for "generate nearest turnout" menu item.

  rail_infill_i:=2;             // solid coloured rail infill.

  current_name_str:='';    //  208a  control template reference.

  info_form.ref_name_label.Caption:=current_name_str;

  box_project_title_str:='Market Square';  // 229c was 'Midland Central';   // 214b was 'High Road';      // 082d  // was 'Market Street';    // 0.93.a was 'Town Central';     // 0.79.a was 'Tunnel Junction';

  mouse_modify:=-1;
  now_pen:=0;
  prior_pen:=0;
  info_show_i:=1;

  toggle_1:=0;
  toggle_2:=0;

  scale_bar_i:=0;                      // scalebar colours already set frm control room statup.   0.79.a  was =1
  rail_infill_colour:=clRed;           // pad only.

  nodraw:=1000000;     // this value in the list means do not draw this element.

  screengrid_flag:=True;               // screen grid on.
  grid_spacex:=150;                    // 0.93.a default grid size = 150 mm  // was 50mm
  grid_spacey:=150;
  grid_labels_code_i:=6;               // labels are in mm.

  printgrid_i:=1;                               // print grid on.
  print_railedge_colour:=virtual_black_colour;  // HP driver bug!!!

  snap_proximity_limit:=36;                     // F7 snapping limit.  0.79.a  29-05-06

  timbers_equalized:=False;  // 0.93.a startup square-on timbering.

     // 208a mods...

  number_point_screen.x:=0;       // timber numbering position..
  number_point_screen.y:=0;
  tbnumy_screen:=0;               // default ditto when no timber centre-lines.

  number_point_output.x:=0;       // timber numbering position..
  number_point_output.y:=0;
  tbnumy_output:=0;               // default ditto when no timber centre-lines.


    init_switch_data;         // fill switch data in listbox.

    init_plain_track;         // fill plain track data.

    init_turnout(t_T55_i);    // set up for B-6 starting turnout (T-55 gauge).

      // 223a these needed for experimental chairing on trackpad ...

    init_rail_section;  // 223a for 3D DXF

    init_2d_rea;    // 223a init default 2D DXF chair data

    init_3d_rea;    // 223a init default 3D DXF chair data

            // set printer fonts - here because needs scale...

  if running_under_wine=False      // 205a
     then begin
            print_labels_font.Assign(set_font('Times New Roman',8,[fsBold],clLime));
            printer_text_font.Assign(set_font('Arial',11,[],clBlack));                      // 205a was Comic sans
            print_timber_numbers_font.Assign(set_font('Arial',Round(scale+3),[],clBlack));
            shapes_label_font.Assign(set_font('Times New Roman',18,[fsBold],clBlack));      // 0.93.a was Comic Sans 12   // (gets changed to shapes colour default later.)
            print_corner_page_numbers_font.Assign(set_font('Arial',6,[],clBlack));          // 0.93.a
            print_chair_labels_font.Assign(set_font('Arial',8,[],clBlack));                  // SC 22-SEP-2024 556
          end
     else begin
            print_labels_font.Assign(set_font('Liberation Serif',8,[fsBold],clLime));
            printer_text_font.Assign(set_font('Liberation Sans',11,[],clBlack));
            print_timber_numbers_font.Assign(set_font('Liberation Sans',Round(scale+3),[],clBlack));
            shapes_label_font.Assign(set_font('Liberation Serif',18,[fsBold],clBlack)); // (gets changed to shapes colour default later.)
            print_corner_page_numbers_font.Assign(set_font('Liberation Sans',6,[],clBlack));
            print_chair_labels_font.Assign(set_font('Liberation Sans',8,[],clBlack));        // SC 22-SEP-2024 556
          end;

  //______________

     // aq list ...

  aq_str[0]:='straight stock rail, gauge-face';
  aq_str[1]:='straight turnout rail - wing rail, gauge-face';
  aq_str[2]:='curved turnout rail - wing rail, gauge-face';
  aq_str[3]:='curved stock rail, gauge-face';

  aq_str[4]:='vee point rail, gauge-face';
  aq_str[5]:='vee splice rail, gauge-face';
  aq_str[6]:='main-side check rail, gauge-face';
  aq_str[7]:='turnout-side check rail, gauge-face';

  aq_str[8]:='straight stock rail, outer-face';
  aq_str[9]:='straight turnout rail - wing rail, outer-face';
  aq_str[10]:='curved turnout rail - wing rail, outer-face';
  aq_str[11]:='curved stock rail, outer-face';

  aq_str[12]:='vee point rail, outer-face';
  aq_str[13]:='vee splice rail, outer-face';
  aq_str[14]:='main-side check rail, outer-face';
  aq_str[15]:='turnout-side check rail, outer-face';

  aq_str[16]:='adjacent track (turnout side) near rail, gauge-face';
  aq_str[17]:='adjacent track (turnout side) near rail, outer-face';
  aq_str[18]:='adjacent track (turnout side) far rail, gauge-face';
  aq_str[19]:='adjacent track (turnout side) far rail, outer-face';

  aq_str[20]:='adjacent track (main side) near rail, gauge-face';
  aq_str[21]:='adjacent track (main side) near rail, outer-face';
  aq_str[22]:='adjacent track (main side) far rail, gauge-face';
  aq_str[23]:='adjacent track (main side) far rail, outer-face';

  aq_str[24]:='main road centre-line';
  aq_str[25]:='turnout road centre-line';

  aq_str[26]:='K-crossing check rail, main-side, gauge-face';
  aq_str[27]:='K-crossing check rail, main-side, outer-edge';
  aq_str[28]:='K-crossing check rail, turnout-side, gauge-face';
  aq_str[29]:='K-crossing check rail, turnout-side, outer-edge';

     // not yet implemented...

  aq_str[30]:='straight stock rail, foot, inner edge';
  aq_str[31]:='straight turnout rail - wing rail, foot, inner edge';
  aq_str[32]:='curved turnout rail - wing rail, foot, inner edge';
  aq_str[33]:='curved stock rail, foot, inner edge';

  aq_str[34]:='vee point rail, foot, inner edge';
  aq_str[35]:='vee splice rail, foot, inner edge';
  aq_str[36]:='main-side check rail, foot, inner edge';
  aq_str[37]:='turnout-side check rail, foot, inner edge';

  aq_str[38]:='straight stock rail, foot, outer edge';
  aq_str[39]:='straight turnout rail - wing rail, foot, outer edge';
  aq_str[40]:='curved turnout rail - wing rail, foot, outer edge';
  aq_str[41]:='curved stock rail, foot, outer edge';

  aq_str[42]:='vee point rail, foot, outer edge';
  aq_str[43]:='vee splice rail, foot, outer edge';
  aq_str[44]:='main-side check rail, foot, outer edge';
  aq_str[45]:='turnout-side check rail, foot, outer edge';

  aq_str[46]:='switch flangeway guide-line, main-side';
  aq_str[47]:='switch flangeway guide-line, turnout-side';
  aq_str[48]:='unused aq';

  rings[0,0]:=0;    // init spacing ring position.
  rings[0,1]:=0;


  slew:=trtscent;                           // default slew to adjacent track.
  slew_s:=20*scale;                         // start slew at 20ft scale.
  temp:=500*scale*ABS(slew)*SQR(Pi)/2;      // set default length for 500ft scale slewing rads.
  if temp>minfp then slew_l:=SQRT(temp)
                else slew_l:=600;                // ??? 600 mm otherwise.

  if slew_l<ABS(slew) then slew_l:=ABS(slew);    // ??? arbitrary minimum. (can't go neg).
  if slew_l<1 then slew_l:=1;                    // 1 mm safety minimum (div by zero).

  clear_shovedata;      // init shoves.

  clear_symboldata;     // init current symbols.   227a

  clear_check_diffs;    // 0.94.a init check rail diffs

  current_jaw_options_set:=Copy(set_3D_chair_jaw_defaults(False,Length(current_jaw_options_set)));  // init  239a

  with timbcentre_wait do begin
    pex1.x:=0; pex1.y:=0;
    pex2.x:=0; pex2.y:=0;

    pex_ms.x:=0; pex_ms.y:=0;    // 237b..
    pex_ts.x:=0; pex_ts.y:=0;

    kq:=0;
    shove_code:=0;
    valid:=False;
  end;//with

   //  now ok to start - flag init done.

 initdone_flag:=True;
 quit_code:=3;      // can now only quit via alert query.

end;
//____________________________________________________________________________________________

procedure init_turnout(gauge_index:integer);     // set up starting turnout - ( B-6 turnout reset ).

var
  n:integer;

begin

  plain_track:=False;                               //  False = turnouts,   True = plain track
  set_plain_track(False,True);

  xorg:=320;   // 227a  was 350    028d  was 0      // origin to rail-joint (turnouts) 

  half_diamond:=False;                              // 0.77.a 19-8-02  normal switch calcs.
  hd_switch_timbering:=False;

  hand_i:=1;                                        //  default left-hand turnout.
  gauge_i:=gauge_index;                             //  default index (= 61 for T-55 gauge 26-1-99).
  gauge_str:=gauge[gauge_i].name_str_glist;         //  current gauge name.

  turnoutx:=1100;       // 227a 200ft at 5.5mm/ft  default overall length of turnout mm.
  startx:=0;            // default starting point. (f28000 list entry limit) 1-11-99.
  turnout_i:=1;         // 208d length locked.  was 0 length free.

  turnout_road_i:=0;                                                // normal turnout road exit length.

  gauge_dims(True,False,False);       // set up scale, switch-on defaults (ignore return).

  spiral:=False;       // no transition.
  reset_trans;         // set transition defaults.

  incx:=def_req;       // increment for x mm.

  if set_csi_data(2,2)=False  // set REA size B (group 2, size 2).
     then run_error(82);

  k3n:=6;              // 1:6 crossing             *
  xing_k_i:=7;         // 1:6 crossing list index  *
  xing_type_i:=0;      // straight crossing         *
  wing_tbcount:=3;     // 3 crossing timbers in front of A timber.
  retpar_i:=0;         // no return curve          *
  xing_list_i:=0;      // straight crossing type list index *
  xing_ret_i:=0;       // return curve centres as adjacent track. *

  entry_straight_code:=0;        // sl radio buttons default *
  fixed_sl:=def_req;             // init fixed sl dim.

  pt_i:=3;     // 0.93.a default now 60ft rails / 25 sleepers

  with cpi do begin

    ts_joint_offset_pi:=0;    // 227a    no TS stock rail joint offset
    ms_joint_offset_pi:=0;    // 227a    no MS stock rail joint offset

    tv_joint_offset_pi:=0;    // 227a    no vee splice rail joint offset
    mv_joint_offset_pi:=0;    // 227a    no vee point rail joint offset
  end;//with cpi

  pad_form.snap_exit_to_return_curve_menu_entry.Enabled:=False;   // no return curve.

  screenx:=1250;   // default screen width mm  *

       // 0.91.c init first entry in rollback view list for pad zoom/pan...

  n:=pad_view_list.AddObject('null',Tpad_view.Create);      // string not used. n should be zero.
  Tpad_view(pad_view_list.Objects[n]).pad_view_data.offset_x:=zoom_offsetx;
  Tpad_view(pad_view_list.Objects[n]).pad_view_data.offset_y:=zoom_offsety;
  Tpad_view(pad_view_list.Objects[n]).pad_view_data.width_x:=screenx;

  pad_view_now.offset_x:=zoom_offsetx;
  pad_view_now.offset_y:=zoom_offsety;
  pad_view_now.width_x:=screenx;

               // 0.91.c and init pre-sets...

  pad_view1.offset_x:=zoom_offsetx;
  pad_view1.offset_y:=zoom_offsety;
  pad_view1.width_x:=screenx;

  pad_view2.offset_x:=zoom_offsetx;
  pad_view2.offset_y:=zoom_offsety;
  pad_view2.width_x:=screenx;

  pad_view3.offset_x:=zoom_offsetx;
  pad_view3.offset_y:=zoom_offsety;
  pad_view3.width_x:=screenx;

  pad_view4.offset_x:=zoom_offsetx;
  pad_view4.offset_y:=zoom_offsety;
  pad_view4.width_x:=screenx;


  bunching_jump:=25*scale;            // default 25ft scale for paper bunching.
  bunching_shear:=0;

  xform:=0;                            // zero transform data.
  yform:=0;
  kform:=0;
  xshift:=0;
  yshift:=0;

  pad_form.reset_peg_menu_entry.Checked:=True;       // radio item.
  peg_code:=0;
  pegx:=0;
  pegy:=g/2;

  peg_arm_length:=9*inscale;            // 227a 9" default peg arms   was 3ft

  exittb_i:=2;          // exit is sleepering       *

  calcs_done_and_valid:=False;    // no calcs done yet.


  adjacent_edges:=True;    // 0.93.a   False=adjacent tracks,  True=trackbed edges and platform edges.

           // trackbed ...

  trackbed_ms_width_ins:=90;     // Single   215a  7ft-6in full-size  half-trackbed width  MS
  trackbed_ts_width_ins:=90;     // Single   215a  7ft-6in full-size  half-trackbed width  TS

  draw_ms_trackbed_edge:=False;
  draw_ts_trackbed_edge:=False;

  cess_ms_width_ins:=27;         // Single   215a  2ft-3in full-size cess width MS
  cess_ts_width_ins:=27;

  draw_ms_trackbed_cess_edge:=False;       // 215a
  draw_ts_trackbed_cess_edge:=False;       // 215a


           // platforms ...

  platform_ms_front_edge_ins:=57;        // 4ft-9in default  215a
  platform_ts_front_edge_ins:=57;

  platform_ms_start_width_ins:=144;      // 12ft default
  platform_ms_end_width_ins:=144;        // 12ft default

  platform_ts_start_width_ins:=144;      // 12ft default
  platform_ts_end_width_ins:=144;        // 12ft default

  platform_ms_start_mm:=0;
  platform_ms_length_mm:=def_req;

  platform_ts_start_mm:=0;
  platform_ts_length_mm:=def_req;


  platform_ms_start_skew_mm:=0;      // 207a
  platform_ms_end_skew_mm:=0;        // 207a

  platform_ts_start_skew_mm:=0;      // 207a
  platform_ts_end_skew_mm:=0;        // 207a


  draw_ms_platform:=False;

  draw_ms_platform_rear_edge:=True;
  draw_ms_platform_start_edge:=True;
  draw_ms_platform_end_edge:=True;

  draw_ts_platform:=False;

  draw_ts_platform_rear_edge:=True;
  draw_ts_platform_start_edge:=True;
  draw_ts_platform_end_edge:=True;


  gauge_faces:=True;                               // gauge-faces wanted
  outer_edges:=True;                               // and outer-edges
  midline:=False;                                  // but not rail-centrelines.

  railedges(gauge_faces,outer_edges,centre_lines); // set up these flags.

  reset_defaults;     // do resettable defaults ( on def_req input )

end;
//______________________________________________________________________________

function inc_switch:boolean;   // increase the switch size (within available range).

begin
  RESULT:=False;

  if csi.size_code<csi.group_count
     then begin
            if set_csi_data(csi.group_code,csi.size_code+1)=False // get new switch data from listbox entries.
               then run_error(83);
            RESULT:=True;
          end;
end;
//________________________________________________________________________________________

function calc_switch(sw_info:Tswitch_info; h_diamond,current_calc:boolean):integer;  // calculate switch - return error code,

var
  alpha, beta:extended;
  temp:extended;
  h_inches,lh_inches,sw_front_inches:extended;

begin      // calculate the switch data.

  // sw_info.pattern is type of switch.  0 = curved planing or straight switch; -1 = semi-curved switch;  1 = double-curved switch.

  fbtip:=sw_info.fb_tip_offset;  // FB foot from gauge-face at tip.

  if h_diamond=True        // override - ignore sw_info and calc this switch as a half-diamond.
                           // switch not used - diagonal rails are all on turnout radius.

     then begin
            joggled:=False;
            joggle_long:=0;
            joggle_deep:=0;

            hdk:=ARCTAN(1/hdkn);       // K-crossing angle

            h_inches:=0;               // arbitrary heel location at intesect.
            lh_inches:=0;              // lead to "heel" at intersect.
            swrad:=max_rad+g/2;        // straight switch.
            switch_type:=0;                               // ditto.
            sw_front_inches:=g*TAN(hdk/2)/2/inscale;  // front to tips. = Ctrl-0 = Ctrl-1 = Ctrl-2.

            if (startx=0) and (mouse_modify<>42)     // not if blanking mouse action.
               then startx:=0-18*inscale;            // some negative blanking needed to get both tips, 18" arbitrary.

            current_switch_name:='half-diamond K- '+FormatFloat('0.##',hdkn);

            k1:=hdk;
            k2:=hdk;

            case auto_diamond of
                           0: fixed_diamond:=(hdkn<8.125);  // use switch-diamond if angle is 1:8.125 or flatter.
                           1: fixed_diamond:=True;
                           2: fixed_diamond:=False;
            end;//case

          end
     else begin               // turnout...

            if gaunt=True     // 0.81
               then begin
                      joggled:=False;
                      joggle_long:=0;
                      joggle_deep:=0;

                      h_inches:=gaunt_offset_in;
                      lh_inches:=0;

                      swrad:=max_rad;

                      switch_type:=0;
                      sw_front_inches:=0;
                      current_switch_name:='gaunt';
                    end
               else begin     // normal turnout...

                      joggled:=sw_info.joggled_stock_rail;
                      joggle_long:=sw_info.joggle_length*inscale;
                      if overscale_joggles=True then joggle_deep:=inscale*3/4            // 3/4" joggle
                                                else joggle_deep:=sw_info.joggle_depth*inscale;

                      h_inches:=sw_info.heel_offset_inches;
                      lh_inches:=sw_info.heel_lead_inches;

                      if sw_info.switch_radius_inchormax>max_rad_test      // straight switch
                         then swrad:=max_rad+g/2
                         else swrad:=sw_info.switch_radius_inchormax*inscale;    // switch radius.

                      switch_type:=sw_info.sw_pattern;
                      sw_front_inches:=sw_info.switch_front_inches;
                      current_switch_name:=sw_info.sw_name_str;
                    end;
          end;

    if current_calc=True   // may need to update rail edges...
       then begin
                       // defaults for turnout...

              aqyn[26]:=False;    // h-d MS check rail. (MS check is in diagonal road.)
              aqyn[27]:=False;

              aqyn[28]:=False;    // h-d DS check rail. (DS check is in main road.)
              aqyn[29]:=False;

              if (h_diamond=True) and (fixed_diamond=True)
              and pad_form.check_rails_menu_entry.Checked=True   // generator switch.
                  then begin
                         if k_main_side_check_rail_flag=True  // 0.93.a      was turnout_road_check_rail_flag=True
                            then begin
                                   aqyn[26]:=gauge_faces;    // h-d MS check rail. (MS check is in diagonal road.)
                                   aqyn[27]:=(full_draw or (mouse_modify<=0)) and outer_edges;
                                 end;

                         if k_diagonal_side_check_rail_flag=True  // 0.93.a      was main_road_check_rail_flag=True
                            then begin
                                   aqyn[28]:=gauge_faces;    // h-d DS check rail. (DS check is in main road.)
                                   aqyn[29]:=(full_draw or (mouse_modify<=0)) and outer_edges;
                                 end;
                       end;
            end;

  lh:=lh_inches*inscale;      // lead, toe to heel (incl. planing).

  if gaunt=True      // gaunt template 0.81.a
     then begin
            p:=0; pl:=0;          // no planing.
            h:=h_inches*inscale;  // gaunt offset.

            alpha:=0;  // no swing on the switch curve.
            beta:=0;   // no heel angle.
            k1:=0;     // planing angle.
            k2:=0;     // heel angle.

            k1n:=maxfp;         // RAM unit planing angle.
            k2n:=maxfp;         // RAM unit heel angle.

            k1sinsw:=0;
            k2sinsw:=0;
            k1cossw:=maxfp;
            k2cossw:=maxfp;
            scl:=0;             // switch curve length.
          end
     else begin

            if ((lh<minfp) and (h_diamond=False)) or (swrad<minfp) then begin RESULT:=95; EXIT; end; // can't use zero or negative values.

            if switch_type=0         // curved planing or straight switch...
               then begin
                      p:=0; pl:=0;          // the switch curve starts at the toe - a straight switch is treated as a curved switch of infinite radius.
                      h:=h_inches*inscale;  // heel offset.

                      if (h<minfp) and (h_diamond=False) then begin RESULT:=96; EXIT; end;  // can't use zero or negative values.

                      if h_diamond=False then beta:=ARCTAN(h/lh)  // (radians) average of planing angle and heel angle, k1 and k2.
                                         else beta:=hdk;

                      if swrad>=max_rad_test       // straight switch...
                         then begin

                                alpha:=0;     // no swing on the switch curve.
                                k1:=beta;     // planing angle.
                                k2:=beta;     // heel angle.
                              end
                         else begin                                 // curved planing...
                                temp:=SQRT(SQR(lh)+SQR(h))/2/swrad;
                                if ABS(temp)>1 then begin RESULT:=97; EXIT; end;  // radius too small to fit - will give ARCSIN error.

                                alpha:=ARCSIN(temp);               // swing (radians) on the switch curve.
                                k1:=beta-alpha;                    // planing angle at tip.
                                k2:=beta+alpha;                    // heel angle.
                              end;

                      if (k1<minfp) or (k2<minfp) then begin RESULT:=98; EXIT; end;  // most likely k1 is negative - radius too small or lead too long.

                      k1n:=1/TAN(k1);                    // RAM unit planing angle (at tip if curved).
                      k2n:=1/TAN(k2);                    // RAM unit heel angle.

                      k1sinsw:=swrad*SIN(k1);
                      k2sinsw:=swrad*SIN(k2);
                      k1cossw:=swrad*COS(k1);
                      k2cossw:=swrad*COS(k2);
                      scl:=k2sinsw-k1sinsw;                 // switch curve length.
                    end
               else begin                   // semi-curved switch...

                      k1n:=sw_info.planing_angle;                       // RAM unit planing angle.
                      if k1n<minfp then begin RESULT:=99; EXIT; end;    // can't have zero or negative angle.

                      k1:=ARCTAN(1/k1n);                    // radians.

                      if k1<minfp then begin RESULT:=100; EXIT; end;       // can't have zero or negative angle.

                      p:=sw_info.planing*inscale;  // length of planing (along gauge-face of switch rail).
                      pl:=p*cos(k1);               // ditto length along stock rail to k1 point.

                      if (p<0) or (pl<0) then begin RESULT:=101; EXIT; end;  // can't have negative planing length.

                      temp:=(lh-pl)/swrad+SIN(k1);
                      if ABS(temp)>1 then begin RESULT:=102; EXIT; end;  // radius too small to fit - will give ARCSIN error.
                      k2:=ARCSIN(temp);
                      if k2<minfp then begin RESULT:=103; EXIT; end;     // can't have zero or negative heel angle.

                      k2n:=1/TAN(k2);                     // RAM unit heel angle.

                      k1sinsw:=swrad*SIN(k1);
                      k2sinsw:=swrad*SIN(k2);
                      k1cossw:=swrad*COS(k1);
                      k2cossw:=swrad*COS(k2);
                      scl:=k2sinsw-k1sinsw;               // switch curve length.

                      h:=pl/k1n+k1cossw-k2cossw;          // heel offset.
                    end;
          end;

  toel:=sw_front_inches*inscale;      // rail-joint to toe dimension.
  toex:=toel+xorg;                    // x to toe (main-side).
  toeoxhd:=toex+j*TAN(k1/2);          // x to toe (main-side) outer (for half-diamond rail ends).

  plx:=toex+pl;                       // x to start of switch curve (pl=0 for straight switch or curved planing).
  ph:=p*SIN(k1);                      // gauge-face offset at plx (0 ditto).
  heelx:=toex+lh;                     // x to heel.

  sworgx:=heelx-k2sinsw;              // x to switch curve centre.
  sworgy:=h+k2cossw;                  // y to ditto.

         // now can calculate some x dims...

    if switch_type=0
       then begin         // straight switch or curved planing...

              if gaunt=True
                 then begin
                        setx:=toex;
                        setox:=toex;
                        plx3:=toex;
                        plox11:=toex;
                        fbsetx:=toex;
                        fbsetox:=toex;
                      end
                 else begin
                        temp:=SQR(swrad-g)-SQR(sworgy-g);
                        if temp<0 then begin RESULT:=104; EXIT; end;  // switch rad doesn't cut the straight (gauge-face of curved stock rail).
                        setx:=sworgx+SQRT(temp);

                         // x to "set" in curved stock rail (gauge-face - intercept with switch curve).

                        temp:=SQR(swrad-g-j)-SQR(sworgy-g-j);
                        if temp<0 then begin RESULT:=105; EXIT; end;  // switch rad doesn't cut the straight (outer-edge of curved stock rail).
                        setox:=sworgx+SQRT(temp);                     // x to "set", outer-edge ditto.

                        plx3:=setx;        // no "planing length", switch curve starts at the "set", curved stock rail gauge-face, aq=3.
                        plox11:=setox;     // ditto, outer-edge, aq=11.

                        if rail_section=2       // head+foot (FB section) ... 0.76.a 2-01-02
                           then begin
                                  temp:=SQR(swrad-g+ifb)-SQR(sworgy-g+ifb);
                                  if temp<0 then begin RESULT:=134; EXIT; end;  // switch rad doesn't cut the straight (FB foot of curved stock rail).
                                  fbsetx:=sworgx+SQRT(temp);                    // x to "set" in curved stock rail (FB foot inner - intercept with switch curve).

                                  temp:=SQR(swrad-g-ofb)-SQR(sworgy-g-ofb);
                                  if temp<0 then begin RESULT:=135; EXIT; end;  // switch rad doesn't cut the straight (FB foot of curved stock rail).
                                  fbsetox:=sworgx+SQRT(temp);                   // x to "set", FB foot outer.
                                end
                           else begin             // head only (BH or no rails) - not used...
                                  fbsetx:=setx;
                                  fbsetox:=setox;
                                end;
                      end;
            end

       else begin         // REA semi-curved flexible switch, straight planing...

              setx:=toex-g*TAN(k1/2);             // x to "set" in curved stock rail.
              setox:=setx-j*TAN(k1/2);            // x to ditto, outer-edge.

              plx3:=plx-g*SIN(k1);                // x to start of curve, curved stock rail gauge-face.
              plox11:=plx3-j*SIN(k1);             // x to ditto, outer-edge.

              if rail_section=2    // head+foot (FB section)... 0.76.a 2-01-02  (unfinished)
                 then begin
                        fbsetx:=setx+ifb*SIN(k1/2);   // x to "set" in curved stock rail (FB foot inner).
                        fbsetox:=setx-ofb*SIN(k1/2);  // x to "set" FB foot outer.
                      end
                 else begin             // head only (BH or no rails) - or gaunt...
                        fbsetx:=setx;
                        fbsetox:=setox;
                      end;
            end;


    if gaunt=True
       then begin
              toemidx:=toex;     // toe mark on track centre-line.
              plox:=toex;
              kpl:=0;
              stox:=toex;
              heelox:=toex;
              fbtoex:=toex;
              fbplx:=toex;
              fbplox:=toex;
              fbstox:=toex;
              fbplx3:=toex;
              fbplox11:=toex;
              fbheelox:=toex;
              fbheelx:=toex;
            end
       else begin
              toemidx:=(toex+setx)/2;     // toe mark on track centre-line.

              if (rail_section=2) and (fb_kludge=1)  // 0.94.a kludging inner FB foot as negative outer
                 then begin
                                 // kludge approximations...

                        if switch_type=0    // straight or curved no plx available
                           then begin
                                  temp:=SQR(swrad+railtop)-SQR(sworgy);
                                  if temp<0 then begin RESULT:=106; EXIT; end;   // switch rad doesn't cut the straight (gauge-face of straight stock rail).
                                  plox:=sworgx+SQRT(temp);                       // x to planing intercept (outer-edge of curved turnout rail).

                                  if ABS(temp)<(ABS(swrad+railtop)-minfp) then kpl:=ARCSIN(temp/(swrad+railtop))  // angle at end of planing (for FB).
                                                                          else kpl:=k1;                           // ??
                                end
                           else begin
                                  plox:=plx;    // semi-curved
                                  kpl:=k1;
                                end;

                        stox:=plox-g*SIN(kpl);
                      end
                 else begin     // 0.94.a

              temp:=SQR(swrad+j)-SQR(sworgy);
              if temp<0 then begin RESULT:=106; EXIT; end;   // switch rad doesn't cut the straight (gauge-face of straight stock rail).
              plox:=sworgx+SQRT(temp);                       // x to planing intercept (outer-edge of curved turnout rail).

              if ABS(temp)<(ABS(swrad+j)-minfp) then kpl:=ARCSIN(temp/(swrad+j))  // angle at end of planing (for FB).
                                                else kpl:=k1;                     // ??

              temp:=SQR(swrad-g)-SQR(sworgy-g-j);
              if temp<0 then begin RESULT:=107; EXIT; end;   // switch rad doesn't cut the straight (outer edge of straight turnout rail).
              stox:=sworgx+SQRT(temp);                       // x to straight turnout rail, outer-edge, end of planing (intercept with switch curve in stock rail).

                      end; // 0.94.a

              heelox:=heelx+j*SIN(k2);                       // x to heel, outer rail-edge.


              if rail_section=2       // head+foot (FB section)... 0.76.a 2-01-02   (unfinished)
                 then begin
                        fbtoex:=toex-fbtip*SIN(k1);

                        fbplx:=plox-(j+ifb)*SIN(kpl);

                        temp:=SQR(swrad+ofb)-SQR(sworgy-ifb);
                        if temp<0 then begin RESULT:=136; EXIT; end;   // FB inner foot of straight stock rail.
                        fbplox:=sworgx+SQRT(temp);

                        temp:=SQR(swrad-g+ifb)-SQR(sworgy-g-ofb);
                        if temp<0 then begin RESULT:=137; EXIT; end;   // FB inner foot of curved stock rail.
                        fbstox:=sworgx+SQRT(temp);

                        fbplx3:=plx3+ifb*SIN(k1);
                        fbplox11:=plx3-ofb*SIN(k1);

                        fbheelox:=heelx+ofb*SIN(k2);
                        fbheelx:=heelx-ifb*SIN(k2);
                      end
                 else begin            // head only (BH or no rails)...  (unfinished)
                        fbtoex:=toex;
                        fbplx:=plx;
                        fbplox:=plox;
                        fbstox:=stox;
                        fbplx3:=plx3;
                        fbplox11:=plox11;
                        fbheelox:=heelox;
                        fbheelx:=heelx
                      end;
              end;

  RESULT:=0;                  // switch calculated OK.
end;
//______________________________________________________________________________

function rails_sleepers(len:extended; var num_rails, num_sl_over:integer; var percent_over:extended):boolean;

           // return number of full current rail lengths and extra sleepers over in length len.
           // assumes starting from a rail joint.
var
  len_rail,len_over:extended;

begin

  RESULT:=False;       // init defaults...
  num_rails:=0;
  num_sl_over:=0;
  percent_over:=0;

  if (railen[pt_i]<minfp) or (len<minfp) then EXIT;

  len_rail:=railen[pt_i]*inscale;

  if ABS(len_rail)<minfp
     then num_rails:=0
     else num_rails:=do_truncx(len/len_rail);  // 226d bug-fix

  len_over:=len-num_rails*len_rail;
  percent_over:=len_over*100/len_rail;

  while (psleep[pt_i,num_sl_over]>0) and (len_over>(psleep[pt_i,num_sl_over]*inscale-minfp)) do begin    // >(-minfp) instead of  >= to avoid rounding errors.
    len_over:=len_over-psleep[pt_i,num_sl_over]*inscale;
    INC(num_sl_over);
  end;

  RESULT:=True;
end;
//___________________________________________________________________________________________

function calcturnout:boolean;     // calc all the turnout dimensions.

const
  help_str='The "turnout radius" refers to the section between the end of the switch and the crossing.'
          +' Normally, turnouts have a switch radius greater than or equal to the turnout radius to ease the running into the turnout.'
          +'||It is better to use a longer switch to achieve this, if possible.'
          +'||You may have to click USE LONGER SWITCH more than once to find a switch which fits.';
var
  i:integer;
  heel_to_xing:extended;  // 0.93.a
  flend_out:extended;

begin
  RESULT:=False;   //  in case of error exit.

  if exp_chairing=True
     then begin
            if  ( (dxf_form.clip_fit_radio.Checked=True) or (dxf_form.snap_fit_radio.Checked=True) )
            and (dxf_form.force_eq_checkbox.Checked=True)
               then force_eq:=(k3n<6.99)
               else force_eq:=False;
          end
     else force_eq:=False;

  try
        // mod 0.76.a 1-1-02 ...

    if vertical_rails=True
       then begin
              if midline=True then j:=railtop/2   // for rail centre-lines wanted.
                              else j:=railtop;    // or use rail-width for outer edges.

              ofb:=(railtop+railbottom)/2;   // offset from gauge line to outer FB foot (at base of section).
              ifb:=railbottom-ofb;           // ditto to inner FB foot (at base of section).
            end
       else begin        // inclined rails...

              if midline=True then j:=railtop*COS(rail_inclination)/2   // for rail centre-lines wanted.
                              else j:=railtop*COS(rail_inclination);    // or use rail-width for outer edges.

              ofb:=(railtop+railbottom)*COS(rail_inclination)/2 + rail_height*inscale*SIN(rail_inclination);   // offset from gauge line to outer FB foot (at base of section).
              ifb:=railbottom*COS(rail_inclination) - ofb;                                                     // ditto to inner FB foot (at base of section).
            end;

    case fb_kludge of      // 0.94.a kludged foot-lines wanted

       1: j:=0-ifb;     // override j
       2: j:=ofb;

    end;//case

       // 228a short-angle mods...

    if (xing_type_i=-1) and (generic_was_short_swapped=True)  // first swap short-generic back (k3n may have increased)
       then begin
              xing_type_i:=1;                     // swap back to curviform
              generic_was_short_swapped:=False;   // reset
            end;

       // may need to set it again..

    if (k3n<1.5) and (xing_type_i=1)     // short-angle curviform
       then begin
              xing_type_i:=-1;                   // swap to generic
              generic_was_short_swapped:=True;
            end
       else generic_was_short_swapped:=False;

    //------------

      if k3n<minfp then i:=103                 // can't use zero or negative crossing angle.
                   else begin
                          k3:=ARCTAN(1/k3n);                     // crossing angle in radians.
                          i:=calc_switch(csi,half_diamond,True); // calculate all switch data (might be a custom switch, or half-diamond).
                        end;

      if (i<>0) or (swrad<0)             //^^^
         then begin
                abandon_calcs:=True;
                RESULT:=False;
                EXIT;                     // abandon ship.
              end;

      if gaunt=True                 // 221a
         then equiv_rad:=max_rad
         else begin
                if swrad>=max_rad_test then equiv_rad:=SQR(k1n)*0.6*scale  // r = planing angle^2 * constant - empirical value.
                                       else equiv_rad:=swrad;
              end;

                // must calc switch first to get k2...

      tradius_is_straight:=(ABS(k2-k3)<minfp);   // ^^^ straight turnout radius if switch and crossing angles equal.
      if tradius_is_straight=True
         then xing_calc_i:=0              // calc as if a regular crossing.
         else xing_calc_i:=xing_type_i;

      if xing_calc_i<>0                      // ^^^ curved or generic crossing.
         then sl:=0                          // no entry sl.
         else begin                          // regular crossing or ^^^ straight turnout radius.
                case entry_straight_code of
                                   -1,0: sl:=fw*2*k3n; // short or init auto-fit, minimum sl to ensure knuckle is on the straight.
                                      1: sl:=fixed_sl; // he's fixed it.
                end;//case
              end;

      th:=g-sl/k3n;      // offset at end of turnout curve.

      temp:=(COS(k2)-COS(k3));                              // switch and crossing angles equal, turnout radius is straight.
      if (ABS(temp)<minfp) or (tradius_is_straight=True)
         then tradius:=max_rad+g/2                          // this value not actually used.
         else begin
                        // mods 0.93.a ...

                if xing_calc_i=0  // regular
                   then begin

                          heel_to_xing:=(th-h)*(SIN(k3)-SIN(k2))/temp;   // inter-lead length, heel to curve end (curve end at angle=k3).

                          if (gaunt=True) and (heel_to_xing<(6*scale))  // 217b 6ft minimum length gaunt curve         was 10ft
                             then begin
                                    h:=th-(6*scale)*temp/(SIN(k3)-SIN(k2)); // modify offset according
                                    gaunt_offset_in:=h/inscale;
                                  end;

                        end
                   else if (gaunt=True) and (gaunt_offset_in>((g-fw-minfp)/inscale))
                           then begin
                                  gaunt_offset_in:=(g-fw-minfp)/inscale;   // curviform and generic          offset cannot exceed gauge-flangeway   217a
                                  h:=g-fw-minfp;                           // modify offset according
                                end;

                tradius:=(th-h)/temp;     // turnout radius, gauge-face

              end;

      if (half_diamond=False) and (tradius>minfp) and (tradius>(equiv_rad+minfp)) and (entry_straight_code=0) and (xing_calc_i=0) and (tradius_is_straight=False)

            // turnout rad is positive non-straight and exceeds switch rad.
            // this is or is being calced as a regular crossing.
            // auto-fit entry straight is in force and this is not a half-diamond

            // so reduce turnout rad and auto-fit a longer straight (sl).

          then begin
                 tradius:=equiv_rad;               // reduce turnout radius to limit,
                 th:=h+tradius*(COS(k2)-COS(k3));  // and recalc th and sl.
                 sl:=(g-th)*k3n;
                 if sl<(fw*2*k3n)then begin
                                        abandon_calcs:=True;
                                        RESULT:=False;
                                        EXIT;                // abandon ship.
                                      end;
               end;


      if tradius_is_straight=False
         then begin
                csradius:=tradius-g;     // radius in curved stock rail, gauge-face.

                tl:=tradius*(SIN(k3)-SIN(k2));     // inter-lead length, heel to curve end (curve end at angle=k3).

                torgx:=heelx-tradius*SIN(k2);      // x to turnout curve centre.
                torgy:=h+tradius*COS(k2);          // y to ditto.
              end
         else begin
                csradius:=0;     // invalid rad.
                tl:=(th-h)*k3n;  // inter-lead length, heel to curve end (curve end at angle=k3).
                torgx:=0;        // invalid values..
                torgy:=0;
              end;

              // calc check-rail flare stuff...

  flen_mw:=xing_flare_len+ccd.end_diff_mw.flr_diff*inscale;    // MS working end mm
  flend_out:=fw_end-fw+ccd.end_diff_mw.gap_diff;
  if flend_out<0 then flend_out:=0;                 // end gap can't be less than fw

  if (flen_mw<minfp) or (flen_mw<flend_out)         // no negs or division by zero
     then begin flen_mw:=0; k5_mw:=0; end
     else k5_mw:=ARCSIN(flend_out/flen_mw);   // mw flaring angle.
  jbend_mw:=j/COS(k5_mw/2);                   // j width across flare bends.

  flen_me:=xing_flare_len+ccd.end_diff_me.flr_diff*inscale;    // MS extension end
  flend_out:=fw_end-fw+ccd.end_diff_me.gap_diff;
  if flend_out<0 then flend_out:=0;

  if (flen_me<minfp) or (flen_me<flend_out)
     then begin flen_me:=0; k5_me:=0; end
     else k5_me:=ARCSIN(flend_out/flen_me);   // me flaring angle.
  jbend_me:=j/COS(k5_me/2);                   // j width across flare bends.



  flen_mr:=xing_flare_len+ccd.end_diff_mr.flr_diff*inscale;    // MS wing reach end
  flend_out:=fw_end-fw+ccd.end_diff_mr.gap_diff;
  if flend_out<0 then flend_out:=0;

  if (flen_mr<minfp) or (flen_mr<flend_out)
     then begin flen_mr:=0; k5_mr:=0; end
     else k5_mr:=ARCSIN(flend_out/flen_mr);   // mr flaring angle.
  jbend_mr:=j/COS(k5_mr/2);                   // j width across flare bends.



  flen_tw:=xing_flare_len+ccd.end_diff_tw.flr_diff*inscale;    // TS working end
  flend_out:=fw_end-fw+ccd.end_diff_tw.gap_diff;
  if flend_out<0 then flend_out:=0;

  if (flen_tw<minfp) or (flen_tw<flend_out)
     then begin flen_tw:=0; k5_tw:=0; end
     else k5_tw:=ARCSIN(flend_out/flen_tw);   // tw flaring angle.
  jbend_tw:=j/COS(k5_tw/2);                   // j width across flare bends.



  flen_te:=xing_flare_len+ccd.end_diff_te.flr_diff*inscale;    // TS extension end
  flend_out:=fw_end-fw+ccd.end_diff_te.gap_diff;
  if flend_out<0 then flend_out:=0;

  if (flen_te<minfp) or (flen_te<flend_out)
     then begin flen_te:=0; k5_te:=0; end
     else k5_te:=ARCSIN(flend_out/flen_te);   // te flaring angle.
  jbend_te:=j/COS(k5_te/2);                   // j width across flare bends.



  flen_tr:=xing_flare_len+ccd.end_diff_tr.flr_diff*inscale;    // TS wing reach end
  flend_out:=fw_end-fw+ccd.end_diff_tr.gap_diff;
  if flend_out<0 then flend_out:=0;

  if (flen_tr<minfp) or (flen_tr<flend_out)
     then begin flen_tr:=0; k5_tr:=0; end
     else k5_tr:=ARCSIN(flend_out/flen_tr);   // tr flaring angle.
  jbend_tr:=j/COS(k5_tr/2);                   // j width across flare bends.


  if (half_diamond=True) and (fixed_diamond=True)
     then begin
            flen_mk:=(k_flare_len+ccd.end_diff_mk.flr_diff)*inscale;    // MS k check rail end
            flend_out:=fw_end-fw+ccd.end_diff_mk.gap_diff;
            if flend_out<0 then flend_out:=0;

            if (flen_mk<minfp) or (flen_mk<flend_out)
               then begin flen_mk:=0; k5_mk:=0; end
               else k5_mk:=ARCSIN(flend_out/flen_mk);   // mk flaring angle.
            jbend_mk:=j/COS(k5_mk/2);                   // j width across flare bends.

            flen_mk_oe:=flen_mk-j*TAN(k5_mk/2);   // outer-edge flare length



            flen_dk:=(k_flare_len+ccd.end_diff_dk.flr_diff)*inscale;    // DS k check rail end
            flend_out:=fw_end-fw+ccd.end_diff_dk.gap_diff;
            if flend_out<0 then flend_out:=0;

            if (flen_dk<minfp) or (flen_dk<flend_out)
               then begin flen_dk:=0; k5_dk:=0; end
               else k5_dk:=ARCSIN(flend_out/flen_dk);   // dk flaring angle.
            jbend_dk:=j/COS(k5_dk/2);                   // j width across flare bends.

            flen_dk_oe:=flen_dk-j*TAN(k5_dk/2);   // outer-edge flare length
          end;


          //  MS check rails working length ...

  check_ms_wklen:=ck_working3;            // start with size 3 MS working length check rail.

  if k3n<12.01                            // size 2 if 1:12  to 1:6 inclusive.
     then check_ms_wklen:=ck_working2;    // set size 2 MS check rail.

  if (k3n<=5.99) or ((half_diamond=True) and (k3n<=7.99) and (hd_vcheck_rails>1))   // less than 1:6, or shorten half-diamond MS check(=2) or both checks(=3) for slip switch (if less than 1:8).
     then check_ms_wklen:=ck_working1;    // set size 1 ms check rail.

  check_ms_wklen:=check_ms_wklen+ccd.end_diff_mw.len_diff;  // 0.94.a add mouse diff
  if check_ms_wklen<0 then check_ms_wklen:=0;

         //  TS check rails working length ...

  check_ts_wklen:=ck_working3;            // start with size 3 TS working length check rail.

    if k3n<12.01                            // size 2 if 1:12  to 1:6 inclusive.
       then check_ts_wklen:=ck_working2;    // set size 2 TS check rail.

    if (k3n<=5.99) or ((half_diamond=True) and (k3n<=7.99) and ((hd_vcheck_rails=1) or (hd_vcheck_rails=3)))   // less than 1:6, or shorten half-diamond TS check(=2) or both checks(=3) for slip switch (if less than 1:8).
       then check_ts_wklen:=ck_working1;    // set size 1 TS check rail.

  check_ts_wklen:=check_ts_wklen+ccd.end_diff_tw.len_diff;  // 0.94.a add mouse diff
  if check_ts_wklen<0 then check_ts_wklen:=0;

          // wing rail reach length...

  wgl_ms_len:=wing_reach2;           // start with size 2 MS wing rail.
  check_ms_extlen:=ck_ext2;          // and size 2 MS check extension.

  wgl_ts_len:=wing_reach2;           // start with size 2 TS wing rail.
  check_ts_extlen:=ck_ext2;          // and size 2 TS check extension.

  if k3n<10.01           // size 1 up to 1:10 inclusive.
     then begin
            wgl_ms_len:=wing_reach1;        // no, size 1 MS wing rail wanted.
            check_ms_extlen:=ck_ext1;       // and size 1 MS check extension.

            wgl_ts_len:=wing_reach1;        // no, size 1 TS wing rail wanted.
            check_ts_extlen:=ck_ext1;       // and size 1 TS check extension.
          end;

  wgl_ms_len:=wgl_ms_len+ccd.end_diff_mr.len_diff;  // 0.94.a add mouse diff
  if wgl_ms_len<0 then wgl_ms_len:=0;

  check_ms_extlen:=check_ms_extlen+ccd.end_diff_me.len_diff;  // 0.94.a add mouse diff
  if check_ms_extlen<0 then check_ms_extlen:=0;


  wgl_ts_len:=wgl_ts_len+ccd.end_diff_tr.len_diff;  // 0.94.a add mouse diff
  if wgl_ts_len<0 then wgl_ts_len:=0;

  check_ts_extlen:=check_ts_extlen+ccd.end_diff_te.len_diff;  // 0.94.a add mouse diff
  if check_ts_extlen<0 then check_ts_extlen:=0;

  ckl_ms:=(check_ms_wklen+check_ms_extlen)*inscale;         // mm length for main-side check rail (including extension).
  ckl_ts:=(check_ts_wklen+check_ts_extlen)*inscale;         // mm length for turnout-side check rail (including extension).

  wgl_ms:=wgl_ms_len*inscale;     // mm length for wing rails
  wgl_ts:=wgl_ts_len*inscale;

       // generic use of FB-109 dims for vee rail lengths at different crossing angles...

       // generic use of FB-109 dims for vee rail lengths at different crossing angles...

           // mod 0.75.a 10-10-01..

      veelong:=(bn_to_a+mvj_sp)*inscale;  // init  -  4"(BH default) + 12.5"(BH default) to joint. veelong from blunt nose.

      repeat
             // (mod 0.75.a  11-10-01)...
             // these mods mean that for crossings 1:4 to 1:5 inclusive, and 1:7 to 1:7.5 inclusive,
             // default templates are now 1 timber space longer to the mvjp and tvjp positions (CTRL-8 and CTRL-6).
             // and hence also to the CTRL-9 end position.
             // mod done to comply better with prototype info, FB in particular.

        if k3n< 3.875 then begin veelong:=veelong+(vee_spco1-1)*veetimb_sp*inscale; wing_tbcount:=wing_spco1; BREAK;end;   // funnies. (mod 0.75.a  11-10-01)
        if k3n< 6.875 then begin veelong:=veelong+(vee_spco2-1)*veetimb_sp*inscale; wing_tbcount:=wing_spco2; BREAK;end;
        if k3n<10.125 then begin veelong:=veelong+(vee_spco3-1)*veetimb_sp*inscale; wing_tbcount:=wing_spco3; BREAK;end;
        if k3n<15.125 then begin veelong:=veelong+(vee_spco4-1)*veetimb_sp*inscale; wing_tbcount:=wing_spco4; BREAK;end;
        if k3n<20.125 then begin veelong:=veelong+(vee_spco5-1)*veetimb_sp*inscale; wing_tbcount:=wing_spco5; BREAK;end;

           // for longer crossings, assume straight crossing, and ensure 12" separation between point and splice rails at joint...

        wing_tbcount:=wing_spco6;

        veelong:=veelong+(vee_spco6-1)*veetimb_sp*inscale;   // re-init

               // blunt nose width + offset along point rail < 1ft...

        while  ((bn_wide*inscale)+(veelong/k3n))<scale do veelong:=veelong+veetimb_sp*inscale;
        BREAK;
      until 0<>0;

      RESULT:=xdims;         // go calc remaining x dims for all rails. returns True if calcs ok.
                             // and fill the termination list for curved rails.

    except
      RESULT:=False;
    end;//try
end;
//______________________________________________________________________________

function get_current_diffed_dims:string;

var
  num_str:string;

begin
  num_str:=''; //init

  case current_diff_code of

    501: begin                     //MS1 MS working len
           current_diffed_len:=check_ms_wklen*inscale;
           current_diffed_fl_len:=flen_mw;
           current_diffed_end_gap:=fw_end+ccd.end_diff_mw.gap_diff;
           num_str:='MS1';
         end;

    502: begin                     //MS2 MS ext len
           current_diffed_len:=check_ms_extlen*inscale;;
           current_diffed_fl_len:=flen_me;
           current_diffed_end_gap:=fw_end+ccd.end_diff_me.gap_diff;
           num_str:='MS2';
         end;

    503: begin                     //MS3 MS wing reach
           current_diffed_len:=wgl_ms_len*inscale;;
           current_diffed_fl_len:=flen_mr;
           current_diffed_end_gap:=fw_end+ccd.end_diff_mr.gap_diff;
           num_str:='MS3';
         end;

    504: begin                     //TS1 TS working len
           current_diffed_len:=check_ts_wklen*inscale;;
           current_diffed_fl_len:=flen_tw;
           current_diffed_end_gap:=fw_end+ccd.end_diff_tw.gap_diff;
           if half_diamond=True then num_str:='DS1' else num_str:='TS1';
         end;

    505: begin                     //TS2 TS ext len
           current_diffed_len:=check_ts_extlen*inscale;;
           current_diffed_fl_len:=flen_te;
           current_diffed_end_gap:=fw_end+ccd.end_diff_te.gap_diff;
           if half_diamond=True then num_str:='DS2' else num_str:='TS2';
         end;

    506: begin                     //TS3 TS wing reach
           current_diffed_len:=wgl_ts_len*inscale;;
           current_diffed_fl_len:=flen_tr;
           current_diffed_end_gap:=fw_end+ccd.end_diff_tr.gap_diff;
           if half_diamond=True then num_str:='DS3' else num_str:='TS3';
         end;

    507: begin                     //MS4 MS K check len
           current_diffed_len:=kckl_mk;
           current_diffed_fl_len:=flen_mk;
           current_diffed_end_gap:=fw_end+ccd.end_diff_mk.gap_diff;
           if half_diamond=True then num_str:='MS4';
         end;

    508: begin                     //DS4 DS K check len
           current_diffed_len:=kckl_dk;
           current_diffed_fl_len:=flen_dk;
           current_diffed_end_gap:=fw_end+ccd.end_diff_dk.gap_diff;
           if half_diamond=True then num_str:='DS4';
         end;

  end;//case

  RESULT:=num_str;
end;
//______________________________________________________________________________

procedure update_check_diff_panels;  // 0.94.a

var
  num_str:string;
  able:boolean;

  this_diff:Tcheck_end_diff;

  code,code_max:integer;

begin
  if check_diffs_form.Showing=True
     then begin

            num_str:=get_current_diffed_dims;

            with check_diffs_form do begin

              if num_str=''      // none selected, or K-checks remain selected and not now a half-diamond
                 then begin
                        able:=False;
                        readout_label.Visible:=False;
                        length_panel.Font.Color:=clGray;
                        number_panel.Caption:='';

                        length_panel.Caption:=' ( no rail end is selected )';
                        flare_panel.Caption:='';
                        gap_panel.Caption:='';
                      end
                 else begin                                 // update for selected
                        able:=True;
                        readout_label.Visible:=True;
                        length_panel.Font.Color:=clBlue;
                        number_panel.Caption:=num_str;

                        if show_diffs_fs=True
                           then begin
                                  length_panel.Caption:=' length :  '+round_str(current_diffed_len/inscale,2)+' ins';
                                  flare_panel.Caption:=' flare length :  '+round_str(current_diffed_fl_len/inscale,2)+' ins';
                                  gap_panel.Caption:=' end gap :  '+round_str(current_diffed_end_gap/inscale,2)+' ins';
                                end
                           else begin
                                  length_panel.Caption:=' length :  '+round_str(current_diffed_len,2)+' mm';
                                  flare_panel.Caption:=' flare length :  '+round_str(current_diffed_fl_len,2)+' mm';
                                  gap_panel.Caption:=' end gap :  '+round_str(current_diffed_end_gap,2)+' mm';
                                end;
                      end;

              data_button.Enabled:=able;
              lengthen_button.Enabled:=able;
              shorten_button.Enabled:=able;

              mouse_length_button.Enabled:=able;
              mouse_flare_button.Enabled:=able;
              mouse_gap_button.Enabled:=able;

              bent_button.Enabled:=able;
              machined_button.Enabled:=able;
              no_flare_button.Enabled:=able;
              flare_restore_button.Enabled:=able;


                // enable restore buttons only if something to restore ...

              this_diff:=get_checkrail_diff(current_diff_code);

              if  (this_diff.len_diff=0)
              and (this_diff.flr_diff=0)
              and (this_diff.gap_diff=0)
              and (this_diff.type_diff=0)
                  then reset_button.Enabled:=False
                  else reset_button.Enabled:=able;


              if half_diamond=True
                 then code_max:=508
                 else code_max:=506;

              for code:=501 to code_max do begin

                this_diff:=get_checkrail_diff(code);

                if  (this_diff.len_diff=0)
                and (this_diff.flr_diff=0)
                and (this_diff.gap_diff=0)
                and (this_diff.type_diff=0)
                    then CONTINUE
                    else EXIT;
                    
              end;//next diff

            end;//with form
          end;//if showing
end;
//______________________________________________________________________________

function get_flare_type(type_diff:byte):integer;    // for calcs

 // 0=machined or none, 1=bent

begin
  RESULT:=ABS(flare_type-1);  // init as mint

  case type_diff of
        0: RESULT:=ABS(flare_type-1);  // as mint
        1: RESULT:=1;                  // bent
        2: RESULT:=0;                  // machined or none
  end;//case
end;
//______________________________________________________________________________

function xdims:boolean;          //  calculate rail x dimensions from origin.
                                 //  N.B. heelx and all subsequent dims (except turnout_road_endx, main_road_endx) include xorg.
var
  temp,tempx,tempy,tempk:extended;
  i:integer;
  k88,angled_sl:extended;
  bnw:extended;
  tcosk3:extended;

  swing_on_k_check:extended;  // 0.93.a

  tradius_cl:extended;  // 0.93.a

  ka:extended;          // 214a  angle at knuckle
  knuck_max:extended;

  xorg_bl_mod:extended;  // 234d

begin
  RESULT:=False;  // default init.

  try
      tx:=heelx+tl;                           // x to end of turnout curve.
      fpx:=tx+sl;                             // x to fine point.

          // mod 0.75.a  10-10-01...

      bnx:=fpx+(bn_wide/2)*inscale/SIN(k3/2);   // x to blunt nose. (point rail gauge-face) (slightly wider if curved crossing.
      bnxx:=bnx-bn_wide*inscale*SIN(k3/2);      // x to ditto (splice rail gauge-face).

      bnox:=fpx+j/TAN(k3/2);                  // x to blunt nose outer edge (straight crossing - curved recalcs later).

      atx:=bnx+bn_to_a*inscale;    // x to centre "A" timber.
      wingendx_ms:=atx+wgl_ms;     // x to ms wing rail end.

      csrfpx:=fpx-g*SIN(k3);       // x to curved stock rail opposite fp.
      csrfpox:=csrfpx-j*SIN(k3);   // x to ditto outer-edge.

      dpx:=fpx-g/2/TAN(k3/2);      // x to deflection point (intersection of track centre-lines).

      ipx:=(dpx+fpx)/2;            // x to IP Intermediate 208a  arbitrary

      tcpx:=dpx+(fpx-dpx)*COS(k3); // x to point on turnout road centre-line opposite fine point.
      tcpy:=g/2+(fpx-dpx)*SIN(k3); // y ditto.


      if xing_calc_i<1             // straight crossing, regular (0) or generic (-1)...
         then begin
                txpy:=(trtscent+g)/2;         // y to TXP, turnout-side crossover mid-point.
                txpx:=dpx+k3n*trtscent/2;     // x ditto.
                txpk:=k3;                     // angle at TXP.   0.78.e  23-02-03

                   // 227a mods...

                tvjp_veenx:=fpx+(bnx-fpx+veelong)*COS(k3);   // x to nominal vee rail end
                tvjp_stocknx:=tvjp_veenx-g*SIN(k3);          // x to nominal stock rail end

                tvjp_ncx:=tvjp_veenx-g*SIN(k3)/2;   // x to nominal TS end, centre-line, straight crossing.

                tvjp_nk:=k3;      // angle at rail ends

                retx:=tvjp_ncx;                    // return curve starts at nominal TS end
                rety:=g/2+(tvjp_ncx-dpx)/k3n;


                   // 227a adjust for modified joint positions..

                tvjp_veejx:=tvjp_veenx+cpi.tv_joint_offset_pi*inscale;
                tvjp_stockjx:=tvjp_stocknx+cpi.ts_joint_offset_pi*inscale;

                tvjpx:=tvjp_veejx-g*SIN(k3)/2;    // x to TVJP on track centre-line.
                tvjpk:=k3;                        // angle at TVJP.


              end
         else begin                      // curviform crossing (1)

                txpy:=(trtscent+g)/2;    // y to turnout-side crossover mid-point.

                temp:=SQR(tradius-g/2)-SQR(torgy-txpy);  //^^^
                if temp<minfp then begin                 // something wrong.
                                     txpx:=tcpx;         // set to crossing-point instead.
                                     txpy:=tcpy;
                                   end
                              else txpx:=torgx+SQRT(temp)*SGZ(tradius);   //^^^   // x to turnout-side crossover mid-point.
                try
                  txpk:=ARCSIN((txpx-torgx)/(tradius-g/2));  //^^^ // angle at TXP. 0.78.e  23-02-03.
                except
                  txpk:=k3;   // ??? gone wrong somewhere.
                end;//try


                   // 227a mods...

                try
                  tvjp_nk:=(bnx-fpx+veelong)/tradius+k3;

                  if tvjp_nk>k4_limit then tvjp_nk:=k4_limit;            // can't exceed swing limit. (90 degs approx).
                except
                  tvjp_nk:=k4_limit;
                end;//try

                tvjp_ncx:=torgx+(tradius-g/2)*SIN(tvjp_nk);   // x to nominal ends, centre-line

                tvjp_veenx:=tvjp_ncx+g*SIN(tvjp_nk)/2;    // x to nominal TS vee end
                tvjp_stocknx:=tvjp_ncx-g*SIN(tvjp_nk)/2;  // x to nominal TS stock rail end

                retx:=tvjp_ncx;                         // return curve always starts at nominal rail end  (not used for curviform)
                rety:=torgy-(tradius-g/2)*COS(tvjp_nk);


                   // 227a adjust for modified joint positions..

                tvjp_veejx:=tvjp_veenx+cpi.tv_joint_offset_pi*inscale;
                tvjp_stockjx:=tvjp_stocknx+cpi.ts_joint_offset_pi*inscale;

                try
                  tvjpk:=ARCSIN((tvjp_veejx-torgx)/tradius);   // angle at TVJP
                  if tvjpk>k4_limit then tvjpk:=k4_limit;      // can't exceed swing limit. (90 degs approx).
                except
                  tvjpk:=k4_limit;
                end;//try

                tvjpx:=tvjp_veejx-g*SIN(tvjpk)/2;   // x to TVJP on track centre-line.

              end;

      temp:=1-COS(tvjp_nk);
      if temp<minfp then retr:=maxfp                      // crossing so flat return rad is straight ?
                    else retr:=(trtscent+g/2-rety)/temp;  // return curve centre-line radius.

      if retr<minfp                  // can't use return radius zero or negative.
         then begin
                if retpar_i=1
                   then begin
                          alert(0,'   calculation  failure','Sorry, a return curve to parallel track will not fit.'
                                 +'||Increase the track centre-to-centre spacing or use a flatter V-crossing angle.)',
                                  '','','','','','O K  -  cancel  parallel  V-crossing',0);
                          retpar_i:=0;                                                     //  and cancel the return curve.
                          pad_form.snap_exit_to_return_curve_menu_entry.Enabled:=False;
                        end
                   else retr:=g;      //  use bare minimum for calcs - g arbitrary.
              end;

      retrorgx:=retx+retr*SIN(tvjp_nk);    // x to return curve centre. (= end of return curve.)
      retrorgy:=trtscent+g/2-retr;         // y to return curve centre.

      trpx:=retrorgx;                  // x to turnout-side return point.
      trpy:=trtscent+g/2;              // y to ditto.

      mcpx:=fpx;                      // x to ditto on main road.

      if xing_calc_i<>1                      // straight crossing, regular or generic...
         then mxpx:=dpx+trtscent/2/SIN(k3)   // x to main-side crossover mid-point. Mod 0.78.e WAS trmscent MS spacing. 22-02-03.
         else begin                          // curved crossing...  Mod BUG FIX 0.78.e WAS as for straight crossing. 22-02-03.
                try
                  mxpx:=txpx-(trtscent/2)/TAN(txpk)+(trtscent/2)/SIN(txpk);  // 0.78.e  23-02-03
                except
                  mxpx:=fpx;  // something wrong
                end;
              end;

      mrpx:=retrorgx;       // x to main-side return point.


      mvjp_nx:=bnx+veelong;      // 227a   x to nominal rail ends

      mvjpx:=mvjp_nx+cpi.mv_joint_offset_pi*inscale;    // 227a  x to main-side vee rail joint (point rail).


      default_turnoutx:=mvjpx+(psleep[pt_i,0]+psleep[pt_i,1])*inscale;  // default free length to 2nd sleeper centre beyond point rail joint.

      if turnout_i=0         //  new turnout - reset nominal length - length free.
         then begin
                if (xing_calc_i<>1) and (retpar_i=1)       // set up defaults for screenx.
                   then turnoutx:=retrorgx                 // x to end of return curve,
                   else turnoutx:=default_turnoutx;
                if turnoutx>turnoutx_max then turnoutx:=turnoutx_max;
              end;

         // 209a 217a  exit mods...

      if rail_section=2  // FB rails
         then min_main_road_endx:=0-xorg+fpx+ofb/TAN(k3/2)+scale/6      // FB minimum main road is end of outer foot splice + 2" scale
         else min_main_road_endx:=0-xorg+fpx+railtop/TAN(k3/2)+scale/6; // BH minimum main road is end of head splice + 2" scale

      min_turnout_road_endx:=min_main_road_endx-g*SIN(k3)/2;    //  217a (from CTRL-1)   (turnout-road dim is to track centre-line)

      case turnout_road_i of
         -1: turnout_road_endx:=txpx-xorg;      // crossover
          0: turnout_road_endx:=tvjpx-xorg;     // normal
          1: turnout_road_endx:=turnoutx-xorg;  // long

          3: turnout_road_endx:=min_turnout_road_endx;     // minimum 217a

          // no change to turnout_road_endx  if 2: adjustable
      end;//case

      if turnout_road_endx<min_turnout_road_endx then turnout_road_endx:=min_turnout_road_endx;   // dims are from CTRL-1 !!!

      if k3n<1.5                                  // 228a
         then thdpx:=min_turnout_road_endx+xorg     // (xorg zero on half-diamond)
         else thdpx:=bnx+7.5*k3n*inscale*COS(k3);   // 228a arbitrary 60" fixed exit at 1:8 (from blunt nose) on half-diamonds for make diamond-xing at intersection (60" along TS)


      if (turnout_i=0)                            //  template length free.
      and ((xorg+turnout_road_endx)>turnoutx)
          then turnout_road_endx:=turnoutx-xorg;  // 209a mod  // dims to CTRL-1

        // 209a mods end.

        // 217a mods ...

      case main_road_i of
         -1: main_road_endx:=mxpx-xorg;      // crossover
          0: main_road_endx:=turnoutx-xorg;  // normal

          3: main_road_endx:=min_main_road_endx;     // 217a

          // no change to main_road_endx  if 2: adjustable      1: not valid
      end;//case

      if main_road_endx<min_main_road_endx then main_road_endx:=min_main_road_endx;   // dims are from CTRL-1 !!!

      if (turnout_i=0)                            //  template length free.
      and ((xorg+main_road_endx)>turnoutx)
          then main_road_endx:=turnoutx-xorg;     // dims to CTRL-1

        // 217a mods end.

      if xing_calc_i<>1  // straight crossing, regular or generic:
         then begin
                if retpar_i=1                         // return curve ?
                   then begin
                          vendx:=retx+g/2*SIN(k3);    // yes, x to end of straight portion of vee splice rail,
                          csrendx:=retx-g/2*SIN(k3);  // and stock rail opposite.
                        end
                   else begin
                          case turnout_road_i of
                                        -1: begin
                                              vendx:=txpx+(g/2)*SIN(k3);          // short - for crossover.
                                              csrendx:=vendx-g*SIN(k3);           // x to curved stock rail end (slight error for curved crossing).
                                            end;
                                         0: begin
                                              vendx:=bnx+veelong*COS(k3)+scale/2; // normal - length of splice rail only  (add 6" for luck - looks better).
                                              csrendx:=vendx-g*SIN(k3);           // x to curved stock rail end (slight error for curved crossing).
                                            end;
                                         1: begin
                                              vendx:=turnoutx;           // long - x to rail end - full length of turnout. (could be shorter than normal, e.g. catch points).
                                              csrendx:=vendx-g*SIN(k3);  // 209a  was vendx;  ditto x to curved stock rail end.
                                            end;
                                       2,3: begin                                         // 209a              3 added 217a
                                              vendx:=xorg+turnout_road_endx+g*SIN(k3)/2;  // adjustable
                                              csrendx:=vendx-g*SIN(k3);                   // ditto x to curved stock rail end.
                                            end;


                                       else run_error(237);
                          end;//case
                        end;

                vendox:=vendx+j*SIN(k3);        // x to ditto outer edge.
                csrendox:=csrendx-j*SIN(k3);    // x to ditto outer edge.
              end
         else begin           // ditto for curviform crossing...

                temp:=SQR(tradius+j)-SQR(torgy-g-j);       //^^^
                if temp<minfp then bnox:=fpx+j/TAN(k3/2)   // ???
                              else bnox:=torgx+SQRT(temp)*SGZ(tradius);  // ^^^   // x to blunt nose outer edge.


                    // calc k4 swing and limit turnout road to 90 degrees less 0.001 radians max...

                tradius_cl:=tradius-g/2;     // centre-line radius, turnout road

                case turnout_road_i of
                              -1: if ABS(tradius_cl)<minfp      // crossover  //^^^
                                     then k4:=k4_limit
                                     else begin
                                            if ABS((txpx-torgx)/tradius_cl)>(1.0-minfp)    //^^^
                                               then k4:=k4_limit
                                               else k4:=ARCSIN((txpx-torgx)/tradius_cl);   //^^^ // crossover - angle to end of turnout road.
                                          end;

                               0: begin                         // normal
                                    k4:=tvjpk;                                                // normal length, stop at vee splice rail joint.
                                    if ABS(tradius_cl)>minfp then k4:=k4+scale/2/tradius_cl;  //^^^ // add 6" beyond joint for luck.
                                  end;

                               1: if ABS(tradius_cl)<minfp      // long
                                     then k4:=k4_limit                                         //^^^
                                     else begin
                                            if ABS((turnoutx-torgx)/tradius_cl)>(1.0-minfp)    //^^^
                                               then k4:=k4_limit
                                               else k4:=ARCSIN((turnoutx-torgx)/tradius_cl);   //^^^ // angle to end of long turnout road.
                                          end;

                             2,3: if ABS(tradius_cl)<minfp      // 209a adjustable          //  3 added 216c
                                     then k4:=k4_limit
                                     else begin
                                            if ABS((xorg+turnout_road_endx-torgx)/tradius_cl)>(1.0-minfp)
                                               then k4:=k4_limit
                                               else k4:=ARCSIN((xorg+turnout_road_endx-torgx)/tradius_cl);   // angle to end of adjustable turnout road.
                                          end;

                             else k4:=k3;   // !!!
                end;//case

                if ABS(k4)>k4_limit then k4:=SGZ(k4)*k4_limit;

                vendx:=torgx+tradius*SIN(k4);     //^^^
                vendox:=vendx+j*SIN(k4);
                csrendx:=vendx-g*SIN(k4);
                csrendox:=csrendx-j*SIN(k4);
              end;
      //--------------------

           //  main-side check rail dims:  mod: 071a  17-05-2001...
           //                        mod again: 079a  29-05-2003...

      if (timbers_equalized=False) and (half_diamond=False) and (force_eq=False)  //239b
         then begin
                if square_on_angled=False
                   then ckmidx_ms:=atx                                       // square-on timbering
                   //else ckmidx_ms:=atx+((atx-fpx)*TAN(k3/2)+g-fw)*TAN(k3);   // angled-on timbering - shift ms check rail forward

                   else ckmidx_ms:=atx+g*TAN(k3);   // 237c angled-on timbering - shift ms check rail forward
              end
        // else ckmidx_ms:=atx+((atx-fpx)*TAN(k3/2)+g-fw)*TAN(k3/2);  // timbering equalized - shift ms check rail forward

         else ckmidx_ms:=atx+g*TAN(k3/2);  // 237c  timbering equalized - shift ms check rail forward

      ck_shift:=ckmidx_ms-atx;  // 237b check rail shift for chairing   07-03-2023



             // 0.93.a old code instead ...


      ckendx:=ckmidx_ms+check_ms_extlen*inscale;  // inches full-size MS check rail end extension beyond "A".

      ckflarex:=ckendx-flen_me;                   // x to check rail flare-out.
      ckflarox:=ckflarex-j*TAN(k5_me/2);          // x to check rail flare-out, outer edge.

      ckx_ms:=ckendx-ckl_ms;                      // x to check rail start.

      ckfwx:=ckx_ms+flen_mw;                      // x to end of flare-in.
      ckfwox:=ckfwx+j*TAN(k5_mw/2);               // x to end of flare-in, outer edge.

      ckox:=ckx_ms+j*SIN(k5_mw)*get_flare_type(ccd.end_diff_mw.type_diff);     // x to check rail start.
      ckendox:=ckendx-j*SIN(k5_me)*get_flare_type(ccd.end_diff_me.type_diff);  // x to check rail end.


          // globals for 3D check rail chairing MS CC ...   237a

      ms_checkx1:=ckx_ms+4*inscale;   // flare-in       require 4" overlap beyond chair centre to insert a chair
      ms_checkx2:=ckfwx;              // parallel part
      ms_checkx3:=ckflarex;           // flare-out
      ms_checkx4:=ckendx-4*inscale;   // end of check rail    require 4" overlap beyond chair centre to insert a chair

                   // calc the virtual knuckle points :

            if xing_calc_i<>0  //  for generic or curviform crossing, knuckle on turnout radius :
               then begin
                      try
                        tcosk3:=tradius*COS(k3);
                        wingx:=torgx+SQRT(SQR(tradius)-SQR(tcosk3+fw))*SGZ(tradius);        // x to knuckle point, trg.
                        wingox:=torgx+SQRT(SQR(tradius+j)-SQR(tcosk3+fw+j))*SGZ(tradius);   // x to ditto, outer edge, tro.
                        wingcx:=torgx+SQRT(SQR(tradius-fw)-SQR(tcosk3))*SGZ(tradius);       // x to knuckle point, str.
                        wingcox:=torgx+SQRT(SQR(tradius-fw-j)-SQR(tcosk3-j))*SGZ(tradius);  // x to ditto, outer edge.
                      except
                             // ??? most likely a gaunt turnout with large positive offset.
                             // calc as if for regular crossing instead...

                        wingx:=fpx-fw*k3n;                  //  turnout rail
                        wingox:=wingx+j*TAN(k3/2);

                        wingcx:=fpx-fw/SIN(k3);             //  main rail
                        wingcox:=wingcx-j*TAN(k3/2);
                      end;//try
                    end
               else begin                                // for regular crossing :
                      wingx:=fpx-fw*k3n;                 // x dittos for above, on the straight.
                      wingox:=wingx+j*TAN(k3/2);

                      wingcx:=fpx-fw/SIN(k3);
                      wingcox:=wingcx-j*TAN(k3/2);
                    end;
          // -------------------------------------------------------

            if xing_calc_i<>0  //  for generic or curviform crossing, knuckle on turnout radius :
               then begin
                      try
                        ka:=ARCSIN((wingx-torgx)/tradius);   // knuckle angle in rail
                      except
                        ka:=k3;     // ???
                      end;//try
                    end
               else ka:=k3;  // regular crossing

                // and the knuckle radius terminal points on x   214a   ...

                // keep compiler happy - init for sharp knuckle...

            knuck_rad:=0;    // ignored

            wingx_plus:=wingx;
            wingx_minus:=wingx;

            wingox_plus:=wingox;
            wingox_minus:=wingox;

            wingcx_plus:=wingcx;
            wingcx_minus:=wingcx;

            wingcox_plus:=wingcox;
            wingcox_minus:=wingcox;

            if knuckle_code<>-1      // not sharp
               then begin
                      case knuckle_code of        // 0=normal, -1=sharp, 1=use knuckle_radius

                        0: knuck_rad:=k3n*scale;       // =0 normal   calc as GWR  rad=crossing angle in feet

                        1: knuck_rad:=knuckle_radius*inscale;  // use custom setting

                      end;//case

                      knuck_max:=ABS(fpx-wingx-inscale);  // bend must stop 1 inch scale in front of FP. Reduce bend radius if not.

                      tempx:=ABS(knuck_rad*TAN(ka/2));    // from knuckle to knuckle rad centre

                      if tempx>knuck_max
                         then begin
                                tempx:=knuck_max;
                                try
                                  knuck_rad:=tempx/TAN(ka/2);
                                except
                                  knuck_rad:=tempx;  // ??? arbitrary div0 protection
                                end;//try
                              end;

                      temp:=tempx*COS(ka);         // ditto along crossing angle

                      wing_bend_offset:=tempx*SIN(ka);  // used for knuckle marks.

                      wingx_plus:=wingx+tempx;     // knuckle radius terminal points...  turnout rail
                      wingx_minus:=wingx-temp;

                      wingox_plus:=wingx_plus;
                      wingox_minus:=wingx_minus+j*SIN(ka);


                      wingcx_plus:=wingcx+temp;      // main rail ...
                      wingcx_minus:=wingcx-tempx;

                      wingcox_plus:=wingcx_plus-j*SIN(ka);
                      wingcox_minus:=wingcx_minus;

                    end;

                           //  main-side wing rail:

  wflarex:=wingendx_ms-flen_mr;           // x to wing rail flare-out.
  wflarox:=wflarex-j*TAN(k5_mr/2);        // x to wing flare-out, outer-edge.

  wingendox:=wingendx_ms-j*SIN(k5_mr)*get_flare_type(ccd.end_diff_mr.type_diff);


  ms_wingx1:=wflarex;                // 241a  MS start of wing rail flare for chairing
  ms_wingx2:=wingendx_ms-4*inscale;  // end of MS wing rail require 4" overlap beyond chair centre to stop inserting xing BB chairs

                           //  turnout-side wing rail :

  wingcfpx:=fpx-fw*SIN(k3);           // x to wing rail at f.p.
  wingcfpox:=wingcfpx-j*SIN(k3);      // x to ditto, outer edge.

         // 0.94.a first part up to A timber is symmetrical with main-side..

  wl:=(atx-wingx)+wgl_ts-flen_tr;   // length along ts wing rail, knuckle to flare.
  wfp:=(atx-fpx)+wgl_ts-flen_tr;    // length along ts wing rail, f.p. to flare.

  case xing_calc_i of

           1: begin                         // for curviform V-crossing :
                k6:=wfp/(tradius-fw);                              // angle turned along wfp.
                k7:=flen_tr/(tradius-fw);                          // ditto along flare.
                flarecx:=torgx+(tradius-fw)*SIN(k3+k6);            // x to flare-out, gauge-face.
                flarecox:=flarecx-jbend_tr*SIN(k3+k6+k5_tr/2);     // x to ditto, outer edge.
                flcendx:=torgx+(tradius-(fw_end+ccd.end_diff_tr.gap_diff))*SIN(k3+k6+k7);        // x to end of wing rail, gf.

                case get_flare_type(ccd.end_diff_tr.type_diff) of
                     1: flcendox:=flcendx-j*SIN(k3+k6+k7+k5_tr);                                  // x to ditto, outer edge, bent flares.
                   else flcendox:=flcendx-(j+fw-(fw_end+ccd.end_diff_tr.gap_diff))*SIN(k3+k6+k7); // machined flares.
                end;//case
              end;

           0: begin                         // ditto for regular crossing :

                // !!! these are not correct for a return curve (parallel crossing) - not yet done 29-5-01 !!!

                flarecx:=wingcx+wl*COS(k3);
                flarecox:=flarecx-jbend_tr*SIN(k3+k5_tr/2);
                flcendx:=flarecx+(flen_tr/COS(k5_tr))*COS(k3+k5_tr);   // 0.94.a (flen_tr/COS(k5_tr)) = length along flare leg (was fleg)

                case get_flare_type(ccd.end_diff_tr.type_diff) of
                              1: flcendox:=flcendx-j*SIN(k3+k5_tr);                                   // x to ditto, outer edge, bent flares.
                            else flcendox:=flcendx-(j+fw-(fw_end+ccd.end_diff_tr.gap_diff))*SIN(k3);  // machined flares.
                end;//case

              end;

          -1: begin                              // ditto for generic crossing :
                flarecx:=wingcfpx+wfp*COS(k3);
                flarecox:=flarecx-jbend_tr*SIN(k3+k5_tr/2);
                flcendx:=flarecx+flen_tr*COS(k3+k5_tr);

                case get_flare_type(ccd.end_diff_tr.type_diff) of
                              1: flcendox:=flcendx-j*SIN(k3+k5_tr);                                   // x to ditto, outer edge, bent flares.
                            else flcendox:=flcendx-(j+fw-(fw_end+ccd.end_diff_tr.gap_diff))*SIN(k3);  // machined flares.
                end;//case
              end;
  end;//case


  ts_wingx1:=flarecx;            // 241a  TS start of wing rail flare for chairing
  ts_wingx2:=flcendx-4*inscale;  // end of TS wing rail require 4" overlap beyond chair centre to stop inserting xing BB chairs

  // ---------------------------------------------------------

        //  calc turnout-side check rail dims:  mod: v0.71.a  17-5-01...

  cuckrad:=csradius+fw;                              // rad to check-face.

  cuckflrad_tw:=csradius+fw_end+ccd.end_diff_tw.gap_diff;   // rad to flare-end TS working end.
  cuckflrad_te:=csradius+fw_end+ccd.end_diff_te.gap_diff;   // rad to flare-end TS extension end.

  cuckfpx:=fpx-(g-fw)*SIN(k3);                    // x to check rail at f.p.
  cuckfpox:=cuckfpx+j*SIN(k3);                    // x to ditto, outer edge.

              // align ts mid with "A"...

  cktsmid_offset:=(atx-cuckfpx)/COS(k3);      // length along check, from check at f.p. to intercept on "A". (for square-on timbering).

  ts_ck_pull_back:=0;  // init         237c

  if (timbers_equalized=True) or (half_diamond=True) or (force_eq=True)
     then begin
            //ts_ck_pull_back:=((atx-fpx)*TAN(k3/2)+g-fw)*(TAN(k3)-TAN(k3/2));

            ts_ck_pull_back:=g*TAN(k3/2);                      // 237c
            cktsmid_offset:=cktsmid_offset-ts_ck_pull_back;    // pull check rail back if equalized timbering.
          end
     else begin
            if square_on_angled=True
               then begin
                      //ts_ck_pull_back:=((atx-fpx)*TAN(k3/2)+g-fw)*TAN(k3);

                      ts_ck_pull_back:=g*TAN(k3);                       // 237c
                      cktsmid_offset:=cktsmid_offset-ts_ck_pull_back;   // or angled-on.
                    end;
          end;

  cucklb:=cktsmid_offset+check_ts_extlen*inscale-flen_te;   // length along check, f.p. to flare-out, straight crossing.

  if xing_calc_i=1         // curviform V-crossing, calc swing on cucklb.        and flen_te 0.96.a
     then begin
            if ABS(cuckrad)<minfp
               then begin
                      k10:=Pi/2;               // !!!
                      k11:=pi/2;               // 0.96.a
                    end
               else begin
                      k10:=cucklb/cuckrad;
                      k11:=flen_te/cuckrad;  // 0.96.a
                    end;
          end;

  cuckla:=ckl_ts-cucklb-flen_te-flen_tw;       // length along turnout-side check rail, flare-in to f.p.

  if xing_calc_i<>0            // for curviform or generic straight crossing:
     then begin
            k8:=cuckla/cuckrad;           // angle turned through cuckla.
            k9:=flen_tw/cuckrad;          // angle turned through check rail flare-in.

            if k8<k3            // check-rail flared-in length starts on the turnout curve...
               then begin
                      cuckfwx:=torgx+cuckrad*SIN(k3-k8);                  // x to check rail at flare-in.
                      cuckfwox:=cuckfwx+jbend_tw*SIN(k3-k8+k5_tw/2);      // x to ditto, outer-edge.
                    end
               else begin     // 0.71.a long check working length - ignore switch rad and planing (approximation)..

                      cuckfwx:=torgx-(cuckla-cuckrad*k3);           // x to check rail at flare-in.
                      cuckfwox:=cuckfwx+jbend_tw*SIN(k5_tw/2);      // x to ditto, outer-edge (assumes bent flares).
                    end;

            if (k8+k9)<k3     // check rail starts on turnout curve...
               then begin
                      cuckx:=torgx+cuckflrad_tw*SIN(k3-k8-k9);         // x to check rail start.

                      case get_flare_type(ccd.end_diff_tw.type_diff) of
                                    1: cuckox:=cuckx+j*SIN(k3-k8-k9+k5_tw);                                  // x to ditto, outer-edge. bent flares.
                                  else cuckox:=cuckx+(j+fw-(fw_end+ccd.end_diff_tw.gap_diff))*SIN(k3-k8-k9); // machined flares.
                      end;//case

                    end
               else begin     // 0.71.a long check working length - ignore switch rad and planing (approximation)..

                      cuckx:=cuckfwx-flen_tw;         // x to check rail start.
                      cuckox:=cuckx+jbend_tw*SIN(k5_tw/2)*get_flare_type(ccd.end_diff_tw.type_diff);
                    end;
          end
     else begin          // for regular crossing (also parallel crossing):

            if ABS(COS(k3))<minfp then angled_sl:=sl                 // !!!
                                  else angled_sl:=sl/COS(k3);        // angled length along entry staight to f.p.

            if (cuckla<angled_sl) or (tradius_is_straight=True)      //~~~ 0.79.a all on straight crossing part...
               then begin
                      cuckfwx:=cuckfpx-cuckla*COS(k3);
                      cuckfwox:=cuckfwx+jbend_tw*SIN(k3+k5_tw/2);
                      cuckx:=cuckfwx-(flen_tw/COS(k5_tw))*COS(k3+k5_tw);     // (flen_tw/COS(k5_tw)) was fleg (calc flare as on straight part even if running rail is on curve).

                      case get_flare_type(ccd.end_diff_tw.type_diff) of
                                    1: cuckox:=cuckx+j*SIN(k3+k5_tw);        // bent flares.
                                  else cuckox:=cuckx+(j+fw-(fw_end+ccd.end_diff_tw.gap_diff))*SIN(k3);
                      end;//case
                    end
               else begin

                      k88:=(cuckla-angled_sl)/cuckrad;  // angle turned through flared-in length.
                      k9:=flen_tw/cuckrad;              // angle turned through check rail flare-in.

                      if k88<k3       // check-rail flared-in length starts on the turnout curve...
                         then begin
                                cuckfwx:=torgx+cuckrad*SIN(k3-k88);                  // x to check rail at flare-in.
                                cuckfwox:=cuckfwx+jbend_tw*SIN(k3-k88+k5_tw/2);      // x to ditto, outer-edge.
                              end
                         else begin     // 0.71.a long check working length - ignore switch rad and planing (approximation)..

                                cuckfwx:=torgx-(cuckla-angled_sl-cuckrad*k3);  // x to check rail at flare-in.
                                cuckfwox:=cuckfwx+jbend_tw*SIN(k5_tw/2);       // x to ditto, outer-edge (assumes bent flares).
                              end;

                      if (k88+k9)<k3     // check rail starts on turnout curve...
                         then begin
                                cuckx:=torgx+cuckflrad_tw*SIN(k3-k88-k9);      // x to check rail start.

                                case get_flare_type(ccd.end_diff_tw.type_diff) of
                                              1: cuckox:=cuckx+j*SIN(k3-k88-k9+k5_tw);                                  // x to ditto, outer-edge. bent flares.
                                            else cuckox:=cuckx+(j+fw-(fw_end+ccd.end_diff_tw.gap_diff))*SIN(k3-k88-k9); // machined flares.
                                end;//case

                              end
                         else begin     // 0.71.a long check working length - ignore switch rad and planing (approximation)..

                                cuckx:=cuckfwx-flen_tw;         // x to check rail start.
                                cuckox:=cuckx+jbend_tw*SIN(k5_tw/2)*get_flare_type(ccd.end_diff_tw.type_diff);
                              end;
                    end;
          end;

   ts_checkx1:=cuckx+ts_ck_pull_back*2+4*inscale;     // 237c  flare-in       require 4" overlap beyond chair centre to insert a chair
   ts_checkx2:=cuckfwx+ts_ck_pull_back*2  ;           // 237c  parallel part

                   // TS check rail extension end

  if xing_calc_i=1                                     // for curviform V-crossing only :
     then begin
            cuckflx:=torgx+cuckrad*SIN(k3+k10);                  // x to check rail flare-out.
            cuckflox:=cuckflx+jbend_te*SIN(k3+k10-k5_te/2);      // x to ditto, outer-edge (assumes bent flares).
            cuckendx:=torgx+cuckflrad_te*SIN(k3+k10+k11);  // 0.97.a    // x to check rail end.

            case get_flare_type(ccd.end_diff_te.type_diff) of

                1: cuckendox:=cuckendx+j*SIN(k3+k10+k11-k5_te);     // 0.97.a                                 // x to ditto, outer-edge. bent flares.
              else cuckendox:=cuckendx+(j+fw-(fw_end+ccd.end_diff_te.gap_diff))*SIN(k3+k10+k11);   // 0.97.a  // machined flares.

            end;//case


          end
     else begin                      // straight crossing, normal or original :

           // !!!! these are not correct for a return curve (parallel crossing) - not yet done 29-5-01 !!!!

            cuckflx:=cuckfpx+cucklb*COS(k3);                    // x to check rail flare-out.
            cuckflox:=cuckflx+jbend_te*SIN(k3-k5_te/2);         // x to ditto, outer-edge (assumes bent flares).

            cuckendx:=cuckflx+(flen_te/COS(k5_te))*COS(k3-k5_te);   // x to check rail end. (flen_te/COS(k5_te)) was fleg

            case get_flare_type(ccd.end_diff_te.type_diff) of
                          1: cuckendox:=cuckendx+j*SIN(k3-k5_te);                                   // x to ditto, outer-edge. bent flares.
                        else cuckendox:=cuckendx+(j+fw-(fw_end+ccd.end_diff_te.gap_diff))*SIN(k3);  // machined flares.
            end;//case

          end;


  ts_checkx3:=cuckflx+ts_ck_pull_back*2;                // 237c TS flare-out
  ts_checkx4:=cuckendx+ts_ck_pull_back*2-4*inscale;     // end of TS check rail    require 4" overlap beyond chair centre to insert a chair


//__________________

             // K-crossing check rails...


  if (half_diamond=True) and (fixed_diamond=True)
     then begin

                     // diagonal-side check rail (straight)...

            if (hd_timbers=2) or (hd_timbers=3)       // timbers on main or both sides for slip ...
               then begin
                      if g<(56*inscale)                                           // not P4 etc.
                         then kckl_dk:=(62+ccd.end_diff_dk.len_diff)*inscale      // 215a  124" length for slips arbitrary 9" shorter each end
                         else kckl_dk:=(71+ccd.end_diff_dk.len_diff)*inscale;     // 215a  142" prototype length for slips
                    end
               else
            if hdkn>(6+minfp) then kckl_dk:=(kck2_long/2+ccd.end_diff_dk.len_diff)*inscale  // 0.94.a add mouse diff    // overall length on half-diamond.
                              else kckl_dk:=(kck1_long/2+ccd.end_diff_dk.len_diff)*inscale;

            kckdsx:=toex-(g-fw)*TAN(hdk/2);       // x to start of DS check rail, gf. aq=28.
            kckdsox:=toex-(g-fw-j)*TAN(hdk/2);    // ditto oe.                        aq=29.

            kckdsflx:=kckdsx+kckl_dk-flen_dk;            // x to DS flare bend, gf.
            kckdsflox:=kckdsflx-jbend_dk*SIN(k5_dk/2);   // ditto oe.

            kckdsflendx:=kckdsflx+flen_dk*COS(k5_dk);     // x to end of DS check rail, gf.
            kckdsflendox:=kckdsflendx-j*SIN(k5_dk)*get_flare_type(ccd.end_diff_dk.type_diff);

                     // main-side check rail (angled at hdk)...

            if (hd_timbers=1) or (hd_timbers=3)                        // timbers on diagonal or both sides for slip ...
               then begin
                      if g<(56*inscale)                                           // not P4 etc.
                         then kckl_mk:=(62+ccd.end_diff_mk.len_diff)*inscale      // 215a  124" length for slips arbitrary 9" shorter each end
                         else kckl_mk:=(71+ccd.end_diff_mk.len_diff)*inscale;     // 215a  142" prototype length for slips
                    end
               else
            if hdkn>(6+minfp) then kckl_mk:=(kck2_long/2+ccd.end_diff_mk.len_diff)*inscale  // 0.94.a add mouse diff    // overall length on half-diamond.
                              else kckl_mk:=(kck1_long/2+ccd.end_diff_mk.len_diff)*inscale;

            kckmsx:=toex-fw*TAN(hdk/2);          // x to start of MS check rail, gf. aq=26.
            kckmsox:=toex-(fw+j)*TAN(hdk/2);     // ditto oe.                        aq=27.

                // for irregular half-diamonds these are approximations (hdk angle valid only at start)...

            kckmsflx:=kckmsx+(kckl_mk-flen_mk)*COS(hdk);         // x to MS flare bend, gf.

            kckmsflox:=kckmsflx-jbend_mk*SIN(k5_mk/2+hdk);       // ditto oe.

            kckmsflendx:=kckmsflx+flen_mk*COS(k5_mk+hdk);        // x to end of MS check rail, gf.

            if (tradius_is_straight=True) or (ABS(tradius)<minfp)   // 0.93.a
               then swing_on_k_check:=0
               else swing_on_k_check:=kckl_mk/tradius; // radians

            case get_flare_type(ccd.end_diff_mk.type_diff) of
                          1: kckmsflendox:=kckmsflendx-j*SIN(k5_mk+hdk+swing_on_k_check);         // bent flare.
                        else kckmsflendox:=kckmsflendx-(j+fw-(fw_end+ccd.end_diff_mk.gap_diff))*SIN(hdk+swing_on_k_check);   // machined flare.
            end;//case

          end;

                 //  now fill terminal points on the turnout curved rails...

            // seg_termx[0,  = switch front...

      if (gaunt=True) and (half_diamond=False)
         then begin
                seg_termx[0,2]:=0;      //  aq=2 turnout rail, gauge face, switch front, gauntletted approach track
                seg_termx[0,10]:=0;     //  aq=10 ditto outer.
              end
         else begin
                seg_termx[0,2]:=nodraw;               //  aq=2 turnout rail, gauge face, switch front, do not draw.
                seg_termx[0,10]:=nodraw;              //  aq=10 ditto outer, do not draw.
              end;

      if half_diamond=True
         then begin
                seg_termx[0,3]:=nodraw;     //  aq=3 diagonal stock rail, gauge face, do not draw for h-d.
                seg_termx[0,11]:=nodraw;    //  aq=11 ditto outer edge, do not draw.
              end
         else begin
                seg_termx[0,3]:=0;          //  turnout curved stock rail, gauge face, switch front start.
                seg_termx[0,11]:=0;         //  ditto outer edge.
              end;

            // seg_termx[1,  = planing, and set straight section...

      if half_diamond=True
         then begin
                seg_termx[1,2]:=nodraw;     // aq=2 trg   no planing section for h-d.
                seg_termx[1,10]:=nodraw;    // aq=10 tro  do not draw.

                seg_termx[1,3]:=nodraw;     // aq=3 srg   ditto diagonal stock rail
                seg_termx[1,11]:=nodraw;    // aq=11 sro

              end
         else begin
                if gaunt=True
                   then begin
                          seg_termx[1,2]:=toex;     // aq=2 trg
                          seg_termx[1,10]:=toex;    // aq=10 tro

                          seg_termx[1,3]:=toex;     // aq=3 srg
                          seg_termx[1,11]:=toex;    // aq=11 sro
                        end
                   else begin
                          seg_termx[1,2]:=toex;                          // aq=2 trg  planing start
                          seg_termx[1,10]:=nodraw;    // aq=10 tro  do not draw.

                          seg_termx[1,3]:=setx;       // aq=3 srg  "set" start.
                          seg_termx[1,11]:=setox;     // aq=11 sro
                        end;
              end;

            // seg_termx[2,  = switch radius ...

      if half_diamond=True
         then begin
                seg_termx[2,2]:=nodraw;     // aq=2 trg   no switch radius section for h-d.
                seg_termx[2,10]:=nodraw;    // aq=10 tro  do not draw.

                seg_termx[2,3]:=nodraw;     // aq=3 srg  ditto for diagonal stock rail.
                seg_termx[2,11]:=nodraw;    // aq=11 sro
              end
         else begin
                if gaunt=True
                   then begin
                          seg_termx[2,2]:=toex;     // aq=2 trg
                          seg_termx[2,10]:=toex;    // aq=10 tro

                          seg_termx[2,3]:=toex;     // aq=3 srg
                          seg_termx[2,11]:=toex;    // aq=11 sro
                        end
                   else begin
                          seg_termx[2,2]:=plx;                          // aq=2 trg  switch curve start.
                          if plox<heelox then seg_termx[2,10]:=plox     // aq=10 tro  outer-edge, normal intercept on stock rail,
                                         else seg_termx[2,10]:=nodraw;  // aq=10 or no switch curve draw if extra-wide custom rail.

                          seg_termx[2,3]:=plx3;      // aq=3 srg  ditto for curved stock rail. (!!! coincident with the "set" for straight switch or curved planing).
                          seg_termx[2,11]:=plox11;   // aq=11 sro
                        end;
              end;

            // seg_termx[3,  = turnout radius ...

      if half_diamond=True
         then begin
                if fixed_diamond=True
                   then begin

                           // 227a  blunt K-nose is 1/2" for both FB and BH (some companies 3/4", GWR is 11/16")

                           // first do blunt nose..

                          bnw:=0.5*inscale;  // 227a (bn_wide-0.125)*inscale;    // width of blunt nose.
                                             // if bnw<0 then bnw:=0;

                              // x to diamond point blunt nose (gauge-face intersection)...

                          tempy:=fw+bnw*COS(hdk/2);  // offset to tip.

                          if tradius_is_straight=True
                             then begin
                                    seg_termx[3,2]:=toex+tempy*hdkn;   // 20-09-04 gauge-face.
                                    seg_termx[3,10]:=plox+fw*hdkn;     // outer-edge.
                                  end
                             else begin
                                    seg_termx[3,2]:=torgx+SGZ(tradius)*SQRT(SQR(tradius)-SQR(torgy-tempy));  // gauge-face.
                                    seg_termx[3,10]:=torgx+SGZ(tradius)*SQRT(SQR(tradius+j)-SQR(torgy-fw));  // outer-edge.
                                  end;

                        end
                   else begin   // switch diamond..

                          seg_termx[3,2]:=toex;    // gauge-face.

                          if tradius_is_straight=True       // outer-edge..
                             then seg_termx[3,10]:=plox
                             else seg_termx[3,10]:=torgx+SGZ(tradius)*SQRT(SQR(tradius+j)-SQR(torgy));
                        end;

                seg_termx[3,3]:=toex-g*TAN(hdk/2);      //0-(g/2)*SIN(hdk/2);           // aq=3 srg
                seg_termx[3,11]:=toex-(g+j)*TAN(hdk/2); //seg_termx[3,3]-j*TAN(hdk/2); // aq=11 sro

              end
         else begin     // turnout...

                if gaunt=True
                   then begin
                          seg_termx[3,2]:=toex;     // aq=2 trg
                          seg_termx[3,10]:=toex;    // aq=10 tro

                          seg_termx[3,3]:=toex;     // aq=3 srg
                          seg_termx[3,11]:=toex;    // aq=11 sro
                        end
                   else begin
                          seg_termx[3,2]:=heelx;                                            // aq=2 trg  turnout curve start.
                          if plox>heelox then seg_termx[3,10]:=plox     // aq=10 tro  outer-edge, intercept on (custom extra-wide) stock rail,
                                         else seg_termx[3,10]:=heelox;  // aq=10 or normal draw from heel.

                          seg_termx[3,3]:=heelx-g*SIN(k2);              // aq=3 srg
                          seg_termx[3,11]:=heelx-(g+j)*SIN(k2);         // aq=11 sro
                        end;
              end;

            // seg_termx[4,  = V-crossing...

      case xing_calc_i of
               0: begin                                // for regular crossing:
                    seg_termx[4,2]:=tx;                     // aq=2 trg  curve ends at entry straight.
                    seg_termx[4,10]:=tx+j*SIN(k3);          // aq=10 tro ditto.

                    seg_termx[4,3]:=tx-g*SIN(k3);           // aq=3 srg  ditto for stock rail.
                    seg_termx[4,11]:=tx-(g+j)*SIN(k3);      // aq=11 sro ditto.
                  end;

               1: begin                                // for curviform crossing:
                    seg_termx[4,2]:=wingx_minus;        //  214a trg  end of turnout curve at start of knuckle bend.
                    seg_termx[4,10]:=wingox_minus;      //  214a tro  ditto.

                    seg_termx[4,3]:=csrendx;                // srg  end of turnout curve.
                    seg_termx[4,11]:=csrendox;              // sro  ditto.
                  end;

              -1: begin                                // for generic crossing:
                    seg_termx[4,2]:=wingx_minus;        //  214a trg  end of turnout curve at start of knuckle bend.
                    seg_termx[4,10]:=wingox_minus;      //  214a tro  ditto.

                    seg_termx[4,3]:=csrfpx;                 // srg  end of turnout curve.
                    seg_termx[4,11]:=csrfpox;               // sro  ditto.
                  end;
      end;//case

      if plain_track=True         //  stop the curved stock rail at the switch-front-joint     if plain track (approach track) only wanted
         then begin
                xorg_bl_mod:=xorg-pt_blank;            // - plain track blanking  234d ..
                if xorg_bl_mod<0 then xorg_bl_mod:=0;

                seg_termx[1,3]:=xorg_bl_mod;
                seg_termx[1,11]:=xorg_bl_mod;

                for i:=2 to 4 do begin
                                   seg_termx[i,3]:=nodraw;      // aq=3   gauge-face.
                                   seg_termx[i,11]:=nodraw;     // aq=11  outer-edge.
                                 end;
              end;

    if isolated_crossing=True
       then startx:=Min(ckx_ms,cuckx)-3*scale;   // 217a  blanking      allow 3ft in front of check rails

    RESULT:=True;  // all done ok. added 0.81

  except
    abandon_calcs:=True;    // added 0.81
  end;//try
end;
//_______________________________________________________________________________________________________________________________

function ram_clm_str(ram_kn:extended):string;

var
  clm_kn:extended;

begin        //  calc CLM unit angle...
  try
    if ram_kn>minfp then clm_kn:=1/2/TAN(ARCTAN(1/ram_kn)/2)
                    else clm_kn:=0;                       // ??? kn zero or neg ???

    RESULT:='1:'+FormatFloat('0.00',ram_kn)+' RAM  ( 1:'+FormatFloat('0.00',clm_kn)+' CLM )';
  except
    RESULT:='';
  end;//try
end;
//______________________________________________________________________________

procedure compile_template_data;   // compile results of calcs.

var
  trans_0rad, trans_orgrad, trans_toerad, trans_heelrad, trans_dprad, trans_txrad, trans_fprad, trans_vjrad, trans_9rad:extended;
  rcurv_0, rcurv_toe, rcurv_heel, rcurv_dp, rcurv_tx, rcurv_fp, rcurv_vj:extended;
  rmin_inch:extended;

  rcurv_return, trans_retstartrad, rcurv_retstart, trans_retendrad, rcurv_retend:extended;  // 0.79.a

  slew_rad, slew_minrad, trans_slewrad:extended;
  no_zone_str:string;
  num_dec:integer;

  hand_str:string;
  temp1, temp2, temp3:extended;
  indent_str,temp_str:string;
  temp_flag:boolean;

  tbstyle_str:string;
  hd_str,reg_hd_str,section_str,vertical_str:string;

  num_rails, num_sl_over, num_sl_full:integer;
  percent_over, tb_roll_mm, dummy1:extended;
  dummy_i:integer;
  tb_rolling:boolean;
  tb_roll_str:string;

  pin,pout:Tpex;

  no_info_update_needed:integer;

  dummy_notch1,dummy_notch2,dummy_notch3:Tnotch;



begin
    no_info_update_needed:=spot_mod
                          +zoom_mod
                          +zoffset_mod
                          +bunch_mod
                          //+notch_mod  out 23-04-05 (needed for peg position from notch)
                          +ring_mod
                          +ringdia_mod
                          +ruler1_mod
                          +ruler2_mod
                          +porg_mod
                          +out_factor_mod
                          +shift_keeps_mod
                          +twist_keeps_mod
                          +oneshape_shift_mod

                          +clip_size_mod        // 229a..
                          +clip_angle_mod

                          +corner1_mod
                          +corner2_mod
                          +oneshape_scale_mod
                          +allshapes_shift_mod
                          +allshapes_scale_mod
                          +allshapes_rotate_mod
                          +move_labels_mod        // 0.82.d

                          +rect_left_mod          // 0.93.a ...
                          +rect_right_mod
                          +rect_top_mod
                          +rect_bottom_mod
                          +rect_move_mod;

    if no_info_update_needed<>0 then EXIT;     // mouse action in progress is not for control template.

    if check_diffs_form.Showing=True then update_check_diff_panels;  // 0.94.a

    rmin_mm:=max_rad;            // init for min radius calcs.

    radius_for_obtain:=max_rad;  // 205e  init

    info_text_list.Clear;

    temp_flag:=False;      // first any top panel warnings...

    if (k3n<(4-minfp)) and (half_diamond=False) and (plain_track=False) and (g>(4*scale))  // 0.93.a not for n.g. 4ft and below
       then begin
              program_help_str:='    `0Short-angle  V-crossings`9'
                               +'||The current V-crossing angle is 1:'+FormatFloat('0.##',k3n)
                               +'||`0Turnouts`3 having a crossing angle shorter than 1:4 are unusual in standard-gauge track.'
                               +' The radius is likely to be too sharp for normal rolling-stock.'
                               +'||Templot allows turnouts with crossing angles down to 1:0.5 for narrow-gauge, industrial, and contractor''s lines, etc. You will probably need a custom switch for these.'
                               +'||Short-angle `0diamond-crossings`3 are not unusual, but prototype designs vary.'
                               +'||For all templates with very short crossing angles, Templot has no prototype information to lay out the timbers and some customizing will be needed to match your prototype designs.'
                               +' Click the `0REAL > TIMBERING > TIMBERING DATA...`1 and `0REAL > SHOVE TIMBERS`1 menu items. Add and adjust `0bonus`1 timbers to fill the gaps as necessary,'
                               +' or click the `0REAL > TIMBERING > SHORT-ANGLE TIMBERING > CONFLICTED`1 menu option to have multiple overlapped timbers which will need adjustment.'
                               +'||It is likely that the check rails will also need adjustment - click the `0real > adjust check rails...`1 menu item.' 
                               +'||Also for very short crossing angles, some of the `0tools`1 menu functions may fail to produce a satisfactory result.'
                               +'|| • If you are seeing this message while using partial templates to create complex formations, you can probably ignore it.';

              pad_form.program_warning_panel.Caption:='                   1 : '+FormatFloat('0.##',k3n)+'  V-crossing angle is shorter than 1 : 4   -   please  click  here';
              temp_flag:=True;
            end;


    if (k3n>k2n) and (half_diamond=False) and (plain_track=False)  // 0.93.a  irregular warning
       then begin
              program_help_str:='The current V-crossing angle is 1:'+FormatFloat('0.##',k3n)
                               +'||This is flatter than the heel angle of the currently selected switch size.'
                               +'||The equivalent turnout-road radius is therefore negative and this very long turnout is unlikely to be of any practical use.'
                               +'||You should probably use a longer switch size, or shorten the V-crossing angle to match the current switch size.'
                               +'||Click the `0TEMPLATE > SWITCH SETTINGS...`1 menu item, or the `0template > V-crossing settings...`z menu item.';
              pad_form.program_warning_panel.Caption:='                   1 : '+FormatFloat('0.##',k3n)+'  V-crossing angle is flatter than the switch heel   -   please  click  here';
              temp_flag:=True;
            end;

    if (temp_flag=True) and (creating_tandem=False) and (gaunt=False)  // 218a
       then pad_form.program_warning_panel.Show
       else pad_form.program_warning_panel.Hide;


    reg_hd_str:='';      // keep compiler happy.

    info_form.gauge_bang_label.Caption:=' '+Trim(gauge_str)+' ';        // 216a

    data_entry_form.entry_gauge_label.Caption:=info_form.gauge_bang_label.Caption;     // 241e

    try
      with info_form do begin

        if rail_section=2        // 0.95.a
           then temp_str:=' FB • '
           else temp_str:=' BH • ';

        temp_str:=temp_str+Trim(gauge_str)+' ';    // 0.95.a

// OT 2024        fv_gauge_str:=temp_str;  // label for file viewer 208d

        temp_str:=temp_str+' ';  // 208d

        if gauge_i=gauge_form.gauge_listbox.Items.Count-1
           then temp_str:=temp_str+round_str(scale,2)+' mm / ft    ';     // exact scale.

        if plain_track=False
           then begin
                  if half_diamond=True
                     then begin
                            temp_str:=temp_str+current_switch_name;
                            if tradius_is_straight=True then reg_hd_str:=' (regular half-diamond)'
                                                        else reg_hd_str:=' (irregular half-diamond)';
                          end
                     else begin
                            case csi.group_code of
                                    1: temp_str:=temp_str+Copy(current_switch_name,0,7);  // straight switches.
                                    2: temp_str:=temp_str+Copy(current_switch_name,0,18); // REA switches.
                                    3: temp_str:=temp_str+Copy(current_switch_name,0,14); // GWR flexible switches + 30ft straight switch.
                                    4: temp_str:=temp_str+Copy(current_switch_name,0,16); // GWR heel switches.
                                    5: temp_str:=temp_str+Copy(current_switch_name,0,21); // FB-109 switches.
                                    6: temp_str:=temp_str+Copy(current_switch_name,0,20); // FB BS-110A switches.
                                  else temp_str:=temp_str+current_switch_name;            // custom switches
                            end;//case
                          end;

                  temp_str:=temp_str+'  +  V- '+FormatFloat('0.##',k3n)+'   ';
                end
           else temp_str:=temp_str+'plain track   ';


        case hand_i of
                 0: temp_str:=temp_str+'Y ';
                 1: temp_str:=temp_str+'LH ';
                -1: temp_str:=temp_str+'RH ';
               else run_error(82);
        end;//case

        gauge_label.Caption:=temp_str;
        Caption:='   information         '+temp_str;    // copy to the form caption.
      end;//with info_form

      case hand_i of
               0: hand_str:='unhanded';
               1: hand_str:='left-hand';
              -1: hand_str:='right-hand';
             else run_error(82);
      end;//case

      with info_text_list do begin

                           // show the main details:

          Add('template generated at '+TimeToStr(Time)+' on '+DateToStr(Date)+' using '+Application.Title+' v:'+round_str(program_version/100,2)+version_build);
          Add('scale = '+round_str(scale,2)+' mm/ft     scale ratio = 1:'+round_str(304.8/scale,2));
          Add('track gauge = '+round_str(g,2)+'    flangeway gap = '+round_str(fw,2));

          if (ABS(nomrad)>max_rad_test) and (spiral=False)
             then Add('template: straight')
             else if spiral=False then Add('template: curved (constant radius)')
                                  else Add('template: curved (transition)');

          if (plain_track=True) and (no_timbering=True)
             then Add('no timbering');

          if vertical_rails=True then vertical_str:='rails vertical'
                                  else vertical_str:='rails inclined at '+round_str(rail_inclination*180/Pi,2)+' degrees';

          case rail_section of
                            0: section_str:='no rails';
                            1: section_str:='rail head only (bullhead): '+vertical_str;
                            2: section_str:='rail head and foot (flatbottom): '+vertical_str;
                          else section_str:='';
          end;//case


          Add(section_str);

          Add('------------');

          case hand_i of
                   0: temp_str:='Y ';
                   1: temp_str:='LH ';
                  -1: temp_str:='RH ';
                 else run_error(36);
          end;//case

          if (plain_track=False) and (half_diamond=False)
             then begin
                    if gaunt=True
                       then Add(temp_str+'gaunt turnout:')
                       else Add(temp_str+'turnout:');
                  end
             else if half_diamond=True
                     then begin
                            if tradius_is_straight=True then hd_str:='regular'
                                                        else hd_str:='irregular';

                            if fixed_diamond=True then Add(temp_str+hd_str+' half-diamond, fixed K-crossings (fixed diamond):')
                                                  else Add(temp_str+hd_str+' half-diamond, movable K-crossings (switch-diamond):');
                          end
                     else Add(temp_str+'plain track in'+plain_track_form.plain_track_spacings_listbox.Items.Strings[pt_i]+' ( rail length = '+round_str(railen[pt_i]*inscale,2)+' ):');

          if plain_track=False
             then begin
                    if half_diamond=False
                       then begin
                              if joggled=True then temp_str:='(joggled)'
                                              else temp_str:='(unjoggled)';

                              if gaunt=True then temp_str:=temp_str+', gaunt offset = '+round_str(gaunt_offset_in*inscale,2);

                              case hand_i of
                                       0: Add(current_switch_name+' split-symmetrical switch '+temp_str);
                                       1: Add(current_switch_name+' left-hand switch '+temp_str);
                                      -1: Add(current_switch_name+' right-hand switch '+temp_str);
                                     else run_error(36);
                              end;//case
                            end;

                    temp_str:=ram_clm_str(k3n);

                    case xing_type_i of
                            0 : begin
                                  temp_str:=temp_str+' regular V-crossing';
                                  if retpar_i=1 then temp_str:=temp_str+' (returning parallel)';
                                end;

                            -1: begin
                                  temp_str:=temp_str+' generic V-crossing';
                                  if retpar_i=1 then temp_str:=temp_str+' (returning parallel)';
                                end;

                             1: temp_str:=temp_str+' curviform V-crossing';

                           else run_error(48);

                    end;//case

                    if (xing_type_i<>0) and (tradius_is_straight=True)
                       then begin
                              if half_diamond=True then temp_str:=temp_str+' (converted to regular crossing for regular half-diamond)'
                                                   else temp_str:=temp_str+' (converted to regular crossing because turnout-curve is straight)';
                            end;

                    Add(temp_str);


                    if half_diamond=True then Add(ram_clm_str(k1n)+' K-crossing');

                    if no_timbering=True
                       then tbstyle_str:='no'
                       else begin
                              if half_diamond=True
                                 then tbstyle_str:='equalized-constant'
                                 else begin
                                        if (timbers_equalized=False) and (force_eq=False)
                                           then begin
                                                  if square_on_angled=True then tbstyle_str:='angled-on'
                                                                           else tbstyle_str:='square-on';
                                                end
                                           else begin
                                                  if equalizing_fixed=True then tbstyle_str:='equalized-constant'
                                                                           else tbstyle_str:='equalized-incremental';
                                                end;
                                      end;
                            end;
                    Add(tbstyle_str+' timbering');
                  end;


          if slewing=True
             then begin
                    Add('------------');
                    Add('N.B. this template includes SLEWING for all or part of its length');
                    Add('the radius figures quoted below do not apply');
                    Add('within the slewing zone except where stated');

                    if (plain_track=False) and (half_diamond=False)
                       then begin
                              Add('');
                              Add('the figures quoted below for the switch and turnout radii,');
                              Add('and for the smallest radius on this template, do not apply');
                              Add('if any part of the turnout lead overlaps the slewing zone');
                            end;

                    if (plain_track=False) and (half_diamond=True)
                       then begin
                              Add('');
                              Add('the figures quoted below for the diagonal-road radius,');
                              Add('and for the smallest radius on this template, do not apply');
                              Add('if any part of the half-diamond lead overlaps the slewing zone');
                            end;
                  end;


          if ((ABS(nomrad)<max_rad_test) or (spiral=True)) and (plain_track=False)     // do calcs for approx radius in turnout road and limit checks...
             then begin

                    Add('------------');
                    if spiral=False    // fixed curve.
                       then begin

                              Add('main-road centre-line radius = '+rad_str(clrad1,2)+' (constant radius)');

                              if half_diamond=False
                                 then begin
                                        rcurv_toe:=curved_onto_calc(equiv_rad-g/2,clrad1);

                                        rcurv_heel:=calculate_turnout_radius(clrad1,dummy_notch1,dummy_notch2,dummy_notch3);       // 218a    rcurv_heel:=curved_onto_calc(tradius-g/2,clrad1);    //^^^

                                        Add('resultant switch radius (centre-line) = '+rad_str(rcurv_toe,0)+' approx.');

                                        if gaunt=True then Add('resultant gaunt turnout radius (centre-line) = '+rad_str(curved_onto_calc(tradius-g/2,clrad1),0)+' approx.')   // so info matches obtain-for-gaunt info
                                                      else Add('resultant turnout radius (centre-line) = '+rad_str(rcurv_heel,2));

                                        if rmin_mm>ABS(rcurv_toe) then rmin_mm:=ABS(rcurv_toe);
                                        if rmin_mm>ABS(rcurv_heel) then rmin_mm:=ABS(rcurv_heel);

                                        radius_for_obtain:=rcurv_heel;  // 205e

                                      end
                                 else begin
                                        if tradius_is_straight=True
                                           then begin
                                                  Add('diagonal-road centre-line radius = '+rad_str(clrad1,2)+reg_hd_str);

                                                  radius_for_obtain:=clrad1;  // 205e
                                                end
                                           else begin
                                                  rcurv_heel:=calculate_turnout_radius(clrad1,dummy_notch1,dummy_notch2,dummy_notch3);   // 218a    rcurv_heel:=curved_onto_calc(tradius-g/2,clrad1);
                                                  Add('resultant diagonal-road centre-line radius = '+rad_str(rcurv_heel,2)+reg_hd_str);
                                                  if rmin_mm>ABS(rcurv_heel) then rmin_mm:=ABS(rcurv_heel);

                                                  radius_for_obtain:=rcurv_heel;  // 205e
                                                end;
                                      end;


                              if retpar_i=1      // parallel V-crossing  0.79.a
                                 then begin
                                        rcurv_return:=curved_onto_calc(retr,0-clrad1);                //  (opposite sense, so 0-)
                                        if rmin_mm>ABS(rcurv_return) then rmin_mm:=ABS(rcurv_return);
                                        Add('resultant return curve radius (centre-line) = '+rad_str(rcurv_return,0)+' approx.');
                                        Add('');
                                        Add('loop radius beyond return curve (centre-line) = '+rad_str((clrad1-trtscent),2));
                                        if rmin_mm>ABS(clrad1-trtscent) then rmin_mm:=ABS(clrad1-trtscent);      // 0.79.a
                                      end;

                              if rmin_mm>ABS(clrad1) then rmin_mm:=ABS(clrad1);


                            end
                       else begin      // transition curve.
                              if half_diamond=False
                                 then begin
                                        docurving(False,True,0,g/2,temp1,temp2,temp3,trans_0rad);        // get curving radius at Ctrl-0 datum.
                                        docurving(False,True,xorg,g/2,temp1,temp2,temp3,trans_orgrad);   // get curving radius at toe joint.
                                        docurving(False,True,toex,g/2,temp1,temp2,temp3,trans_toerad);   // get curving radius at toe.
                                        docurving(False,True,heelx,g/2,temp1,temp2,temp3,trans_heelrad); // get curving radius at switch heel.
                                        docurving(False,True,dpx,g/2,temp1,temp2,temp3,trans_dprad);     // get curving radius at deflection point.
                                        docurving(False,True,fpx,g/2,temp1,temp2,temp3,trans_fprad);     // get curving radius at fine point.
                                        docurving(False,True,mvjpx,g/2,temp1,temp2,temp3,trans_vjrad);   // get curving radius at vee joint.
                                        docurving(False,True,turnoutx,g/2,temp1,temp2,temp3,trans_9rad); // get curving radius at Ctrl-9 rail end.

                                        rcurv_toe:=curved_onto_calc(equiv_rad-g/2,trans_toerad);    // calc approx curved radii...
                                        rcurv_heel:=curved_onto_calc(tradius-g/2,trans_heelrad);    //^^^
                                        if dpx>heelx then rcurv_dp:=curved_onto_calc(tradius-g/2,trans_dprad)  //^^^
                                                     else rcurv_dp:=curved_onto_calc(equiv_rad-g/2,trans_dprad);

                                        if xing_calc_i<>0  // turnout - curved or generic crossing..
                                           then begin
                                                  trans_txrad:=nomrad1;    // keep compiler happy.
                                                  rcurv_tx:=nomrad1;       // keep compiler happy.

                                                  rcurv_fp:=curved_onto_calc(tradius-g/2,trans_fprad);   //^^^
                                                  rcurv_vj:=curved_onto_calc(tradius-g/2,trans_vjrad);   //^^^
                                                end
                                           else begin     // regular crossing...
                                                  docurving(False,True,tx,g/2,temp1,temp2,temp3,trans_txrad);      // get curving radius at curve end point.
                                                  rcurv_tx:=curved_onto_calc(tradius-g/2,trans_txrad);   //^^^
                                                  rcurv_fp:=trans_fprad;
                                                  rcurv_vj:=trans_vjrad;
                                                end;

                                        Add(hand_str+' transition curve :');
                                        Add('main-road centre-line radius at datum (CTRL-0) = '+rad_str(trans_0rad,2));
                                        Add('main-road centre-line radius at switch front (CTRL-1) = '+rad_str(trans_orgrad,2));
                                        Add('');
                                        Add('main-road centre-line radius at toe (CTRL-2) = '+rad_str(trans_toerad,2));
                                        Add('resultant turnout-road radius at toe (CTRL-2) = '+rad_str(rcurv_toe,0)+' approx.');
                                        Add('');
                                        Add('main-road centre-line radius at switch heel = '+rad_str(trans_heelrad,2));
                                        Add('resultant turnout-road radius at switch heel = '+rad_str(rcurv_heel,0)+' approx.');
                                        Add('');
                                        Add('main-road centre-line radius at deflection point (CTRL-3) = '+rad_str(trans_dprad,2));
                                        Add('resultant turnout-road radius at deflection point (CTRL-3) = '+rad_str(rcurv_dp,0)+' approx.');
                                        Add('');

                                        if xing_calc_i=0  // regular crossing..
                                           then begin
                                                  Add('main-road centre-line radius at end of turnout curve (CESP) = '+rad_str(trans_txrad,2));
                                                  Add('resultant turnout-road radius at end of turnout curve (CESP) = '+rad_str(rcurv_tx,0)+' approx.');
                                                  Add('');
                                                end;

                                        Add('main-road centre-line radius at fine point (CTRL-4) = '+rad_str(trans_fprad,2));
                                        Add('resultant turnout-road radius at fine point (CTRL-4) = '+rad_str(rcurv_fp,0)+' approx.');
                                        Add('');
                                        Add('main-road centre-line radius at vee joint (CTRL-8) = '+rad_str(trans_vjrad,2));
                                        Add('resultant turnout-road radius at vee joint (CTRL-6) = '+rad_str(rcurv_vj,0)+' approx.');
                                        Add('');
                                        Add('main-road centre-line radius at full length (CTRL-9) = '+rad_str(trans_9rad,2));


                                        if spiral=False    // bug fix mods 214a ...  // don't include radius at TOE or HEEL if a short transition is near (confusing result, radius shown may apply to only a few mm)
                                           then begin
                                                  if rmin_mm>ABS(rcurv_toe) then rmin_mm:=ABS(rcurv_toe);
                                                  if rmin_mm>ABS(rcurv_heel) then rmin_mm:=ABS(rcurv_heel);
                                                end
                                           else begin
                                                  if  (os<(toex-scale*3)) or (os>(toex+scale*3))                // 3ft arbitrary closeness of a zone end
                                                  or ((os+tst)<(toex-scale*3)) or ((os+tst)>(toex+scale*3))
                                                     then if rmin_mm>ABS(rcurv_toe) then rmin_mm:=ABS(rcurv_toe);

                                                  if  (os<(heelx-scale*3)) or (os>(heelx+scale*3))              // 3ft arbitrary closeness of a zone end
                                                  or ((os+tst)<(heelx-scale*3)) or ((os+tst)>(heelx+scale*3))
                                                     then if rmin_mm>ABS(rcurv_heel) then rmin_mm:=ABS(rcurv_heel);
                                                end;


                                        if rmin_mm>ABS(rcurv_dp) then rmin_mm:=ABS(rcurv_dp);

                                        if (spiral=False) or (xing_calc_i=1)       // bug fix mods 214a ...  // don't include radius at CESP or FP if not curviform and a short transition is near (confusing result, radius shown may apply to only a few mm)
                                           then begin
                                                  if (xing_calc_i=0) and (rmin_mm>ABS(rcurv_tx)) then rmin_mm:=ABS(rcurv_tx);   // CESP regular crossing
                                                  if rmin_mm>ABS(rcurv_fp) then rmin_mm:=ABS(rcurv_fp);
                                                end
                                           else begin
                                                  if  (os<(tx-scale*3)) or (os>(fpx+scale*3))              // 3ft arbitrary closeness of a zone end
                                                  or ((os+tst)<(tx-scale*3)) or ((os+tst)>(fpx+scale*3))
                                                     then begin
                                                            if (xing_calc_i=0) and (rmin_mm>ABS(rcurv_tx)) then rmin_mm:=ABS(rcurv_tx);   // CESP regular crossing
                                                            if rmin_mm>ABS(rcurv_fp) then rmin_mm:=ABS(rcurv_fp);
                                                          end;
                                                end;

                                        if rmin_mm>ABS(rcurv_vj) then rmin_mm:=ABS(rcurv_vj);

                                        if rmin_mm>ABS(trans_0rad) then rmin_mm:=ABS(trans_0rad);
                                        if rmin_mm>ABS(trans_orgrad) then rmin_mm:=ABS(trans_orgrad);
                                        if rmin_mm>ABS(trans_toerad) then rmin_mm:=ABS(trans_toerad);
                                        if rmin_mm>ABS(trans_heelrad) then rmin_mm:=ABS(trans_heelrad);
                                        if rmin_mm>ABS(trans_dprad) then rmin_mm:=ABS(trans_dprad);
                                        if (xing_calc_i=0) and (rmin_mm>ABS(trans_txrad)) then rmin_mm:=ABS(trans_txrad);
                                        if rmin_mm>ABS(trans_fprad) then rmin_mm:=ABS(trans_fprad);
                                        if rmin_mm>ABS(trans_vjrad) then rmin_mm:=ABS(trans_vjrad);
                                        if rmin_mm>ABS(trans_9rad) then rmin_mm:=ABS(trans_9rad);


                                      end
                                 else begin  // half-diamond...
                                        docurving(False,True,0,g/2,temp1,temp2,temp3,trans_0rad);        // get curving radius at datum.
                                        docurving(False,True,fpx,g/2,temp1,temp2,temp3,trans_fprad);     // get curving radius at fine point.
                                        docurving(False,True,mvjpx,g/2,temp1,temp2,temp3,trans_vjrad);   // get curving radius at vee joint.
                                        docurving(False,True,turnoutx,g/2,temp1,temp2,temp3,trans_9rad); // get curving radius at Ctrl-9 rail end.

                                        rcurv_0:=curved_onto_calc(tradius-g/2,trans_0rad);


                                        if xing_calc_i<>0 // half-diamond - curved or generic crossing (irregular)...
                                           then begin
                                                  trans_txrad:=nomrad1;    // keep compiler happy.
                                                  rcurv_tx:=nomrad1;       // keep compiler happy.

                                                  rcurv_fp:=curved_onto_calc(tradius-g/2,trans_fprad);   //^^^
                                                  rcurv_vj:=curved_onto_calc(tradius-g/2,trans_vjrad);   //^^^
                                                end
                                           else begin     // regular crossing    (irregular hd) ...
                                                  docurving(False,True,tx,g/2,temp1,temp2,temp3,trans_txrad);      // get curving radius at curve end point.
                                                  rcurv_tx:=curved_onto_calc(tradius-g/2,trans_txrad);   //^^^
                                                  rcurv_fp:=trans_fprad;
                                                  rcurv_vj:=trans_vjrad;
                                                end;


                                        Add(hand_str+' transition curve'+reg_hd_str+' :');
                                        Add('main-road centre-line radius at K-crossing (CTRL-0) = '+rad_str(trans_0rad,2));
                                        Add('resultant diagonal-road radius at K-crossing (CTRL-0) = '+rad_str(rcurv_0,0)+' approx.');
                                        Add('');

                                        if xing_calc_i=0  // regular crossing..
                                           then begin
                                                  Add('main-road centre-line radius at end of diamond curve (CESP) = '+rad_str(trans_txrad,2));
                                                  Add('resultant diagonal-road radius at end of diamond curve (CESP) = '+rad_str(rcurv_tx,0)+' approx.');
                                                  Add('');
                                                end;

                                        Add('main-road centre-line radius at fine point (CTRL-4) = '+rad_str(trans_fprad,2));
                                        Add('resultant diagonal-road radius at fine point (CTRL-4) = '+rad_str(rcurv_fp,0)+' approx.');
                                        Add('');
                                        Add('main-road centre-line radius at vee joint (CTRL-8) = '+rad_str(trans_vjrad,2));
                                        Add('resultant diagonal-road radius at vee joint (CTRL-6) = '+rad_str(rcurv_vj,0)+' approx.');
                                        Add('');
                                        Add('main-road centre-line radius at full length (CTRL-9) = '+rad_str(trans_9rad,2));

                                        if rmin_mm>ABS(rcurv_0) then rmin_mm:=ABS(rcurv_0);


                                        if (spiral=False) or (xing_calc_i=1)       // bug fix mods 214a ...  // don't include radius at CESP or FP if not curviform and a short transition is near (confusing result, radius shown may apply to only a few mm)
                                           then begin
                                                  if (xing_calc_i=0) and (rmin_mm>ABS(rcurv_tx)) then rmin_mm:=ABS(rcurv_tx);   // CESP regular crossing
                                                  if rmin_mm>ABS(rcurv_fp) then rmin_mm:=ABS(rcurv_fp);
                                                end
                                           else begin
                                                  if  (os<(tx-scale*3)) or (os>(fpx+scale*3))                // 3ft arbitrary closeness of a zone end
                                                  or ((os+tst)<(tx-scale*3)) or ((os+tst)>(fpx+scale*3))
                                                     then begin
                                                            if (xing_calc_i=0) and (rmin_mm>ABS(rcurv_tx)) then rmin_mm:=ABS(rcurv_tx);   // CESP regular crossing
                                                            if rmin_mm>ABS(rcurv_fp) then rmin_mm:=ABS(rcurv_fp);
                                                          end;
                                                end;


                                        if rmin_mm>ABS(rcurv_vj) then rmin_mm:=ABS(rcurv_vj);
                                        if rmin_mm>ABS(trans_0rad) then rmin_mm:=ABS(trans_0rad);
                                        if (xing_calc_i=0) and (rmin_mm>ABS(trans_txrad)) then rmin_mm:=ABS(trans_txrad);
                                        if rmin_mm>ABS(trans_fprad) then rmin_mm:=ABS(trans_fprad);
                                        if rmin_mm>ABS(trans_vjrad) then rmin_mm:=ABS(trans_vjrad);
                                        if rmin_mm>ABS(trans_9rad) then rmin_mm:=ABS(trans_9rad);
                                      end;


                              if retpar_i=1   // parallel crossing. 0.79.a  ...
                                 then begin
                                        Add('');

                                        docurving(False,True,retx,g/2,temp1,temp2,temp3,trans_retstartrad);   // get curving radius at start of return curve.
                                        docurving(False,True,mrpx,g/2,temp1,temp2,temp3,trans_retendrad);     // end of ditto.

                                        rcurv_retstart:=curved_onto_calc(retr,0-trans_retstartrad);    // calc approx return curve radii (opposite sense, so 0-)...
                                        rcurv_retend:=curved_onto_calc(retr,0-trans_retendrad);

                                        Add('main-road track centre-line radius at start of return curve = '+rad_str(trans_retstartrad,2));
                                        Add('resultant return curve radius at start of return curve = '+rad_str(rcurv_retstart,0)+' approx.');
                                        Add('');

                                        Add('main-road track centre-line radius at end of return curve = '+rad_str(trans_retendrad,2));
                                        Add('resultant return curve radius at end of return curve = '+rad_str(rcurv_retend,0)+' approx.');
                                        Add('loop radius at end of return curve = '+rad_str(trans_retendrad-trtscent,2));
                                        Add('loop radius at full length = '+rad_str(trans_9rad-trtscent,2));
                                        Add('');

                                        if rmin_mm>ABS(rcurv_retstart) then rmin_mm:=ABS(rcurv_retstart);
                                        if rmin_mm>ABS(rcurv_retend) then rmin_mm:=ABS(rcurv_retend);

                                        if rmin_mm>ABS(trans_retstartrad) then rmin_mm:=ABS(trans_retstartrad);
                                        if rmin_mm>ABS(trans_retendrad) then rmin_mm:=ABS(trans_retendrad);

                                        if rmin_mm>ABS(trans_retendrad-trtscent) then rmin_mm:=ABS(trans_retendrad-trtscent);  // loop radius at TRP.
                                        if rmin_mm>ABS(trans_9rad-trtscent) then rmin_mm:=ABS(trans_9rad-trtscent);  // ditto at far end.

                                      end;

                            end;

                  end
             else begin                       // not a curved template...
                    if plain_track=False
                       then begin                 // straight turnout..
                              if (half_diamond=False) and (rmin_mm>ABS(equiv_rad-g/2)) then rmin_mm:=ABS(equiv_rad-g/2);

                              if rmin_mm>ABS(tradius-g/2) then rmin_mm:=ABS(tradius-g/2);             //^^^


                              radius_for_obtain:=tradius-g/2;  // 205e

                            end
                       else begin                 // plain track...
                              //Add('------------');

                              Add('overall length = '+round_str(turnoutx,2));

                              if tb_roll_percent>minfp
                                 then begin
                                        tb_roll_mm:=tb_roll_percent*railen[pt_i]*inscale/100;  // timber rolling.
                                        tb_rolling:=True;
                                        tb_roll_str:='rolled-out length from rail joint';
                                      end
                                 else begin
                                        tb_roll_mm:=0;
                                        tb_rolling:=False;
                                        tb_roll_str:='overall length';
                                      end;

                              if  (rails_sleepers(railen[pt_i]*inscale-0.01,dummy_i,num_sl_full,dummy1)=True)  // get full length sleeper count (0.01mm arbitrary).
                              and (rails_sleepers(turnoutx-tb_roll_mm,num_rails,num_sl_over,percent_over)=True)
                                  then begin
                                         Add(tb_roll_str+' = '+IntToStr(num_rails)+' full rail lengths + '+IntToStr(num_sl_over)+' sleepers in '+round_str(percent_over,2)+' % of a rail length');
                                         if percent_over>minfp
                                            then Add('(to the next rail joint from CTRL-0 requires '+IntToStr(num_sl_full-num_sl_over)+' sleepers in '+round_str(100-percent_over,2)+' % of a rail length)');

                                         if tb_rolling=True
                                            then begin
                                                   if rails_sleepers(tb_roll_mm-0.0001,dummy_i,num_sl_over,percent_over)=True   //-0.0001 mm arbitrary to ensure shows 100% of a rail.
                                                      then begin
                                                             Add('');
                                                             Add('rolled-in length to rail joint'+' = '+IntToStr(num_sl_over)+' sleepers in '+round_str(percent_over,2)+' % of a rail length');
                                                             if percent_over>minfp
                                                                then Add('(to the next rail joint from CTRL-1 requires '+IntToStr(num_sl_full-num_sl_over)+' sleepers in '+round_str(100-percent_over,2)+' % of a rail length)');
                                                           end;
                                                 end;

                                       end;
                              Add('');

                              if (ABS(nomrad)<max_rad_test) or (spiral=True)
                                 then begin
                                        if spiral=True              // transition plain track.
                                           then begin
                                                  docurving(False,True,0,g/2,temp1,temp2,temp3,trans_0rad);        // get curving radius at datum end.
                                                  docurving(False,True,xorg,g/2,temp1,temp2,temp3,trans_orgrad);   // get curving radius at joint end.
                                                  Add(hand_str+' transition curve :');
                                                  Add('track centre-line radius at datum end (CTRL-0) = '+rad_str(trans_0rad,2));
                                                  Add('track centre-line radius at joint end (CTRL-1) = '+rad_str(trans_orgrad,2));

                                                  if rmin_mm>ABS(trans_0rad) then rmin_mm:=ABS(trans_0rad);
                                                  if rmin_mm>ABS(trans_orgrad) then rmin_mm:=ABS(trans_orgrad);
                                                end
                                           else begin
                                                  Add(hand_str+' constant curve : track centre-line radius = '+rad_str(clrad1,2));
                                                  rmin_mm:=ABS(clrad1);  // fixed radius plain track.
                                                end;
                                      end
                                 else begin
                                        Add('straight plain track');
                                        rmin_mm:=max_rad;            // straight plain track.
                                      end;
                            end;//plain track
                  end;//not a curved template


          if slewing=True
             then begin

                    Add('------------');
                    Add('slewing data ( slew mode '+IntTostr(slew_mode)+' ) :');
                    Add('');
                    Add('slewing zone start = '+round_str(slew_s,2));
                    Add('slewing zone length = '+round_str(slew_l,2));
                    Add('amount of slew = '+round_str(slew,2));
                    Add('');

                    if slew_mode=1
                       then begin
                              if ABS(slew)>minfp then slew_rad:=2*SQR(slew_l)/slew/SQR(Pi)   // (sign of rad is for start end of slew, slew amount is +ve towards the hand).
                                                 else slew_rad:=max_rad;                     // shouldn't get here !!!

                              Add('nominal slewing radius (on straight track) = '+rad_str(ABS(slew_rad),2));

                              if (ABS(nomrad)<max_rad_test) or (spiral=True)
                                 then begin
                                        if spiral=False     // fixed radius...
                                           then begin
                                                  slew_minrad:=curved_onto_calc(slew_rad,clrad1);
                                                  Add('resultant slewing radius after curving (at start of slewing zone) = '+rad_str(slew_minrad,2)+' approx.');
                                                  if rmin_mm>ABS(slew_minrad) then rmin_mm:=ABS(slew_minrad);

                                                  slew_minrad:=curved_onto_calc((0-slew_rad),clrad1);
                                                  Add('resultant slewing radius after curving (at end of slewing zone) = '+rad_str(slew_minrad,2)+' approx.');
                                                  if rmin_mm>ABS(slew_minrad) then rmin_mm:=ABS(slew_minrad);

                                                end
                                           else begin       // transition...

                                                  docurving(False,True,slew_s,g/2,temp1,temp2,temp3,trans_slewrad);            // get curving radius slew start.
                                                  slew_minrad:=curved_onto_calc(slew_rad,trans_slewrad);
                                                  Add('resultant slewing radius after curving (at start of slewing zone) = '+rad_str(slew_minrad,2)+' approx.');
                                                  if rmin_mm>ABS(slew_minrad) then rmin_mm:=ABS(slew_minrad);

                                                  docurving(False,True,(slew_s+slew_l),g/2,temp1,temp2,temp3,trans_slewrad);   // get curving radius at slew end.
                                                  slew_minrad:=curved_onto_calc((0-slew_rad),trans_slewrad);
                                                  Add('resultant slewing radius after curving (at end of slewing zone) = '+rad_str(slew_minrad,2)+' approx.');
                                                  if rmin_mm>ABS(slew_minrad) then rmin_mm:=ABS(slew_minrad);
                                                end;
                                      end;
                            end
                       else begin
                              Add('slewing radii not available ( slew mode 2 )');
                              Add('mode 2 slewing factor = '+round_str(slew2_kmax*50,2));
                            end;
                  end;

          if (trtscent<>0) or (trmscent<>0)
             then begin
                    Add('------------');
                    Add('adjacent track centres main side = '+round_str(trmscent,2));
                    Add('adjacent track centres turnout side = '+round_str(trtscent,2));
                    if plain_track=False
                       then begin
                              Add('angle at TXP crossover mid-point (CTRL-5) = '+round_str(txpk*180/Pi,2)+' degrees'+k_ram_str(txpk));
                              Add('angle at TVJP turnout road vee joint (CTRL-6) = '+round_str(tvjpk*180/Pi,2)+' degrees'+k_ram_str(tvjpk));
                            end;
                  end;


          if (retpar_i=1) and (plain_track=False) then Add('turnout road centres for parallel crossing = '+round_str(trtscent,2));

          if plain_track=False then Add('------------');

          if ((ABS(nomrad)<max_rad_test) or (spiral=True)) and (plain_track=False)
                then begin
                       Add('equivalent straight template dimensions BEFORE curving :');
                       Add('');
                       indent_str:='        ';
                     end
                else indent_str:='';

          if plain_track=False
             then begin
                   Add(indent_str+'overall length = '+round_str(turnoutx,2));

                   if half_diamond=False
                      then begin
                             Add(indent_str+'approach/exit track in'+plain_track_form.plain_track_spacings_listbox.Items.Strings[pt_i]+' ( rail length = '+round_str(railen[pt_i]*inscale,2)+' ):');
                             if xorg>0
                                then begin
                                       if rails_sleepers(xorg,num_rails,num_sl_over,percent_over)=True
                                          then Add(indent_str+'approach track length = '+round_str(xorg,2)+'  ( '+IntToStr(num_rails)+' full rail lengths + '+IntToStr(num_sl_over)+' sleepers in '+round_str(percent_over,2)+' % of a rail length )');
                                     end
                                else Add(indent_str+'approach track length = 0');
                           end
                      else Add(indent_str+'exit track in'+plain_track_form.plain_track_spacings_listbox.Items.Strings[pt_i]+' ( rail length = '+round_str(railen[pt_i]*inscale,2)+' ):');

                   if turnoutx>(mvjpx+minfp)   // exit length
                      then begin
                             if rails_sleepers(turnoutx-mvjpx,num_rails,num_sl_over,percent_over)=True
                                then Add(indent_str+'exit track length = '+round_str(turnoutx-mvjpx,2)+'  ( '+IntToStr(num_rails)+' full rail lengths + '+IntToStr(num_sl_over)+' sleepers in '+round_str(percent_over,2)+' % of a rail length )');
                           end
                      else Add(indent_str+'exit track length = 0');

                   if half_diamond=False
                      then begin
                             Add('');
                             Add(indent_str+'turnout-road centre-line radius (at turnout-curve) = '+rad_str(tradius-g/2,2));

                             Add('');
                             if swrad>=max_rad_test then Add(indent_str+'nominal switch-curve radius (rail gauge-face) = '+rad_str(equiv_rad,2)+'  (straight switch)')
                                                    else Add(indent_str+'switch-curve radius (rail gauge-face) = '+rad_str(swrad,2));
                             Add(indent_str+'turnout-curve radius (rail gauge-face) = '+rad_str(tradius,2));

                             Add(indent_str+'switch-curve radial centre: X = '+round_str(sworgx,2)+'  Y = '+round_str(sworgy-g/2,2)+'  (from CTRL-0)');
                             Add(indent_str+'turnout-curve radial centre: X = '+round_str(torgx,2)+'  Y = '+round_str(torgy-g/2,2)+'  (from CTRL-0)');

                           end
                      else begin
                             Add('');
                             if tradius_is_straight=True
                                then Add(indent_str+'diagonal-road centre-line radius = straight'+reg_hd_str)
                                else begin
                                       Add(indent_str+'diagonal-road centre-line radius = '+rad_str(tradius-g/2,2)+reg_hd_str);

                                       Add('');
                                       Add(indent_str+'diagonal-curve radius (rail gauge-face) = '+rad_str(tradius,2));
                                       Add(indent_str+'diagonal-curve radial centre: X = '+round_str(torgx,2)+'  Y = '+round_str(torgy-g/2,2)+'  (from CTRL-0)');
                                     end;
                           end;

                   if xing_calc_i<>1   // straight or generic crossing..
                      then begin
                             Add(indent_str+'V-crossing entry-straight (curve-end to fine-point) = '+round_str(sl,2));
                             if retpar_i=1 then Add(indent_str+'return-curve radius (track centre) = '+rad_str(retr,2));
                           end;
                   Add('');
                   if half_diamond=False
                      then begin
                             Add(indent_str+'switch front (rail-joint to switch-toe) = '+round_str(toex-xorg,2));
                             Add(indent_str+'virtual lead (switch-toe to fine-point) = '+round_str(fpx-toex,2));
                             Add(indent_str+'actual lead (switch-toe to blunt nose) = '+round_str(bnx-toex,2));
                           end
                      else begin
                             Add(indent_str+'half-diamond main-road inter-length (CTRL-0 to MCP) = '+round_str(fpx,2));
                             Add(indent_str+'half-diamond virtual lead (K-crossing to fine-point) = '+round_str(fpx-setx,2));
                             Add(indent_str+'half-diamond actual lead (K-crossing to blunt nose) = '+round_str(bnx-setx,2));
                           end;

                   Add('');
                   case knuckle_code of
                     -1: Add(indent_str+'knuckle bend = sharp');
                      0: Add(indent_str+'knuckle bend radius (normal) = '+round_str(knuck_rad,2));
                      1: Add(indent_str+'knuckle bend radius (custom) = '+round_str(knuck_rad,2));
                   end;//case

                   Add(indent_str+'blunt nose to timber A = '+round_str(bn_to_a*inscale,2));
                   Add(indent_str+'width of blunt nose = '+round_str(bn_wide*inscale,2));
                   Add('');
                   Add(indent_str+'wing rail reach length (main-side) = '+round_str(wgl_ms,2));
                   Add(indent_str+'wing rail reach length (turnout-side) = '+round_str(wgl_ts,2));
                   Add(indent_str+'check rail overall length (main-side) = '+round_str(ckl_ms,2));
                   Add(indent_str+'check rail overall length (turnout-side) = '+round_str(ckl_ts,2));
                  end;

          if spiral=True
             then begin
                    Add('------------');
                    Add('transition curve data :');
                    Add('');
                    Add('initial radius at the track centre-line = '+rad_str(clrad1,2));
                    Add('final radius at the track centre-line = '+rad_str(clrad2,2));
                    Add('length along the initial radius = '+round_str(os,2)+' (at the track centre-line)');
                    Add('length along transition section = '+round_str(tst,2)+' (at the track centre-line)');
                    Add('spiral constant = '+round_str(trans_k/1.0E6,4)+' (at the track centre-line)');
                 end;
        end;//with info_text_list

        smallest_radius:=ABS(rmin_mm); // 208a  goes into file for displaying box data

        with info_form do begin
            limit_rad_label.Caption:=round_str(min_radius,0)+' mm';  // in case it is default.

            if rounding_checkbox.Checked=True       // 228b
               then num_dec:=0
               else num_dec:=2;                    // 228b was 1

            rmin_inch:=ABS(rmin_mm/25.4);

            // out 228b if rmin_inch>100 then num_dec:=0;       //  don't show fractional inches over 100".

            if ABS(rmin_mm)>=max_rad_test then min_rad_now_label.Caption:='straight'
                                          else min_rad_now_label.Caption:=round_str(rmin_mm,num_dec)+' mm  ( '+round_str(rmin_inch,num_dec)+'" )';

            if ABS(rmin_mm)<(ABS(min_radius)-0.5)       //  set the minimum radius warning lamp.  // 0.98.b  -0.5 to prevent display anomolies
               then begin
                      min_rad_warn:=1;
                      min_rad_now_label.Font.Color:=clRed;
                      min_rad_lamp_panel.Color:=clRed;
                    end
               else begin
                      min_rad_warn:=0;
                      min_rad_now_label.Font.Color:=clBlue;
                      min_rad_lamp_panel.Color:=clLime;
                    end;
        end;//with info_form

        with info_text_list do begin
          Add('------------');

          if (slewing=True) and (plain_track=False)
             then no_zone_str:=' (except within the slewing zone)'
             else no_zone_str:='';

          if ABS(rmin_mm)<(max_rad_test-2)   // 212a -2 kludge for make transition
             then Add('smallest radius on this template'+no_zone_str+' = '+round_str(rmin_mm,2)+' mm  ( '+round_str(rmin_inch,2)+'" )')
             else Add('smallest radius on this template'+no_zone_str+' = straight');

          docurving(True,True,       0,g/2,temp1,temp2,swing_0,temp3);    // calc the total swing...
          docurving(True,True,turnoutx,g/2,temp1,temp2,swing_9,temp3);
          total_swing:=swing_9-swing_0;

          if plain_track=True then temp_str:=''
                              else temp_str:=' (in main road)';

          Add('total angular swing on this template = '+round_str(total_swing*180/Pi,2)+' degrees'+k_ram_str(total_swing)+temp_str);
          Add('------------');
          Add('nominal gauge :  '+gauge_form.gauge_listbox.Items.Strings[gauge_i]);

          Add('------------');
          Add('template location on trackpad :');
          Add('');
          Add('rotation :  X = '+round_str(xform,2)+'   Y = '+round_str(yform*hand_i+y_datum,2)+'   K = '+round_str(kform*hand_i*180/Pi,2)+' degrees'+k_ram_str(kform*hand_i));
          Add('   shift :  X = '+round_str(xshift,2)+'   Y = '+round_str(yshift*hand_i,2));
          Add('rail-end :  X = '+round_str(datumx_on_pad,2)+'   Y = '+round_str(datumy_on_pad*hand_i+y_datum,2));

          Add('');
          Add('peg from origin :  X = '+round_str(pegx_on_pad,2)+       '   Y = '+round_str(pegy_on_pad*hand_i+y_datum,2)+         '   K = '+round_str(arm_angle*hand_i*180/Pi,2)+' degrees'+k_ram_str(arm_angle*hand_i));
          Add('peg from notch :  X = '+round_str(pegx_on_pad-notchx,2)+'   Y = '+round_str((pegy_on_pad*hand_i+y_datum)-notchy,2)+'   K = '+round_str(((arm_angle*hand_i)-notch_angle)*180/Pi,2)+' degrees'+k_ram_str((arm_angle*hand_i)-notch_angle));

          Add('');

          pin.x:=xt1;
          pin.y:=yt1;
          dotransform(kform,xform,yform,pin,pout);

          rad1_orgx:=pout.x+xshift;
          rad1_orgy:=(pout.y+yshift)*hand_i+y_datum;

          if spiral=True
             then begin
                    pin.x:=xt2;
                    pin.y:=yt2;
                    dotransform(kform,xform,yform,pin,pout);

                    rad2_orgx:=pout.x+xshift;
                    rad2_orgy:=(pout.y+yshift)*hand_i+y_datum;
                  end;

                        // add radial centres to info...

          if spiral=False   // fixed rad.
             then begin
                    if ABS(nomrad)<max_rad_test then Add('radial centre :  X = '+round_str(rad1_orgx,2)+'   Y = '+round_str(rad1_orgy,2));
                  end
             else begin

                    if ABS(nomrad1)<max_rad_test  // transition.
                       then Add('radial centre (1st rad) :  X = '+round_str(rad1_orgx,2)+'   Y = '+round_str(rad1_orgy,2))
                       else Add('radial centre (1st rad) = straight');

                    if ABS(nomrad2)<max_rad_test
                       then Add('radial centre (2nd rad) :  X = '+round_str(rad2_orgx,2)+'   Y = '+round_str(rad2_orgy,2))
                       else Add('radial centre (2nd rad) = straight');
                  end;

          Add('');
          if ABS(pegy-g/2)<minfp  // (pegy==g/2) peg on main road?
             then begin
                    temp1:=clrad_at_x(pegx);
                    if ABS(temp1)>max_rad_test
                       then Add('track centre-line radius at peg = straight')          // added 212a
                       else Add('track centre-line radius at peg = '+rad_str(temp1,2)+'  ( '+rad_str(temp1/25.4,2)+' " )');
                  end
             else Add('track centre-line radius at peg = n/a (peg off MS centre-line)');

          if (half_diamond=False) and (spiral=False) and (slewing=False) and (plain_track=False)  // add geometrical radius info.
             then begin
                    Add('internal geometrical radius = '+rad_str(igeo_rad,2)+'  ( '+rad_str(igeo_rad/25.4,2)+' " )');
                    Add('external geometrical radius (substitution radius) = '+rad_str(egeo_rad,2)+'  ( '+rad_str(egeo_rad/25.4,2)+' " )');
                  end;

        Add('------------');

        Add('total length of pointwork timbering = '+round_str(total_template_timber_length,2)+' mm   ( '+IntToStr(template_number_of_timbers)+' timbers )');         // 226a
        Add('total length of plain track sleepering = '+round_str(total_template_sleeper_length,2)+' mm   ( '+IntToStr(template_number_of_sleepers)+' sleepers )');   // 226a

        Add('------------');

        Add('experimental chairing: total number of chairs on this template = '+IntToStr(chair_count)+' chairs');     // 233d

        Add('');

        Add('------------');

        Add('');

      end;//with info_text_list

    finally
                    // show the text..

      if pad_form.Active=True
         then begin
                with info_form do begin
                  info_memo.Text:=info_text_list.Text;  // update visible control template data.
                  ref_name_label.Visible:=True;
                  rename_button.Visible:=True;
                  ref_label.Visible:=True;
                end;//with

                if rail_options_form.Showing=True then update_rail_settings;  // 206b

                if gaps_form.Showing=True then update_symbols_dialog;   // 227a

              end
         else begin
                if keep_form.Active=True
                   then begin
                          with info_form do begin
                            info_memo.Clear; // data may not match captions and labels.
                            ref_name_label.Visible:=False;
                            rename_button.Visible:=False;
                            ref_label.Visible:=False;
                          end;//with
                        end;
              end;
    end;//try

end;
//______________________________________________________________________________

function curved_onto_calc(org_rad, onto_rad:extended):extended;

var                // return approx radius of org_rad when curved onto onto_rad.
  temp:extended;

begin
  temp:=org_rad+onto_rad;
  if ABS(temp)>minfp                     // inside curve, or rads differ.
     then RESULT:=org_rad*onto_rad/temp  // no zero division.
     else RESULT:=max_rad;               // return effective infinity turnout road radius (straight).
end;
//______________________________________________________________________________

function equiv_rad_calc(curved_rad,onto_rad:extended):extended;    // 217a

     // return approx orginal rad before being curved onto onto_rad to become curved_rad

var
  temp:extended;

begin
  temp:=onto_rad-curved_rad;
  if ABS(temp)>minfp
     then RESULT:=curved_rad*onto_rad/temp     // no zero division.
     else RESULT:=max_rad;                     // return effective infinity original (straight) - curved_rad and onto_rad equal
end;
//______________________________________________________________________________

procedure init_resize;          // set up for any re-sizing - before calling calcturnout.

var
  sz:integer;

begin

  switch_modify_mode:=False;    // init flag to default - so can't change the switch to match the crossing.
                                // either he has locked it,
                                // or switch adjusts are not in force,
                                // or the switch is being set by the mouse moves directly,

  if ( ( (mouse_modify=1) or (mouse_modify=2) ) and (switch_free=True) ) or (generate_nearest=True)
     then begin
            if (xing_free=True) or (generate_nearest=True)
               then begin     // adjust turnout in force - and can change both switch and crossing angle as required.
                              // So set a starting switch for the calcs. Mouse moves will change the crossing angle,
                              // and a matching switch can then be set.

                      if csi.group_code=1  // 6 sizes, straight switch...
                         then begin
                                sz:=1;                      // start with 9ft (shortest) straight switch.
                                if k3n>5.875 then sz:=2;    // or start with 12ft switch for 1:6 or more.
                                if k3n>7.875 then sz:=3;    // or start with 15ft switch for 1:8 or more.
                                if k3n>9.875 then sz:=4;    // or start with 18ft straight switch for 1:10 or more.
                                if k3n>11.875 then sz:=5;   // or start with 24ft straight switch for 1:12 or more.
                                if k3n>13.875 then sz:=6;   // or start with 30ft straight switch for 1:14 or more.

                                if set_csi_data(csi.group_code,sz)=False // get switch data from listbox entries to switch[] array for control template.
                                   then run_error(81);

                                switch_modify_mode:=True;   // and flag that it can be changed if nec to suit crossing.
                              end;

                      if csi.group_code=7   // 3 sizes, model switch...
                         then begin
                                sz:=1;                      // start with 1:24 (shortest) straight switch.
                                if k3n>5.875 then sz:=2;    // or start with 1:32 switch for 1:6 or more.
                                if k3n>7.875 then sz:=3;    // or start with 1:40 switch for 1:8 or more.

                                if set_csi_data(csi.group_code,sz)=False // get switch data from listbox entries to switch[] array for control template.
                                   then run_error(81);

                                switch_modify_mode:=True;   // and flag that it can be changed if nec to suit crossing.
                              end;

                      if (csi.group_code=2) or (csi.group_code=5) or (csi.group_code=6)    // 6 sizes, REA semi-curved switch or FB switch...
                         then begin
                                sz:=1;                     // start with A semi-curved switch if 1:5.75 or less. (A-7 is natural)
                                if k3n>5.875 then sz:=2;   // or start with B switch for 1:6 or more (B-8 is natural)
                                if k3n>8.125 then sz:=3;   // or start with C switch for 1:8.25 or more (C-10 is natural)
                                if k3n>10.125 then sz:=4;  // or start with D switch for 1:10.25 or more (D-12 is natural)
                                if k3n>12.125 then sz:=5;  // or start with E switch for 1:12.25 or more (E-16 is natural)
                                if k3n>16.125 then sz:=6;  // or start with F switch for 1:16.25 or more (F-20 is natural).

                                if set_csi_data(csi.group_code,sz)=False // get switch data from listbox entries to switch[] array for control template.
                                   then run_error(81);

                                switch_modify_mode:=True;   // and flag that it can be changed if nec to suit crossing.
                              end;

                      if csi.group_code=3    // 4 sizes, GWR curved switch + 30ft straight...
                         then begin
                                sz:=1;                     // start with B curved switch up to 1:8
                                if k3n>8.125 then sz:=2;   // or start with C switch for 1:8.25 to 1:10
                                if k3n>10.125 then sz:=3;  // or start with D switch for 1:10.25 and above.
                                if k3n>13.875 then sz:=4;  // or start with 30ft switch for 1:14 and above.

                                if set_csi_data(csi.group_code,sz)=False // get switch data from listbox entries to switch[] array for control template.
                                   then run_error(81);

                                switch_modify_mode:=True;   // and flag that it can be changed if nec to suit crossing.
                              end;

                      if csi.group_code=4    // 8 sizes, GWR old-type heel switch...
                         then begin
                                sz:=1;                      // start with 9ft if less than 1:5.
                                if k3n>4.875 then sz:=2;    // or start with 10ft switch for 1:5 or more.
                                if k3n>5.875 then sz:=3;    // or start with 12ft switch for 1:6 or more.
                                if k3n>7.875 then sz:=4;    // or start with 14ft switch for 1:8 or more.
                                if k3n>8.875 then sz:=5;    // or start with 15ft switch for 1:9 or more.
                                if k3n>9.875 then sz:=6;    // or start with 16ft switch for 1:10 or more.
                                if k3n>10.875 then sz:=7;   // or start with 18ft straight switch for 1:11 or more.
                                if k3n>11.875 then sz:=8;   // or start with 20ft straight switch for 1:12 or more.

                                if set_csi_data(csi.group_code,sz)=False // get switch data from listbox entries to switch[] array for control template.
                                   then run_error(81);

                                switch_modify_mode:=True;   // and flag that it can be changed if nec to suit crossing.
                              end;


                    end
               else begin     // adjust turnout in force - but the crossing is locked.
                              // So mouse moves change the switch directly...
                              // (switch_only set by mouse move in range 9-21).

                      if (csi.group_code=1) or (csi.group_code=2) or (csi.group_code=5) or (csi.group_code=6) // 6 sizes, straight switch or REA semi-curved or FB switch.
                         then begin
                                sz:=1;                 // keep compiler happy.
                                case switch_only of
                                          0..10: sz:=1;
                                             12: sz:=2;
                                             14: sz:=3;
                                             16: sz:=4;
                                             18: sz:=5;
                                         20..25: sz:=6;
                                end;//case

                                if set_csi_data(csi.group_code,sz)=False // get switch data from listbox entries to switch[] array for control template.
                                   then run_error(80);
                              end;

                      if csi.group_code=7  // 3 sizes, model switch...
                         then begin
                                sz:=1;                 // keep compiler happy.
                                case switch_only of
                                          0..10: sz:=1;
                                             12: sz:=2;
                                         14..25: sz:=3;
                                end;//case

                                if set_csi_data(csi.group_code,sz)=False // get switch data from listbox entries to switch[] array for control template.
                                   then run_error(80);
                              end;

                      if csi.group_code=3    // 4 sizes, GWR curved switch + 30ft straight...
                         then begin
                                sz:=1;                 // keep compiler happy.
                                case switch_only of
                                          0..10: sz:=1;
                                             13: sz:=2;
                                             16: sz:=3;
                                         19..25: sz:=4;
                                end;//case

                                if set_csi_data(csi.group_code,sz)=False // get switch data from listbox entries to switch[] array for control template.
                                   then run_error(78);
                              end;

                      if csi.group_code=4       // 8 sizes, GWR heel switch...
                         then begin
                                sz:=1;                 // keep compiler happy.
                                case switch_only of
                                          0..11: sz:=1;
                                             12: sz:=2;
                                             13: sz:=3;
                                             14: sz:=4;
                                             15: sz:=5;
                                             16: sz:=6;
                                             17: sz:=7;
                                         18..25: sz:=8;
                                end;//case

                                if set_csi_data(csi.group_code,sz)=False // get switch data from listbox entries to switch[] array for control template.
                                   then run_error(79);
                              end;
                    end;
	  end;                                     // ready to calc turnout.

end;
//_________________________________________________________________________________________

procedure calc_fill_symbols;    //  227a calc the droppers and gaps

var
  n,m:integer;
  dir:integer;     // +/-1

  railx,raily,symbolx,symboly,k,sink,cosk:extended;

  to_symbol,to_flagpole,to_id,id_ht:extended;

  ax,ay,droprad,flagpolex,flagpoley,idx,idy,tuf:extended;

  hg:extended;  // half rail gap

  pex:Tpex;

begin
  if Length(current_symbols)<1 then EXIT;

  for n:=0 to Length(current_symbols)-1 do begin

    with current_symbols[n] do begin

      with symbol_data do begin

        if symb_check_rail=True     // on check rail
           then begin
                  dir:=-1;
                  to_symbol:=fw+j/2;    // to middle of check rail
                end
           else begin
                  dir:=1;
                  to_symbol:=j/2;       // to middle of running rail
                end;

        case symb_size of     // trial and error...

          0: begin                   // small
               droprad:=4*inscale;                       // radius of dropper flag
               to_flagpole:=to_symbol+16*inscale;        // end of flagpole
               to_id:=to_flagpole+8.5*inscale;           // centre of ID text
               id_ht:=5*inscale;                         // ID font height
               tuf:=1.5*inscale;                         // gap tuf thickness
             end;

          1: begin                   // medium
               droprad:=6*inscale;
               to_flagpole:=to_symbol+20*inscale;
               to_id:=to_flagpole+12.5*inscale;
               id_ht:=7.5*inscale;
               tuf:=2.25*inscale;                        // gap tuf thickness
             end;

          2: begin                      // large
               droprad:=8*inscale;
               to_flagpole:=to_symbol+26*inscale;
               to_id:=to_flagpole+16.5*inscale;
               id_ht:=10*inscale;
               tuf:=3*inscale;                           // gap tuf thickness
             end;

          3: begin                      // giant
               droprad:=10*inscale;
               to_flagpole:=to_symbol+34*inscale;
               to_id:=to_flagpole+20.5*inscale;
               id_ht:=12.5*inscale;
               tuf:=4*inscale;                          // gap tuf thickness
             end;

        else begin                   // small
               droprad:=4*inscale;                       // radius of dropper flag
               to_flagpole:=to_symbol+16*inscale;        // end of flagpole
               to_id:=to_flagpole+8.5*inscale;           // centre of ID text
               id_ht:=5*inscale;                         // ID font height
               tuf:=1.5*inscale;                         // gap tuf thickness
             end;

        end;//case

        if symb_type=2 then to_id:=to_id*3/4;  // sticker text can be closer

        railx:=symb_x+xorg;

        case symb_rail of

          1: begin
               symbolx:=railx;                // MS stock rail
               symboly:=0-dir*to_symbol;      // middle of rail
               k:=0;

               flagpolex:=railx;
               flagpoley:=0-dir*to_flagpole;

               idx:=railx;
               idy:=0-dir*to_id;
             end;

          2: begin                              // turnout-road crossing rail
               raily:=aq2offset(railx,k);
               symbolx:=railx+dir*to_symbol*SIN(k);       // middle of rail..
               symboly:=raily-dir*to_symbol*COS(k);

               flagpolex:=railx+dir*to_flagpole*SIN(k);
               flagpoley:=raily-dir*to_flagpole*COS(k);

               idx:=railx+dir*to_id*SIN(k);
               idy:=raily-dir*to_id*COS(k);
             end;

          3: begin                              // main-road crossing rail
               symbolx:=railx;                  // middle of rail..
               symboly:=g+dir*to_symbol;
               k:=0;

               flagpolex:=railx;
               flagpoley:=g+dir*to_flagpole;

               idx:=railx;
               idy:=g+dir*to_id;
             end;

          4: begin                                        // TS stock rail
               raily:=aq3offset(railx,k);
               symbolx:=railx-dir*to_symbol*SIN(k);      // middle of rail..
               symboly:=raily+dir*to_symbol*COS(k);

               flagpolex:=railx-dir*to_flagpole*SIN(k);
               flagpoley:=raily+dir*to_flagpole*COS(k);

               idx:=railx-dir*to_id*SIN(k);
               idy:=raily+dir*to_id*COS(k);
             end;

        else begin
               symbolx:=railx;                // MS stock rail
               symboly:=0-dir*to_symbol;     // middle of rail
               k:=0;

               flagpolex:=railx;
               flagpoley:=0-dir*to_flagpole;

               idx:=railx;
               idy:=0-dir*to_id;
             end;

        end;//case

        // put drawing data in record...

        with drawn_symbol do begin

          pex.x:=symbolx;
          pex.y:=symboly;
          symbol_point:=convert_point(curve_point(pex));    // to grid in 1/100ths mm

          pex.x:=flagpolex;
          pex.y:=flagpoley;
          end_point:=convert_point(curve_point(pex));    // to grid in 1/100ths mm

          pex.x:=idx;
          pex.y:=idy;
          id_point:=convert_point(curve_point(pex));    // to grid in 1/100ths mm

          pex.x:=id_ht;
          pex.y:=0;                         // not used
          id_height:=convert_point(pex);    // text height in 1/100ths mm

              // now calc the flag polygon...

          case symb_type of

               0: begin   // 24-sided dropper flag...

                    for m:=0 to 23 do begin

                      pex.x:=symbolx+droprad*SIN(m*2*Pi/24);
                      pex.y:=symboly+droprad*COS(m*2*Pi/24);

                      poly_points[m]:=convert_point(curve_point(pex));    // to grid in 1/100ths mm

                    end;//next

                    num_points:=24;
                  end;


               1: begin   // gap

                    sink:=SIN(k);
                    cosk:=COS(k);

                    hg:=inscale*3/4;  // half rail gap - 1.5" rail gap

                    ax:=symbolx-(j+inscale/2)*sink/2;     // rail edge at gap  (rail+1/2" width for symbol clearance)
                    ay:=symboly+(j+inscale/2)*cosk/2;

                    //-------

                    pex.x:=ax-hg*cosk;    // to tuf at rail edge
                    pex.y:=ay-hg*sink;

                    poly_points[0]:=convert_point(curve_point(pex));    // to grid in 1/100ths mm

                    //-------

                    pex.x:=pex.x-(2.5*tuf-hg)*cosk;  // along, corner of tuf at rail edge
                    pex.y:=pex.y-(2.5*tuf-hg)*sink;

                    poly_points[1]:=convert_point(curve_point(pex));    // to grid in 1/100ths mm

                    //-------

                    pex.x:=pex.x-tuf*sink;  // across, outer corner of tuf
                    pex.y:=pex.y+tuf*cosk;

                    poly_points[2]:=convert_point(curve_point(pex));    // to grid in 1/100ths mm

                    //-------

                    pex.x:=pex.x+5*tuf*cosk;  // along, outer corner of tuf
                    pex.y:=pex.y+5*tuf*sink;

                    poly_points[3]:=convert_point(curve_point(pex));    // to grid in 1/100ths mm

                    //-------

                    pex.x:=pex.x+tuf*sink;  // across, corner of tuf on rail edge
                    pex.y:=pex.y-tuf*cosk;

                    poly_points[4]:=convert_point(curve_point(pex));    // to grid in 1/100ths mm

                    //-------

                    pex.x:=pex.x-(2.5*tuf-hg)*cosk;  // along, rail edge at gap
                    pex.y:=pex.y-(2.5*tuf-hg)*sink;

                    poly_points[5]:=convert_point(curve_point(pex));    // to grid in 1/100ths mm

                    //-------

                    pex.x:=pex.x+(j+inscale/2)*sink;  // across, to opposite rail edge
                    pex.y:=pex.y-(j+inscale/2)*cosk;

                    poly_points[6]:=convert_point(curve_point(pex));    // to grid in 1/100ths mm

                    //-------

                    pex.x:=pex.x+(2.5*tuf-hg)*cosk;  // along, rail edge
                    pex.y:=pex.y+(2.5*tuf-hg)*sink;

                    poly_points[7]:=convert_point(curve_point(pex));    // to grid in 1/100ths mm

                    //-------

                    pex.x:=pex.x+tuf*sink;  // across, corner of tuf on rail edge
                    pex.y:=pex.y-tuf*cosk;

                    poly_points[8]:=convert_point(curve_point(pex));    // to grid in 1/100ths mm

                    //-------

                    pex.x:=pex.x-5*tuf*cosk;  // along, outer corner of tuf
                    pex.y:=pex.y-5*tuf*sink;

                    poly_points[9]:=convert_point(curve_point(pex));    // to grid in 1/100ths mm

                    //-------

                    pex.x:=pex.x-tuf*sink;  // across, corner of tuf on rail edge
                    pex.y:=pex.y+tuf*cosk;

                    poly_points[10]:=convert_point(curve_point(pex));    // to grid in 1/100ths mm

                    //-------

                    pex.x:=pex.x+(2.5*tuf-hg)*cosk;  // along, rail edge
                    pex.y:=pex.y+(2.5*tuf-hg)*sink;

                    poly_points[11]:=convert_point(curve_point(pex));    // to grid in 1/100ths mm

                    //-------

                    num_points:=12;

                  end;

             //2: sticker is boxed string only

          end;//case

        end;//with symbol_data
      end;//with drawn_symbol

    end;//with symbol

  end;//next symbol
end;
//______________________________________________________________________________

function draw_symbols_current(canv:TCanvas; symbol_type:integer):boolean;    // 227a

var
  i,n,dummy_i:integer;

  move_to,line_to:TPoint;

  check_intx,check_inty:extended;

  check_int1x,check_int1y,check_int2x,check_int2y:extended;

  pole_length:extended;

  infill_points:array[0..23] of TPoint;

  font_height,half_stringwidth,half_stringheight:integer;

  drawn_id_str:string;

                          //////////////////////////////////////////////////////

                          function set_highlit_colour:TColor;

                          begin
                            if paper_colour=clWhite
                               then RESULT:=clBlack
                               else RESULT:=clWhite;
                          end;
                          //////////////////////////////////////////////////////

begin
  RESULT:=False;  // default init.

  with canv do begin

    if Length(current_symbols)<1 then EXIT;

    for n:=0 to Length(current_symbols)-1 do begin

      with current_symbols[n] do begin

        with symbol_data do begin

          if symb_type<>symbol_type then CONTINUE;

          with drawn_symbol do begin

                    // for click detection...

            if symb_type<>2
               then begin
                      pad_symbol_point.X:=Round(symbol_point.X*sx+ex-gx);
                      pad_symbol_point.Y:=Round((symbol_point.Y+yd)*sy+by-gy);
                    end
               else begin     // sticker..
                      pad_symbol_point.X:=Round(id_point.X*sx+ex-gx);
                      pad_symbol_point.Y:=Round((id_point.Y+yd)*sy+by-gy);
                    end;

            pad_proximity:=Round(id_height.X*sx);  // arbitrary

            case symb_type of

               0: begin                  // it's a dropper...
                    Pen.Width:=1;
                    Pen.Style:=psSolid;
                    Pen.Mode:=pmCopy;
                    if n=highlit_symbol
                       then Pen.Color:=set_highlit_colour
                       else Pen.Color:=symb_colour;

                    Brush.Style:=bsSolid;
                    if n=highlit_symbol
                       then Brush.Color:=set_highlit_colour
                       else Brush.Color:=symb_colour;

                    for i:=0 to (num_points-1) do begin

                      check_intx:=limits(h_minint,h_maxint,poly_points[i].X*sx+ex-gx,dummy_i);
                      check_inty:=limits(h_minint,h_maxint,(poly_points[i].Y+yd)*sy+by-gy,dummy_i);

                      infill_points[i].X:=Round(check_intx);
                      infill_points[i].Y:=Round(check_inty);

                    end;//next

                    Polygon(Slice(infill_points,num_points));   // number of points, not index

                      // now the flagpole ...

                    check_int1x:=limits(h_minint,h_maxint,symbol_point.X*sx+ex-gx,dummy_i);
                    check_int1y:=limits(h_minint,h_maxint,(symbol_point.Y+yd)*sy+by-gy,dummy_i);

                    check_int2x:=limits(h_minint,h_maxint,end_point.X*sx+ex-gx,dummy_i);
                    check_int2y:=limits(h_minint,h_maxint,(end_point.Y+yd)*sy+by-gy,dummy_i);

                    move_to.X:=Round(check_int1x);  move_to.Y:=Round(check_int1y);
                    line_to.X:=Round(check_int2x);  line_to.Y:=Round(check_int2y);

                    pole_length:=SQRT(SQR(move_to.X-line_to.X)+SQR(move_to.Y-line_to.Y));    // in dots

                    Pen.Width:=Round(pole_length/5);  // 5 arbitrary

                    MoveTo(move_to.X, move_to.Y);
                    LineTo(line_to.X, line_to.Y);

                  end;

               1: begin   // gap H-section fishplate tuf

                    Pen.Width:=1;
                    Pen.Style:=psSolid;
                    Pen.Mode:=pmCopy;
                    if n=highlit_symbol
                       then Pen.Color:=set_highlit_colour
                       else Pen.Color:=symb_colour;

                    Brush.Style:=bsSolid;
                    if n=highlit_symbol
                       then Brush.Color:=set_highlit_colour
                       else Brush.Color:=symb_colour;

                    for i:=0 to (num_points-1) do begin

                      check_intx:=limits(h_minint,h_maxint,poly_points[i].X*sx+ex-gx,dummy_i);
                      check_inty:=limits(h_minint,h_maxint,(poly_points[i].Y+yd)*sy+by-gy,dummy_i);

                      infill_points[i].X:=Round(check_intx);
                      infill_points[i].Y:=Round(check_inty);

                    end;//next

                    Polygon(Slice(infill_points,num_points));   // number of points, not index


                  end;

              // 2: sticker - boxed string only

            end;//case

               // now the ID text...

            if symb_type<>1  // no text for gap
               then begin
                      check_intx:=limits(h_minint,h_maxint,id_point.X*sx+ex-gx,dummy_i);
                      check_inty:=limits(h_minint,h_maxint,(id_point.Y+yd)*sy+by-gy,dummy_i);

                      move_to.X:=Round(check_intx); move_to.Y:=Round(check_inty);

                      Font.Assign(pad_form.pad_dropper_font_label.Font);

                      font_height:=Round(id_height.X*sx);

                      if font_height<1 then font_height:=1;

                      Font.Height:=0-font_height;   // heights negative

                      drawn_id_str:=Trim(symb_id_str);

                      half_stringwidth:=TextWidth(drawn_id_str) div 2;
                      half_stringheight:=TextHeight(drawn_id_str) div 2;

                      if n=highlit_symbol
                         then Font.Color:=set_highlit_colour
                         else Font.Color:=symb_colour;

                      Brush.Color:=paper_colour;
                      Brush.Style:=bsSolid; //bsClear;

                      if symb_type=2  // sticker  draw box rectangle
                         then begin

                                Pen.Width:=2;
                                Pen.Style:=psSolid;
                                Pen.Mode:=pmCopy;
                                Pen.Color:=clBlack;

                                if n=highlit_symbol
                                   then Brush.Color:=set_highlit_colour
                                   else Brush.Color:=symb_colour;

                                Rectangle(move_to.X-half_stringwidth-7, move_to.Y-half_stringheight, move_to.X+half_stringwidth+9, move_to.Y+half_stringheight);  // 7,9 arbitrary padding

                                Brush.Style:=bsClear;  // for TextOut over rectangle

                                Pen.Width:=1;  // reset

                                Font.Color:=clBlack;

                              end;

                      TextOut(move_to.X-half_stringwidth,move_to.Y-half_stringheight,drawn_id_str);

                      Font.Assign(pad_form.Font);  // restore
                    end;

          end;//with
        end;//with

      end;//with

      RESULT:=True;  // something was drawn

    end;//next symbol
  end;//with canv
end;
//______________________________________________________________________________

function new_marks_list(var list:Tmark_array):boolean;

         // return pointer to array of pointers to Tmark records.
var
  mark_count:integer;

  num_rail_lengths:extended;
  num_sleepers,num_timbers:integer;
  min_count:integer;

begin
  RESULT:=True;                                 // assume good result.

  SetLength(list, 0);

  // estimate number of marks needed...

  mark_count:=0;

  if (cl_only=True) or (no_timbering=True)          // no timbering.
     then begin
            if cl_only=True then mark_count:=50     // so only 50 slots (for platforms, was 40), for label, peg, rad centres, end marks, etc
                            else mark_count:=100;   // rails but no timbering, also need the joints and guidemarks (100=arbitrary.)
          end
     else begin
                             // first assume its all plain track...

            num_rail_lengths:=turnoutx/(railen[pt_i]*inscale);     // number of rail lengths (extended - inluding fraction of rail).

            if num_rail_lengths<1.0 then num_rail_lengths:=1.0;     // minimum (in case of a custom long rail with only a few timbers at one end). 0.76.a 4-11-01

            if (retpar_i=1) and (plain_track=False) and (turnoutx>tvjp_ncx)
               then num_rail_lengths:=num_rail_lengths+(turnoutx-tvjp_ncx)/(railen[pt_i]*inscale);  // for return curve.

            num_sleepers:=Round(num_rail_lengths*sleeper_count[pt_i])+bontimb+1;  // number of sleepers (+1 for rounding down).

            mark_count:=Round(num_rail_lengths*2+60);      // 2 joints per rail-length + 60 (arbitrary) for start joints, peg, centres, transition marks, etc.

            if pad_form.timber_outlines_menu_entry.Checked=True
               then begin
                      if timbering_infill=False then mark_count:=mark_count+num_sleepers*6    // 6 marks = 4 sides + 2 reduced ends.
                                                else mark_count:=mark_count+num_sleepers*8;   // 2 extra mark entries per sleeper for timber infill (4 corners inside the outline extensions).
                    end;

            if pad_form.timber_centres_menu_entry.Checked=True then mark_count:=mark_count+num_sleepers;      // 1 mark for timber centre-line.

            if dxf_form.unchaired_kerf_checkbox.Checked=True then mark_count:=mark_count+num_sleepers*30;     // 228b  kerf adjusted outlines  timber + 2 sockets, nibs and snibs

            if exp_chairing=True
               then begin
                      if dxf_form.unchaired_kerf_checkbox.Checked=False then mark_count:=mark_count+num_sleepers*30;  // if not already done   kerf adjusted outlines  timber + 2 sockets, nibs and snibs

                      mark_count:=mark_count+num_sleepers*4;      // 221a chair outline corners = 2 marks per chair = 4 for plain track (1 mark = 2 corner points).

                      mark_count:=mark_count+num_sleepers*26;     // 221a chair outline corner rads = 14 additional marks per chair = 26 for plain track (1 mark = 2 points on rad).

                      mark_count:=mark_count+num_sleepers*16;     // 221a chair bolt marks. 4 bolts per chair = 8 marks per chair.

                      mark_count:=mark_count+num_sleepers*10;     // 221a insertion markers, 5 per chair.  (key, seat, outer jaw, inner jaw, spare)

                      mark_count:=mark_count+num_sleepers*60;     // 60 marks per timber for the 3D timber webs, sprues and outer flanges, dropper wire ridges    was 44 241f

                      mark_count:=mark_count+num_sleepers*240;    // 240 marks per chair for chair plugs, sockets, socket flanges, supports  244d  was 200   237a  was 32 234e    was 50 241b

                      mark_count:=mark_count+num_sleepers*14;     // 14 marks for timber top flange   228a

                    end;

            if pad_form.timber_numbers_menu_entry.Checked=True then mark_count:=mark_count+num_sleepers;      // 1 mark for timber number.

            mark_count:=mark_count+num_sleepers*5;   // 228b  another 5 marks per timber for others

            if plain_track=False    // then for turnouts...
               then begin
                      min_count:=Round((mxpx-xorg)/scale*8);               // (8 arbitrary) minimum for turnout, 8 marks per ft.
                      if mark_count<min_count then mark_count:=min_count;

                      mark_count:=mark_count+500;                       // (500 arbitrary) extra marks for a turnout.

                      mark_count:=mark_count+12;    // 0.94.a check-rail labels, 244e check extent marks

                      mark_count:=mark_count+24;    // 211b long switch marks and switch and xing labels.

                      if exittb_i<>1
                         then num_timbers:=Round((mvjpx-toex)/(scale*2))       // assume 2ft spacing
                         else num_timbers:=Round((turnoutx-toex)/(scale*2));   // exit timbered

                      if dxf_form.unchaired_kerf_checkbox.Checked=True then mark_count:=mark_count+num_timbers*8;     // 2 additional sockets

                      if exp_chairing=True   // 221a
                         then begin
                                if dxf_form.unchaired_kerf_checkbox.Checked=False then mark_count:=mark_count+num_timbers*8;     // 228b kerf-adjusted outlines, 2 additional sockets

                                mark_count:=mark_count+num_timbers*4;     // 2 additional chairs per timber for turnout ...

                                mark_count:=mark_count+num_timbers*26;    // 14 additional marks per chair for the corners

                                mark_count:=mark_count+num_timbers*16;    // 8 additional marks per chair for the bolt centres

                                mark_count:=mark_count+num_timbers*2;     // 1 aditional mark per chair for the seat insertion markers.

                                mark_count:=mark_count+num_timbers*10;    // 221a additional insertion markers, 5 per chair.  (key, seat, outer jaw, inner jaw, spare)

                                mark_count:=mark_count+num_timbers*200;   // 100 marks per additional chair for chair plugs, sockets, socket flanges, supports  228a      was 32 234e     was 40 241b

                                mark_count:=mark_count+num_timbers*120;   // 120 spare marks per timber for pointwork chairing  237a

                              end;

                    end;
          end;

  mark_count:=mark_count+500;     // 221a  500 more for luck

  SetLength(list, mark_count);

  Result := True;
end;
//___________________________________________________________________________________________

function new_aqarray(aq:integer):boolean;   // finished with old rail data, so free the memory.
                                            // then create a new array.
var
  n:integer;
  p:Pointer;
  list_size:extended;
  max_list:integer;

begin
  RESULT:=False;             // init default return.

                             // first clear the old...
  for n:=0 to 1 do begin
    p:=xy_p[aq,n];                     // 0=x, 1=y
    if p<>nil then intarray_free(p);
    xy_p[aq,n]:=nil;                   // in case not wanted, or error.
  end;//for

  nldim_array[aq]:=0;                  //  clear max array index.

  nlmax_array[aq]:=0;             //  clear nlmax maximum index used.
  nlnow_array[aq]:=0;             //  clear nlnow current data array index.

                        // and then get a new list, if needed...

  if aqyn[aq]=False              // not wanted.
     then begin
            RESULT:=True;        // ok result.
            EXIT;
          end;

  list_size:=0;     // keep compiler happy.

  if (plain_track=True) or (cl_only=True) or (rail_section=0)
     then begin
            case aq of
              0,3,8,11,30,33,38,41 : if (cl_only=False) and (rail_section<>0)
                                        then list_size:=turnoutx/incx     // plain track main rails.
                                        else list_size:=0;                // no rails, centre-lines only

              16..23 : list_size:=turnoutx/incx; // platforms, trackbed edges

              24 : list_size:=turnoutx/incx;         // main-side centre-line.
              25 : list_size:=turnoutx/incx;         // 0.93.a for irregular half_diamond // turnout-side centre-line.

              else list_size:=0;                     // no turnout rails.
            end;//case
          end
     else begin             // normal turnout or half-diamond or gaunt...
            case aq of
                      0,8,30,38 : if main_road_i<>0 then list_size:=(xorg+main_road_endx)/incx       // straight stock rail   217a
                                                    else list_size:=turnoutx/incx;

                         16..23 : list_size:=turnoutx/incx;       // platforms, trackbed edges

                      1,9,31,39 : begin
                                    if gaunt=True                            // 0.93.a
                                       then list_size:=flcendx/incx          // includes approach track gauntletted rail.
                                       else list_size:=(flcendx-toex)/incx;  // closure rail - turnout-side wing rail.

                                       list_size:=list_size+(12*knuck_rad/k3n/scale);  // 214a   1" steps along knuckle radius. At normal setting that means 12 steps.
                                  end;

                     2,10,32,40 : begin
                                    if gaunt=True                               // 0.93.a
                                       then list_size:=wingendx_ms/incx         // includes approach track gauntletted rail.
                                       else list_size:=(wingendx_ms-toex)/incx; // closure rail - main-side wing rail.

                                       list_size:=list_size+(12*knuck_rad/k3n/scale);  // 214a   1" steps along knuckle radius. At normal setting that means 12 steps.
                                  end;

                     3,11,33,41 : if turnout_road_i>1 then list_size:=(xorg+turnout_road_endx)/incx       // 209a  curved stock rail.     was =2 217a
                                                      else list_size:=turnoutx/incx;

                     5,13,35,43 : if turnout_road_i>1 then list_size:=(xorg+turnout_road_endx-fpx)/incx   // 209a  vee splice rail.       was =2 217a
                                                      else list_size:=(turnoutx-fpx)/incx;

                     4,12,34,42 : if main_road_i<>0 then list_size:=(xorg+main_road_endx-fpx)/incx   // vee point rail. 217a
                                                    else list_size:=(turnoutx-fpx)/incx;

                     6,14,36,44 : list_size:=(ckl_ms)/incx;     // main-side check rails.
                     7,15,37,45 : list_size:=(ckl_ts)/incx;     // turnout-side check rails.

                             24 : if main_road_i<>0 then list_size:=(xorg+main_road_endx)/incx   // 217a  main-road centre-line
                                                    else list_size:=turnoutx/incx;

                             25 : if turnout_road_i>1 then list_size:=(xorg+turnout_road_endx)/incx   // 209a  turnout-road centre-line   was =2 217a
                                                      else list_size:=turnoutx/incx;

                          26,27 : list_size:=(kckmsflendx-kckmsx)/incx;  // k-crossing check rails.
                          28,29 : list_size:=(kckdsflendx-kckdsx)/incx;

                             else run_error(10);

            end;//case
          end;

  max_list:=Round(ABS(list_size+24));   // add an extra 24 slots to allow for terminal points, etc.

  p:=intarray_create(max_list,True);
  if p=nil then EXIT                    // create failed.
           else xy_p[aq,0]:=p;          // pointer to x-values array.

  p:=intarray_create(max_list,True);
  if p=nil then EXIT                    // create failed.
           else xy_p[aq,1]:=p;          // pointer to y_values array.

  nldim_array[aq]:=max_list;            //  set nldim with max index.
  RESULT:=True;                         // ok, got new lists.
end;
//_______________________________________________________________________________________

function new_calc_draw(on_canvas:TCanvas; calcs_code, mode:integer):boolean;  // calc and draw all rail lines and marks.
                                                                              // on the specified canvas.
var
  i,n,aq:integer;
  p1,p2:TPoint;
  move_to, line_to:TPoint;
  clear_by,infringed_by,ring_rad:extended;

  pen_width:integer; // 212a

  peg_dim,locator_dim1,locator_dim2:integer;  // 227a peg_dim moved here

  pex:Tpex;  // 227a

  locator_point:TPoint;

  check_intx,check_inty:extended;  // 227a

  tempX,tempY:integer;

  temp:extended;

  dummy_i:integer;

begin
  RESULT:=False;              // init error return.

  yd:=y_datum*100;            // first get y datum in 1/100th mm.

   // calc new marks and timbers (not rail ends), also peg angles...

   // 227a init clear the list of timber lengths...

  if timb_len_list.Count>0 then for n:=0 to timb_len_list.Count-1 do Ttimber_count_object(timb_len_list.Objects[n]).Free;  // 227a
  timb_len_list.Clear;   // 227a


  mark_index:=0;               //  init index for list of new marks.
  timb_numbers_str:='';        //  and accumulator string for the timber numbers.

  if new_marks_list(marks_list)=False    // ### clear the old marks list and create a new one.
     then begin
            memory_alert;     //  warn him.
            EXIT;
          end;

  total_template_timber_length:=0;   // 0.95.a init
  total_template_sleeper_length:=0;  // 226a init

  template_number_of_timbers:=0;     // 226a  init
  template_number_of_sleepers:=0;    // 226a  init

  timber_fill_overdraw_generated:=False;  // 227d  init

  if Length(current_shoved_timbers)>0
     then for n:=0 to Length(current_shoved_timbers)-1 do current_shoved_timbers[n].shoved_mod_infill.shoved_number_str:='';   // init  227e

  if (cl_only=False) and (no_timbering=False)  // no timber marks if drawing track centre-line only.
     then calctimbers;                         // calc timbering marks, and total timber/sleeper lengths

  shove_timber_form.timbering_length_label.Caption:='pointwork timbering   : '+round_str(total_template_timber_length,2)+' mm  ( '+IntToStr(template_number_of_timbers)+' timbers )';
  shove_timber_form.sleepering_length_label.Caption:='plain track sleepering : '+round_str(total_template_sleeper_length,2)+' mm  ( '+IntToStr(template_number_of_sleepers)+' sleepers )';

  guidemarks;         // calc guide marks and rad end marks (need the timbering calculated first for rail joints).

  calc_fill_symbols;  // 227a  calc the droppers and gaps


  if plain_track=False then add_check_labels;    // 0.94.a

  if (hide_current_flag=False) and (calcs_code=2) and (keep_form.Active=False)
     then begin
            current_is_showing:=current_is_showing or pad_marks_current(on_canvas,True);  // then draw them in.

            if (cl_only=False) and (pad_form.show_template_symbols_menu_entry.Checked=True)
               then current_is_showing:=current_is_showing or draw_symbols_current(on_canvas,0);  // 227a draw the 0=droppers over timbers
          end;

  xy_max[0]:=Integer($80000000);       // init max x with min 32-bit value.
  xy_min[0]:=Integer($7FFFFFFF);       // init min x with max 32-bit value.
  xy_max[1]:=Integer($80000000);       // ditto y...
  xy_min[1]:=Integer($7FFFFFFF);

  list_planing_mark_aq1:=0;        // init markers for infilled switch blades...
  list_planing_mark_aq2:=0;

  min_ring_distance:=maxfp;        // init for infringements. v:0.76.a 1-5-02.
  ring_infringed:=False;
  ring_copies_infringed:=False;
  incx125:=incx*125;

  for n:=0 to ring_count_c do begin
    rings_checkpoints[n,2]:=-1;        // no aq yet being checked.
    rings_checkpoints[n,3]:=0;         // not closer than incx to ring.
  end;//for

  pen_width:=1;  // init

  if dummy_template=True      // 212a
     then begin
            if bgnd_form.pad_shapes_linewidth_2_radiobutton.Checked=True then pen_width:=2;
            if bgnd_form.pad_shapes_linewidth_3_radiobutton.Checked=True then pen_width:=3;
          end;

  for aq:=aq_max_c downto 0 do begin      // Main loop for rails :
                                          // (in reverse order so that FB foot, adjacent rails and centre-lines are overdrawn.)


  if (aqyn[aq]=True) and (draw_mode<>0) and ( (mode=1) or (mode=2) ) and (hide_current_flag=False) and (calcs_code=2) and (keep_form.Active=False)
     then begin                                                  // re-daw in progress on-screen
            ink_colour:=paper_colour;
            show_a_line(pad_form.Canvas,aq,pen_width,True);      // so erase any existing line, one line at a time (ignore return).
          end;                                                   // n.b. directly on the pad canvas, in case of canvas change since draw.

  if new_aqarray(aq)=False then EXIT;   // finished with old data, so free any memory, and then get a new list.

  if (cl_only=True) or (rail_section=0)    // no rails wanted, but don't ignore platforms and trackbed edges
     then begin
            if aq<16 then CONTINUE;  // no template rails

            if adjacent_edges=False   // adjacent rails
               then begin
                      if (aq<>24) and (aq<>25) then CONTINUE;   // only centre lines, ignore adjacent rails
                    end
               else begin                        // platforms and trackbed edges
                      if aq>25 then CONTINUE;    // ignore only half-diamond rails
                    end;
          end;

  if (plain_track=False) or (aq=0) or (aq=8) or (aq=3) or (aq=11) or ((aq>15) and (aq<25))  // stock rails and adjacent tracks only if plain track.
     then begin
            if aqyn[aq]=True
               then begin
                      if oneline(aq)=False    //  Calc all x,y for rail-edges and put in lists.
                         then begin
                                abandon_calcs:=True;   // 0.93.a ex 081 for irregular diamond calcs.
                                EXIT;
                              end;

                      if (nlnow_array[aq]>=(nldim_array[aq]-1)) and (draw_mode=0)  //  just ignore if we are re-drawing.
                         then repeat
                                i:=alert(1,'   calculation  diagnostics ...',aq_str[aq]+'||rail-data overflow !||Increase the step size or reduce the overall length.',
                                                      '','','? help','','abandon this','continue  with  truncated  data',3);
                                if i=3 then alert_help(0,'||Sorry, detailed overflow help is not yet written.'
                                                        +'||(Your template is too long for the memory allocated to contain it.)','');
                                if i=5 then begin
                                              abandon_calcs:=True;
                                              EXIT;
                                            end;
                              until i<>3

                         else begin
                                if (hide_current_flag=False) and (calcs_code=2) and (keep_form.Active=False)
                                   then begin
                                          ink_colour:=rail_colour;  // init

                                          if (aq=24) or (aq=25) // track centre-lines
                                             then begin
                                                    if dummy_template=True
                                                       then ink_colour:=shapes_colour
                                                       else ink_colour:=guide_colour;
                                                  end;

                                          if ((aq=16) or (aq=17) or (aq=20) or (aq=21)) and (adjacent_edges=True)  // 0.93.a platforms
                                             then ink_colour:=guide_colour;

                                          current_is_showing:=current_is_showing or show_a_line(on_canvas,aq,pen_width,False); //  draw in new line.
                                        end;
                              end;

                    end;
          end;
  end;//NEXT aq

  if xy_max[0]=Integer($80000000)       // nothing put in the lists (all rails turned off ?)
     then begin
            xy_max[0]:=Round(turnoutx*100);       // approx 1/100th mm
            xy_min[0]:=0;
            xy_max[1]:=Round(turnoutx*50);        // arbitrary half of x.
            xy_min[1]:=0;
          end;

  if ((cl_only=False) and (rail_section<>0))
  or( ((draw_ts_platform=True) or (draw_ms_platform=True)) and (adjacent_edges=True) )
     then do_railends;  //  calc rail end marks and platform ends

  if (hide_current_flag=True) or (calcs_code<>2) or (keep_form.Active=True)   //  no more drawing.
     then begin
            RESULT:=True;     //  flag rails calculated ok.
            EXIT;
          end;

  if ((cl_only=False) and (rail_section<>0))
  or ( ((draw_ts_platform=True) or (draw_ms_platform=True)) and (adjacent_edges=True) )
     then draw_rail_endmarks(on_canvas,True);   // and draw rail end marks and platform ends.

  if (cl_only=False) and (pad_form.show_template_symbols_menu_entry.Checked=True)
     then begin
            current_is_showing:=current_is_showing or draw_symbols_current(on_canvas,1);   //  227a draw the gaps over rails
            current_is_showing:=current_is_showing or draw_symbols_current(on_canvas,2);   //  227a draw the stickers over rails
          end;


  with on_canvas do begin

       // 227a  calc and draw the locator ...

    if pad_form.show_locator_menu_entry.Checked=True
       then begin
              case locator_rail of
                       0: do_nothing;         // free move, set by trail_locator

                       1: begin
                            locatory:=0;      // straight stock rail, middle of    aq=0.
                            locatork:=0;
                          end;

                       2: locatory:=aq2offset(locatorx,locatork);  // curved turnout rail       aq=2.

                       3: begin
                            locatory:=g;                       // straight turnout rail     aq=1.
                            locatork:=0;
                          end;

                       4: if plain_track=True
                             then begin
                                    locatory:=g;       // TS running rail plain track.
                                    locatork:=0;
                                  end
                             else locatory:=aq3offset(locatorx,locatork);  // curved stock rail         aq=3.

                       5: begin
                            locatory:=g/2-trmscent;            // adjacent main-side centre-line.
                            locatork:=0;
                          end;
                       6: begin
                            locatory:=g/2+trtscent;            // adjacent turnout-side centre-line.
                            locatork:=0;
                          end;
                       7: begin
                            locatory:=(g+trtscent)/2;          // double-track turnout-side centre-line.
                            locatork:=0;
                          end;
                       8: begin
                            locatory:=g/2;                     // main road centre-line     aq=24.
                            locatork:=0;
                          end;
                       9: locatory:=aq25offset(locatorx,locatork); // turnout road centre-line  aq=25.

                     else begin
                            locatory:=g/2;    // main-road centre-line
                            locatork:=0;
                          end;
              end;//case

              try
                case locator_rail of
                         1: begin
                              pex.x:=locatorx;
                              pex.y:=locatory-j/2;
                            end;

                         2: begin
                              pex.x:=locatorx+j/2*SIN(locatork);
                              pex.y:=locatory-j/2*COS(locatork);
                            end;

                         3: begin
                              pex.x:=locatorx;
                              pex.y:=locatory+j/2;
                            end;

                         4: begin
                              pex.x:=locatorx-j/2*SIN(locatork);
                              pex.y:=locatory+j/2*COS(locatork);
                            end;
                       else begin
                              pex.x:=locatorx;
                              pex.y:=locatory;
                            end;
                end;//case
              except
                pex.x:=locatorx;
                pex.y:=locatory;
              end;//try
              
              locator_point:=convert_point(curve_point(pex));    // to grid in 1/100ths mm

              check_intx:=limits(h_minint,h_maxint,locator_point.X*sx+ex-gx,dummy_i);
              check_inty:=limits(h_minint,h_maxint,(locator_point.Y+yd)*sy+by-gy,dummy_i);

              tempX:=Round(check_intx);
              tempY:=Round(check_inty);

              Pen.Style:=psSolid;

              if paper_colour<>clBlue then Pen.Color:=clBlue
                                      else Pen.Color:=clBlack;

              locator_dim1:=Round(10*inscale*fx);  // 10" each way    cross-hairs
              locator_dim2:=Round(6*inscale*fx);   //  6" each way    diamond

                // locator cross-hairs ..

              Pen.Width:=1;

              move_to.X:=tempX-locator_dim1;  move_to.Y:=tempY;
              line_to.X:=tempX+locator_dim1;  line_to.Y:=tempY;
              if check_limits(move_to, line_to)=True then begin MoveTo(move_to.X, move_to.Y); LineTo(line_to.X, line_to.Y); end;

              move_to.X:=tempX;  move_to.Y:=tempY-locator_dim1;
              line_to.X:=tempX;  line_to.Y:=tempY+locator_dim1;
              if check_limits(move_to, line_to)=True then begin MoveTo(move_to.X, move_to.Y); LineTo(line_to.X, line_to.Y); end;


                // locator diamond ..

              Pen.Width:=2;  

              move_to.X:=tempX;               move_to.Y:=tempY+locator_dim2;  // north

              line_to.X:=tempX+locator_dim2;  line_to.Y:=tempY;               // east
              if check_limits(move_to, line_to)=True then begin MoveTo(move_to.X, move_to.Y); LineTo(line_to.X, line_to.Y); end;

              line_to.X:=tempX;               line_to.Y:=tempY-locator_dim2;  // south
              if check_limits(move_to, line_to)=True then begin {MoveTo(move_to.X, move_to.Y);} LineTo(line_to.X, line_to.Y); end;

              line_to.X:=tempX-locator_dim2;  line_to.Y:=tempY;               // west
              if check_limits(move_to, line_to)=True then begin {MoveTo(move_to.X, move_to.Y);} LineTo(line_to.X, line_to.Y); end;

              line_to.X:=tempX;               line_to.Y:=tempY+locator_dim2;  // north
              if check_limits(move_to, line_to)=True then begin {MoveTo(move_to.X, move_to.Y);} LineTo(line_to.X, line_to.Y); end;


            end;



      // finally draw the fixing peg

      // peg drawing moved here 227a to overwrite rails and droppers

      // centre dims saved for us by pad_marks_current

    Pen.Style:=psSolid;
    Pen.Width:=1;

    if paper_colour<>clRed then Pen.Color:=clRed
                           else Pen.Color:=clBlack;

    peg_dim:=10;  // init

    if peg_dim>Round(scale*fx) then peg_dim:=Round(scale*fx); // but not more than 2ft scale overall.

    if pad_form.peg_solid_red_menu_entry.Checked=True    // 0.95.a
       then Brush.Color:=Pen.Color                       // solid red
       else Brush.Color:=paper_colour;

    if peg_dim<5
       then begin
              peg_dim:=5;
              Brush.Color:=Pen.Color;   // solid fill when very small
            end;

    if turnoutx=0 then peg_dim:=peg_dim*2;   // 0.93.a  invalidated template

    Brush.Style:=bsSolid;    // solid is better for aligning with the notch.

    if turnoutx=0    // 0.93.a  invalidated template -- draw red Z instead of peg ...
       then begin
              Pen.Width:=2;
              move_to.X:=pad_pegx-peg_dim;  move_to.Y:=pad_pegy-peg_dim;    // top of Z
              line_to.X:=pad_pegx+peg_dim;  line_to.Y:=pad_pegy-peg_dim;
              if check_limits(move_to, line_to)=True then begin MoveTo(move_to.X, move_to.Y); LineTo(line_to.X, line_to.Y); end;

              move_to.X:=pad_pegx+peg_dim;  move_to.Y:=pad_pegy-peg_dim;    // diagonal of Z
              line_to.X:=pad_pegx-peg_dim;  line_to.Y:=pad_pegy+peg_dim;
              if check_limits(move_to, line_to)=True then begin MoveTo(move_to.X, move_to.Y); LineTo(line_to.X, line_to.Y); end;

              move_to.X:=pad_pegx-peg_dim;  move_to.Y:=pad_pegy+peg_dim;    // bottom of Z
              line_to.X:=pad_pegx+peg_dim;  line_to.Y:=pad_pegy+peg_dim;
              if check_limits(move_to, line_to)=True then begin MoveTo(move_to.X, move_to.Y); LineTo(line_to.X, line_to.Y); end;

              Pen.Width:=1; // restore
            end
       else begin     // normal peg ...

              move_to.X:=pad_pegx-peg_dim;  move_to.Y:=pad_pegy-peg_dim;
              line_to.X:=pad_pegx+peg_dim;  line_to.Y:=pad_pegy+peg_dim;

              if pad_form.peg_hollow_menu_entry.Checked=True    // 0.95.a
                 then begin
                        if check_limits(move_to, line_to)=True then arc_ellipse(on_canvas,move_to.X, move_to.Y, line_to.X, line_to.Y);  // 0.95.a clear centre
                      end
                 else begin
                        if check_limits(move_to, line_to)=True then Ellipse(move_to.X, move_to.Y, line_to.X, line_to.Y);
                      end;

              move_to.X:=pad_pegx-peg_dim*2;  move_to.Y:=pad_pegy;
              line_to.X:=pad_pegx+peg_dim*2;  line_to.Y:=pad_pegy;
              if check_limits(move_to, line_to)=True then begin MoveTo(move_to.X, move_to.Y); LineTo(line_to.X, line_to.Y); end;

              move_to.X:=pad_pegx;  move_to.Y:=pad_pegy-peg_dim*2;
              line_to.X:=pad_pegx;  line_to.Y:=pad_pegy+peg_dim*2;
              if check_limits(move_to, line_to)=True then begin MoveTo(move_to.X, move_to.Y); LineTo(line_to.X, line_to.Y); end;

            end;


        // the final job is to draw the arms on the peg...
        // these marks have been kindly saved for us by pad_marks_current

    Pen.Color:=clRed;

    p1:=peg_arm1.p1;      // first arm
    p2:=peg_arm1.p2;

    move_to.X:=Round(p1.X*sx+ex-gx);  move_to.Y:=Round((p1.Y+yd)*sy+by-gy);
    line_to.X:=Round(p2.X*sx+ex-gx);  line_to.Y:=Round((p2.Y+yd)*sy+by-gy);
    if check_limits(move_to, line_to)=True then begin MoveTo(move_to.X, move_to.Y); LineTo(line_to.X, line_to.Y); end;

    p1:=peg_arm2.p1;      // second arm
    p2:=peg_arm2.p2;

    move_to.X:=Round(p1.X*sx+ex-gx);  move_to.Y:=Round((p1.Y+yd)*sy+by-gy);
    line_to.X:=Round(p2.X*sx+ex-gx);  line_to.Y:=Round((p2.Y+yd)*sy+by-gy);
    if check_limits(move_to, line_to)=True then begin MoveTo(move_to.X, move_to.Y); LineTo(line_to.X, line_to.Y); end;

  end;//with canvas

  RESULT:=True;             //  flag rails calculated ok.

              // infringement results... v:0.76.a 1-5-02

  with info_form do begin
    if ((ring_warn=True) and (ring_infringed=True))
    or ((ring_copies_warn=True) and (ring_copies_infringed=True))
       then ring_lamp_panel.Tag:=1             // flashing.
       else if (ring_warn=True)
            or (ring_copies_warn=True)
               then ring_lamp_panel.Tag:=0     // steady.
               else ring_lamp_panel.Tag:=2;    // lamp off.

    if ring_warn=True   // labels are showing...
       then begin
              if (cl_only=True) or (warn_centrelines=True)
                 then ring_rad:=rings[0,2]/2  // centre-lines infringe inner diameter.
                 else ring_rad:=rings[0,3]/2; // rails (gauge-faces) infringe outer diameter.

              if ring_infringed=False
                 then begin
                        ring_infringed_warning_label.Font.Color:=clBlue;
                        ring_infringed_warning_label.Caption:=' clear  by';
                        ring_infringed_by_label.Font.Color:=clBlue;
                        clear_by:=ABS(min_ring_distance/100-ring_rad);
                        if clear_by<1.0E6 then ring_infringed_by_label.Caption:=round_str(clear_by,2)+' mm'
                                          else ring_infringed_by_label.Caption:='  a  mile';   // no rails?
                      end
                 else begin
                        ring_infringed_warning_label.Font.Color:=clRed;
                        ring_infringed_warning_label.Caption:='infringed by';
                        ring_infringed_by_label.Font.Color:=clRed;
                        infringed_by:=ABS(ring_rad-min_ring_distance/100);
                        if infringed_by<1.0E6 then ring_infringed_by_label.Caption:=round_str(infringed_by,2)+' mm'
                                              else ring_infringed_by_label.Caption:='  a  mile';
                      end;
            end;
  end;//with

  if grid_form.show_dummy_vehicles_radio_button.Checked=True then draw_dummy_vehicle_on_control_template(False,on_canvas);  //  0.98.a  draw dummy vehicle over template

end;
//______________________________________________________________________________

function get_cl_offset:extended;

begin
  case cl_options_code of     // 213a

      -3: RESULT:=g/2-tb/2;
      -2: RESULT:=g/2-trmscent;
      -1: RESULT:=g/2-trmscent/2;
       0: RESULT:=g/2;               // main road (normal)
       1: RESULT:=g/2+trtscent/2;
       2: RESULT:=g/2+trtscent;
       3: RESULT:=g/2+tb/2;

      99: RESULT:=g/2+cl_options_custom_offset;

     else RESULT:=g/2;
  end;//case
end;
//______________________________________________________________________________


procedure draw_dummy_vehicle_on_control_template(for_env:boolean; on_canvas:TCanvas);    // 0.98.a

    // 215c  for_env = for outlines envelope, come here to generate next step, not for drawing vehicle

var
  dvx1,dvy1,dvx2,dvy2,dummy1,dummy2,dvs_mm,dvwb_mm,dv,dvwb_calc:extended;

  pin_dim:integer;

  move_to,line_to,pin1,pin2:TPoint;

  dv_len,dv_hbw,dv_hcw,dv_el,dv_theta:extended;

  tr_y,tr_k:extended;  // on turnout road rail

  xtr,ytr:extended;    // on turnout road centre-line

  calc_dir:integer;

  cl_offset:extended;  // 213a

                       /////////////////////////////////////////////////////////

                       function mm_to_pixels(p:Tpex):TPoint;

                       begin
                         RESULT.X:=Round(p.x*fx+ex-gx);
                         RESULT.Y:=Round((p.y*hand_i+y_datum)*fy+by-gy);
                       end;
                       /////////////////////////////////////////////////////////

                       procedure draw_dv_line(p1,p2:Tpex);

                       begin
                         move_to:=mm_to_pixels(p1);
                         line_to:=mm_to_pixels(p2);

                            // draw line between them ...

                         if check_limits(move_to,line_to)=True
                            then begin on_canvas.MoveTo(move_to.X,move_to.Y); on_canvas.LineTo(line_to.X,line_to.Y); end;

                       end;
                       /////////////////////////////////////////////////////////

begin

  with cdvi do begin     // vehicle dimensions

    if dv_start<(0-dv_length) then dv_start:=0-dv_length;  // 205a limits

    if dv_start>(turnoutx/inscale+dv_length-dv_wheelbase) then dv_start:=turnoutx/inscale+dv_length-dv_wheelbase;  // 205a limits

    dvwb_mm:=dv_wheelbase*inscale;  // diagonal, init target wheelbase chord

    dv:=dvwb_mm;                    // along curve, init
    dvs_mm:=dv_start*inscale;       //init

      // limit to 1.75 x radius for wheelbase chord ...

    if grid_form.turnout_road_dummy_vehicle_radio_button.Checked=True    // turnout road ...
       then begin
              if dvwb_mm>ABS(rmin_mm*1.75) then EXIT;  // nonsense dimension, won't calculate

              tr_y:=aq3offset(dvs_mm,tr_k);  // get start position on stock rail

              xtr:=dvs_mm+g/2*SIN(tr_k);     // start position on centre line
              ytr:=tr_y-g/2*COS(tr_k);

              docurving(True,True,xtr,ytr,dvx1,dvy1,dummy1,dummy2);   // 1st bogie-pin on pad

              calc_dir:=0; //init

              repeat
                tr_y:=aq3offset(dvs_mm+dv,tr_k);  // get approx 2nd bogie pin position on stock rail

                xtr:=dvs_mm+dv+g/2*SIN(tr_k);     // approx start position on centre line
                ytr:=tr_y-g/2*COS(tr_k);

                docurving(True,True,xtr,ytr,dvx2,dvy2,dummy1,dummy2);  // 2nd bogie-pin on pad, dv is along curve.

                dvwb_calc:=SQRT(SQR(dvx2-dvx1)+SQR(dvy2-dvy1));

                if ABS(dvwb_calc-dvwb_mm)<0.005 then BREAK;  //  near enough

                if (dvwb_calc<dvwb_mm) or (calc_dir=1)    // calculated wheelbase too short
                   then begin

                          if calc_dir=-1 then BREAK;      // was too long, now shortened enough

                          if calc_dir=0 then calc_dir:=1;    // set for lengthen re-calcs only

                          if dvwb_calc>=dvwb_mm
                             then BREAK
                             else begin
                                    dv:=dv+0.01;  // lengthen and try again
                                    CONTINUE;
                                   end;
                        end;

                if (dvwb_calc>dvwb_mm) or (calc_dir=-1)    // calculated wheelbase too long (turnout road)
                   then begin

                          if calc_dir=1 then BREAK;        // was too short, now lengthened enough

                          if calc_dir=0 then calc_dir:=-1;    // set for shorten re-calcs only

                          if dvwb_calc<=dvwb_mm
                             then BREAK
                             else begin
                                    dv:=dv-0.01;  // shorten and try again
                                    CONTINUE;
                                   end;
                        end;

              until 0<>0;

            end
       else begin                     // main road or plain track...
              if spiral=False
                 then begin
                        if dvwb_mm>ABS(nomrad*1.75) then EXIT;  // nonsense dimension, won't calculate
                      end
                 else begin
                        if (dvwb_mm>ABS(nomrad1*1.75)) or (dvwb_mm>ABS(nomrad2*1.75)) then EXIT;  // nonsense dimensions, won't calculate
                      end;

              if ABS(cl_options_code)=2          // 213a   adjacent centre-lines
                 then cl_offset:=get_cl_offset
                 else cl_offset:=g/2;

              docurving(True,True,dvs_mm,cl_offset,dvx1,dvy1,dummy1,dummy2);   // 1st bogie-pin on pad

              repeat

                  // re-calc position of 2nd bogie pin until chord matches required wheelbase ...

                docurving(True,True,dvs_mm+dv,cl_offset,dvx2,dvy2,dummy1,dummy2);  // 2nd bogie-pin on pad, dv is along curve.

                dvwb_calc:=SQRT(SQR(dvx2-dvx1)+SQR(dvy2-dvy1));

                if ABS(dvwb_calc-dvwb_mm)<0.005 then BREAK;  //  near enough

                if dvwb_calc>=dvwb_mm
                   then BREAK
                   else dv:=dv+0.01;  // lengthen and try again

              until 0<>0;
            end;

         // outline calcs ...

    try
      dv_theta:=ARCSIN((dvy2-dvy1)/dvwb_calc);  // angle on pad between pins
      if dvx2<dvx1 then dv_theta:=Pi-dv_theta;
    except
      dv_theta:=Pi/2*SGN(dvy2-dvy1);            // +/- 90 degrees
    end;//try

    dv_hcw:=(dv_width/2+dv_clearance)*inscale;   // half the full clearance width

    dv_hbw:=dv_width/2*inscale;                  // half body width

    dv_el:=(dv_length-dv_wheelbase)/2*inscale;   // end length from pins

    dv_len:=dv_length*inscale;                   // body length



    with dv_corners_calc do begin   // calcs for control template     global results for making copies

         // calc body outline b1 - b2 - b3 - b4  clockwise corners from bottom left ...

      pb1.x:=dvx1+dv_hbw*SIN(dv_theta);     // body opposite pins...
      pb1.y:=dvy1-dv_hbw*COS(dv_theta);

      pb2.x:=dvx1-dv_hbw*SIN(dv_theta);
      pb2.y:=dvy1+dv_hbw*COS(dv_theta);

      b1.x:=pb1.x-dv_el*COS(dv_theta);      // near body corners...
      b1.y:=pb1.y-dv_el*SIN(dv_theta);

      b2.x:=pb2.x-dv_el*COS(dv_theta);
      b2.y:=pb2.y-dv_el*SIN(dv_theta);

      b3.x:=b2.x+dv_len*COS(dv_theta);      // far body corners...
      b3.y:=b2.y+dv_len*SIN(dv_theta);

      b4.x:=b1.x+dv_len*COS(dv_theta);
      b4.y:=b1.y+dv_len*SIN(dv_theta);

      o1.x:=b1.x+33*SIN(dv_theta);          // overdraw blanking corners  33" arbitrary (used only for envelope)...
      o1.y:=b1.y-33*COS(dv_theta);

      o2.x:=b2.x-33*SIN(dv_theta);
      o2.y:=b2.y+33*COS(dv_theta);

      o3.x:=b3.x-33*SIN(dv_theta);
      o3.y:=b3.y+33*COS(dv_theta);

      o4.x:=b4.x+33*SIN(dv_theta);
      o4.y:=b4.y-33*COS(dv_theta);

      if for_env=False    // not for outlines envelope  215c
         then begin
                with on_canvas do begin     // so draw dummy vehicle

                  Pen.Style:=psSolid;
                  Pen.Width:=1;
                  Pen.Color:=pad_form.Font.Color;  //clRed;


                  Brush.Color:=timber_colour;
                  Brush.Style:=bsDiagCross;

                    // (check_limits omitted ...)

                  Polygon([mm_to_pixels(b1),mm_to_pixels(b2),mm_to_pixels(b3),mm_to_pixels(b4)]);


                  Brush.Color:=paper_colour;  // restore Brush for gaps in dotted lines.
                  Brush.Style:=bsSolid;
                end;//with canvas
              end;

        // calc clearance extensions c1 - c2 - c3 - c4  clockwise corners from bottom left ...

      pc1.x:=dvx1+dv_hcw*SIN(dv_theta);     // clearance opposite pins...
      pc1.y:=dvy1-dv_hcw*COS(dv_theta);

      pc2.x:=dvx1-dv_hcw*SIN(dv_theta);
      pc2.y:=dvy1+dv_hcw*COS(dv_theta);

      c1.x:=pc1.x-dv_el*COS(dv_theta);      // near corners...
      c1.y:=pc1.y-dv_el*SIN(dv_theta);

      c2.x:=pc2.x-dv_el*COS(dv_theta);
      c2.y:=pc2.y-dv_el*SIN(dv_theta);

      c3.x:=c2.x+dv_len*COS(dv_theta);      // far corners...
      c3.y:=c2.y+dv_len*SIN(dv_theta);

      c4.x:=c1.x+dv_len*COS(dv_theta);
      c4.y:=c1.y+dv_len*SIN(dv_theta);

      m1.x:=c1.x+dv_len*COS(dv_theta)/2;    // mid-points clearance...
      m1.y:=c1.y+dv_len*SIN(dv_theta)/2;

      m2.x:=c2.x+dv_len*COS(dv_theta)/2;
      m2.y:=c2.y+dv_len*SIN(dv_theta)/2;

    end;//with dv_corners_calc

    if for_env=True   // for outlines envelope  215c
       then EXIT      // go use dv_corners_calc to generate envelope
       else begin
              with on_canvas do begin     // draw dummy vehicle
                with dv_corners_calc do begin
                  Pen.Style:=psDash;        // clearance sides ...

                  draw_dv_line(c2,c3);
                  draw_dv_line(c4,c1);

                  Pen.Style:=psSolid;       // clearance corners ...

                  draw_dv_line(b2,c2);
                  draw_dv_line(c3,b3);
                  draw_dv_line(b4,c4);
                  draw_dv_line(c1,b1);

                  draw_dv_line(m1,m2);      // mid-line


                    // draw line between bogie pins ...

                  pt1.x:=dvx1;
                  pt1.y:=dvy1;

                  pt2.x:=dvx2;
                  pt2.y:=dvy2;

                  Pen.Style:=psSolid;

                  draw_dv_line(pt1,pt2);

                   // draw bogie pins ...

                  pin1:=mm_to_pixels(pt1);
                  pin2:=mm_to_pixels(pt2);

                  if grid_form.attach_ring_checkbox.Checked=True  // 206b
                     then begin
                            rings[0,0]:=dvx1;
                            rings[0,1]:=dvy1*hand_i+y_datum;
                          end;

                end;//with dv_corners_calc

                pin_dim:=7;                                                   // pin blob radius arbitrary.
                if pin_dim>Round(scale*fx/2) then pin_dim:=Round(scale*fx/2); // but not more than 6" scale radius.
                if pin_dim<1 then pin_dim:=1;                                 // and not less than this.

                   // 1st pin ...

                Brush.Color:=Pen.Color; // clRed;
                Brush.Style:=bsSolid;   // for pins

                move_to.X:=pin1.X-pin_dim;  move_to.Y:=pin1.Y-pin_dim;
                line_to.X:=pin1.X+pin_dim;  line_to.Y:=pin1.Y+pin_dim;

                if check_limits(move_to,line_to)=True
                   then Ellipse(move_to.X,move_to.Y, line_to.X,line_to.Y);

                   // 2nd pin ...

                move_to.X:=pin2.X-pin_dim;  move_to.Y:=pin2.Y-pin_dim;
                line_to.X:=pin2.X+pin_dim;  line_to.Y:=pin2.Y+pin_dim;

                if check_limits(move_to,line_to)=True
                   then Ellipse(move_to.X,move_to.Y, line_to.X,line_to.Y);

                Brush.Color:=paper_colour;  // restore Brush for gaps in dotted lines.

                draw_rings(on_canvas,False,True);   //  re-draw attached spacing-ring  (not copies).

              end;//with canvas

            end;

  end;//with cdvi
end;
//______________________________________________________________________________

function oneline(aq:integer):boolean; //  Calc a rail-line.
                                      //  Enter with required line in aq.
                                      //  X,Y data is put in rail-data array for this aq.
begin
  RESULT:=True;                       // default ok.

  case aq of                          // which rail-line ?

    0,8,16..24: RESULT:=strails(aq);        // straight stock rail, adjacent tracks/trackbed/platforms, main-road track centre-line.

           1,9: begin
                  RESULT:=strails(aq);                   // straight turnout rail to knuckle.
                  if knuckle_code<>-1 then knuckle(aq);  // knuckle radius  214a   -1=sharp knuckle
                  cuwing(aq);                            // then turnout-side wing rail.
                end;

          2,10: begin
                  curails(aq);                                  // curved turnout rail to end of curve.
                  if xing_calc_i=0 then stxrail(aq);            // then straight crossing part, if normal crossing.
                  if knuckle_code<>-1 then knuckle(aq);         // knuckle radius  214a   -1=sharp knuckle
                  stwing(aq);                                   // then main-side wing rail.
                end;

          3,11: begin
                  curails(aq{,flen_tw});      // curved stock rail to end of turnout curve.
                  if xing_calc_i<>1
                     then begin
                            stcurail(aq);                        // then add straight section if straight crossing, normal or original.
                            if retpar_i=1 then retrails(aq);     // and finally add the return curve if any.
                          end;
                end;

          4,12: stvee(aq);                  // straight vee rail.

          5,13: begin
                  cuvee(aq);                                               // curved vee rail,
                  if (xing_calc_i<>1) and (retpar_i=1) then retrails(aq);  // then if straight crossing,
                end;                                                       // add return curve if any.

          6,14: stckrail(aq);       // main-side check rail.

          7,15: cuckrail(aq);       // turnout-side check rail.

            25: turnroad_cl;        // turnout road centre-line.

            26: cuwing(aq);        // K-crossing check rail, MS, gauge-face
            27: cuwing(aq);        // K-crossing check rail, MS, outer edge

         28,29: k_checkrail_ds(aq);        // K-crossing check rail, DS.

           else run_error(26);
  end;//case
end;
//______________________________________________________________________________

procedure toe(aq:integer);      // do toe length.  ys constant.

begin
  if (gaunt=True) and (plain_track=False)     // plain track bug fix 211c
     then begin
            case aq of                     // which rail ?
                   2: ys:=h;               // trg, // not actually used, xb = nodraw.
                   3: ys:=h+g;             // srg.
                  10: ys:=h-j;             // tro, // not actually used, xb = nodraw.
                  11: ys:=h+g+j;           // sro.
                 else run_error(64);
            end;//case
          end
     else begin
            case aq of                     // which rail ?
                   2: ys:=0;               // trg, // not actually used, xb = nodraw.
                   3: ys:=g;               // srg.
                  10: ys:=0-j;             // tro, // not actually used, xb = nodraw.
                  11: ys:=g+j;             // sro.
                 else run_error(64);
            end;//case
          end;
  ks:=0;                          // gradient angle.
end;
//___________________________________________________________________________________________

procedure straight_planing(aq:integer; do_joggle:boolean);     // semi-curved switch : return ys, ks along planing length at this xs.

var
  xpl,y,len,jog:extended;

begin
  if switch_type<>-1 then run_error(154); // only come here for semi-curved switches.

  len:=stox-setx;   // length of planing for joggle.

  xpl:=xs-setx;     // current length along planing.

  if (do_joggle=True) and ( (aq=3) or (aq=11) ) and (len>minfp)
     then begin
            if xpl>=len
               then jog:=0                           // beyond end of planing.
               else jog:=(len-xpl)/len*joggle_deep;  // run out the joggle. 0.71.a 13-4-01.
          end
     else jog:=0;                    // no joggle offset.

  y:=jog+xpl*TAN(k1);                // total offset.

  case aq of                     // which rail ?
         2: ys:=y;                        // trg.
         3: ys:=g+y;                      // srg.
        10: ys:=0;                        // tro.
        11: ys:=g+y+j/COS(k1);            // sro.
       else run_error(155);
  end;//case

ks:=k1;                   // return ks gradient angle also.
end;
//_______________________________________________________________________________________________________________________________

procedure swcurve(aq:integer; do_joggle:boolean);    // do switch curve / straight switch.
                                                     // return ys,ks at this xs.
var
  x,temp:extended;
  xjog,len,jog:extended;

begin
  if gaunt=True
     then begin
            ks:=0;
            case aq of
                  2: ys:=h;          // trg
                  3: ys:=h+g;        // srg
                 10: ys:=h-j;        // tro
                 11: ys:=h+g+j;      // sro
                else run_error(66);
            end;//case
            EXIT;
          end;

  if (do_joggle=True) and (switch_type<>0) then run_error(155); // planing should only come here for curved switches.

  len:=stox-setx;   // length of planing for joggle.

  if (do_joggle=True) and ( (aq=3) or (aq=11) ) and (len>minfp)
     then begin
            xjog:=xs-setx;              // length along planing.
            if xjog>=len
               then jog:=0                            // beyond end of planing.
               else jog:=(len-xjog)/len*joggle_deep;  // run out the joggle. 0.71.a 13-4-01.
          end
     else jog:=0;    // no joggle offset.

  x:=xs-sworgx;                 // x from rad centre.

  case aq of                  // which rail ?
            2: rsw:=swrad;             // trg radius.
            3: rsw:=swrad-g;           // srg radius.
           10: rsw:=swrad+j;           // tro radius.
           11: rsw:=swrad-g-j;         // sro radius.
          else run_error(67);
  end;//case

  temp:=SQR(rsw)-SQR(x);

  if temp<minfp                      // prevent sqrt of negative or zero.
     then begin
            abandon_calcs:=True;      // we have a fly in the ointment.
            EXIT;
          end
     else ys:=jog+sworgy-SQRT(temp);  // calc switch curve.

  if (ABS(rsw)<minfp) or (Abs(x)>(Abs(rsw)-minfp))    // prevent division by zero or ARCSIN error.
     then begin
            abandon_calcs:=True;      // we have a different fly in the ointment.
	    EXIT;
	  end
     else ks:=ARCSIN(x/rsw);          // calc gradient angle (ignore joggle).
end;
//_______________________________________________________________________________________________________________________________

procedure double_curved_planing(aq:integer);

begin
//      nyi
end;
//__________________________________________________________________________________________

function strails(aq:integer):boolean;     //   all straight rails.

var
  stxmax:extended;
  bnw,xbn:extended;

begin
  RESULT:=False;   // default init.

  try

    if plain_track=False
       then begin
              if main_road_i<>0 then stxmax:=xorg+main_road_endx      // stop at end of main-road exit  217a
                                else stxmax:=turnoutx;                // stop at end of template.
            end
       else begin
              stxmax:=xorg-pt_blank;         // or stop at rail joint for plain track (approach track) only   - plain track blanking 234d.
              if stxmax<0 then stxmax:=0;
            end;

    case aq of                       // which side of which rail ?

      0,8: begin                 // stock rail.

             if (plain_track=False) and (half_diamond=False) and (joggled=True) and ({cri.}turnout_road_crossing_rail_flag=True)   // mod 0.76.a 27-4-02.
                then begin
                       if aq=0 then ys:=0      // gauge-face aq=0.
                               else ys:=0-j;   // outer edge aq=8.

                       xb:=0;                  // start at datum.
                       xe:=toex-joggle_long;   // end of toe front.
                       dostr(aq,xb,xe,ys);     // fill the list for this part.

                       xb:=toex-inscale;          // now do the joggle (extends 1" arbitrary beyond toe).
                       xe:=plox;                  // end of planing.
                       dostr_joggle(aq,xb,xe,ys); // fill the list for this part.

                       xb:=plox;               // end of joggle.
                       xe:=stxmax;             // continue to end of turnout.
                     end
                else begin      //   no joggle, ys constant for this rail.

                       if aq=0 then ys:=0      // gauge-face aq=0.
                               else ys:=0-j;   // outer edge aq=8.

                       if half_diamond=True
                          then begin
                                 if aq=0 then xb:=toex      // half diamond switch starts at toe.
                                         else xb:=toeoxhd;  // aq=8.
                               end
                          else xb:=0;           // start at datum.
                       xe:=stxmax;              // end of turnout.
                     end;
            end;

        1: begin   //  main road crossing rail.

             ys:=g;              //  gauge face. ys constant.

             if isolated_crossing=True   // 217a
                then begin
                       xb:=wingcx_minus-scale/6;     // 2" arbitrary
                       xe:=wingcx_minus;             // end at start of knuckle_rad.
                     end
                else begin

                       if (half_diamond=True) and (fixed_diamond=True)   //  fixed-diamond
                          then begin

                                 // 227a  blunt K-nose is 1/2" for both FB and BH (some companies 3/4", GWR is 11/16")

                                   // first do blunt nose..

                                 bnw:=0.5*inscale;  // width of blunt nose

                                   // leave K-crossing flangeway...

                                 if tradius_is_straight=True                        // calc blunt nose...
                                    then xbn:=setx+fw/SIN(hdk)+bnw/TAN(hdk)                                // x to blunt nose
                                    else xbn:=torgx+SQRT(SQR(tradius-g+fw)-SQR(torgy-g-bnw))*SGZ(tradius); // x to blunt nose

                                 if (xbn>startx) and (xbn<turnoutx)
                                    then if f28000(aq,xbn,g+bnw)=1 then EXIT;  // first put actual blunt nose mark at start of aq=1 list.

                                 xb:=xbn;        // then gauge-face of rail.

                                 xe:=wingcx_minus;   // 214a end at start of knuckle_rad.
                               end
                          else begin                 //  normal switch or switch-diamond.

                                 if gaunt=True
                                    then xb:=0       //  gaunt starts at rail joint.
                                    else xb:=setx;   //  start gauge face at "set" in stock rail.

                                 xe:=stox;           //  end first section at end of planing.    !! mod 18-8-01. 0.73.a - for better blade infill printing.
                                 dostr(aq,xb,xe,ys); //  fill the list for this part.

                                 xb:=stox;           //  main part of main road turnout rail.

                                 xe:=wingcx_minus;   // 214a end at start of knuckle_rad.

                               end;
                     end;
           end;

        9: begin                  //  main-road crossing rail
             ys:=g+j;             //  outer edge.  ys constant.

             if isolated_crossing=True   // 217a
                then begin
                       xb:=wingcox_minus-scale/6;     // 2" arbitrary
                       xe:=wingcox_minus;             // end at start of knuckle_rad.
                     end
                else begin
                       if (half_diamond=True) and (fixed_diamond=True)            //  fixed-diamond
                          then begin
                                 if tradius_is_straight=True
                                    then xb:=stox+fw/SIN(hdk)  //  leave K-crossing flangeway.
                                    else xb:=torgx+SQRT(SQR(tradius-g+fw)-SQR(torgy-g-j))*SGZ(tradius);   //^^^
                               end
                          else begin
                                 if gaunt=True
                                    then xb:=0       //  gaunt starts at rail joint.
                                    else xb:=stox;   //  start normal outer edge.
                               end;

                       xe:=wingcox_minus;   // 214a end at start of knuckle_rad.
                     end;
           end;


   16..23: begin   // 0.93.a mods ...

             xb:=0;         //  start at datum. init, changed later for platforms, trackbed
             xe:=stxmax;    //  end of turnout. init, changed later for platforms, trackbed

             if adjacent_edges=True    // platforms and trackbed edges ...
                then begin
                       case aq of

                          16: begin  // TS platform rear edge

                                xb:=platform_ts_start_mm;
                                if xb<0 then xb:=0;

                                if platform_ts_length_mm<0
                                   then xe:=stxmax
                                   else xe:=xb+platform_ts_length_mm;

                                xb:=xb-platform_ts_start_skew_mm;      // 207a
                                xe:=xe+platform_ts_end_skew_mm;        // 207a
                                if xe<xb then xe:=xb;                  // 207a
                                if xb>xe then xb:=xe;                  // 207a

                                ys:=0; // calculated in dostr
                              end;

                          17: begin  // TS platform near edge.

                                xb:=platform_ts_start_mm;
                                if xb<0 then xb:=0;

                                if platform_ts_length_mm<0
                                   then xe:=stxmax
                                   else xe:=xb+platform_ts_length_mm;

                                ys:=g/2+platform_ts_front_edge_ins*inscale;
                              end;

                          18: begin                                         // TS trackbed
                                xb:=trackbed_ts_start_mm;        // 215a ...
                                if xb<0 then xb:=0;

                                if trackbed_ts_length_mm<0
                                   then xe:=stxmax
                                   else xe:=xb+trackbed_ts_length_mm;

                                ys:=g/2+trackbed_ts_width_ins*inscale;        // TS trackbed width      215a
                              end;

                          19: begin
                                xb:=trackbed_ts_start_mm;        // 215a ...
                                if xb<0 then xb:=0;

                                if trackbed_ts_length_mm<0
                                   then xe:=stxmax
                                   else xe:=xb+trackbed_ts_length_mm;

                                if draw_ts_trackbed_cess_edge=True
                                   then ys:=g/2+(trackbed_ts_width_ins+cess_ts_width_ins)*inscale    // show TS cess width  215a
                                   else ys:=g/2+trackbed_ts_width_ins*inscale+j/2;                   // cutting edge mark is half rail-width wide
                              end;

                          20: begin  // MS platform rear edge

                                xb:=platform_ms_start_mm;
                                if xb<0 then xb:=0;

                                if platform_ms_length_mm<0
                                   then xe:=stxmax
                                   else xe:=xb+platform_ms_length_mm;

                                xb:=xb-platform_ms_start_skew_mm;      // 207a
                                xe:=xe+platform_ms_end_skew_mm;        // 207a
                                if xe<xb then xe:=xb;                  // 207a
                                if xb>xe then xb:=xe;                  // 207a

                                ys:=0; // calculated in dostr
                              end;

                          21: begin  // MS platform near edge.

                                xb:=platform_ms_start_mm;
                                if xb<0 then xb:=0;

                                if platform_ms_length_mm<0
                                   then xe:=stxmax
                                   else xe:=xb+platform_ms_length_mm;

                                ys:=g/2-platform_ms_front_edge_ins*inscale;
                              end;


                          22: begin                                         // MS trackbed
                                xb:=trackbed_ms_start_mm;       // 215a ...
                                if xb<0 then xb:=0;

                                if trackbed_ms_length_mm<0
                                   then xe:=stxmax
                                   else xe:=xb+trackbed_ms_length_mm;

                               ys:=g/2-trackbed_ms_width_ins*inscale;        // MS trackbed width      215a
                              end;

                          23: begin
                                xb:=trackbed_ms_start_mm;       // 215a ...
                                if xb<0 then xb:=0;

                                if trackbed_ms_length_mm<0
                                   then xe:=stxmax
                                   else xe:=xb+trackbed_ms_length_mm;

                                if draw_ms_trackbed_cess_edge=True
                                   then ys:=g/2-(trackbed_ms_width_ins+cess_ms_width_ins)*inscale    // show MS cess width     215a
                                   else ys:=g/2-trackbed_ms_width_ins*inscale-j/2;                   // cutting edge mark is half rail-width wide
                              end;

                       end;//case

                     end
                else begin             // adjacent tracks ...
                       case aq of

                         16: ys:=trtscent;      //  gauge-face, turnout side adjacent track near rail.
                         17: ys:=trtscent-j;    //  ditto outer-edge.

                         18: ys:=trtscent+g;    //  gauge-face, ditto far rail.
                         19: ys:=trtscent+g+j;  //  ditto outer-edge.

                         20: ys:=g-trmscent;    //  gauge-face, main side adjacent track near rail.
                         21: ys:=g-trmscent+j;  //  ditto outer-edge.

                         22: ys:=0-trmscent;    //  gauge-face, ditto far rail.
                         23: ys:=0-trmscent-j;  //  ditto outer-edge.

                       end;//case
                     end;

           end;//16..23

       24: begin     // track centre-line...

             ys:=get_cl_offset;   // added 206a

             xb:=0;
             xe:=stxmax;
           end;

      else run_error(29);

    end;//case

    dostr(aq,xb,xe,ys);    //  now fill the list, (or remaining part e.g. if joggled stock rails).
    RESULT:=True;

  except
    EXIT;          // f.p. errors, do nothing in list.
  end;//try
end;
//______________________________________________________________________________

function aq25offset(xs:extended; var k:extended):extended;   // return offset at xs on turnout road centre-line.
                                                             // also return the angle in k.

   // 226c   see also procedure turnroad_cl;     // turnout road track centre-line.

var
  temp:extended;
  x,xlim,rto:extended;

  curve_endx:extended;

  curve_changex,y_rail,ks:extended;  // 226c

                    ////////////////////////////////////////////////////////////

                    procedure do_gaunt;

                    begin
                      RESULT:=g/2+h;
                      k:=0;
                    end;
                    ////////////////////////////////////////////////////////////

                    procedure do_parallel_xing;

                    begin
                      if xs<retx
                         then begin
                                RESULT:=g/2+(xs-dpx)/k3n;   // straight section beyond crossing point.
                                k:=k3;
                                EXIT;
                              end;

                      if xs>retrorgx
                         then begin
                                RESULT:=trtscent+g/2;        // parallel section beyond return curve.
                                k:=0;
                                EXIT;
                              end;

                      if retr<minfp then retr:=minfp;
                      temp:=(retrorgx-xs)/retr;
                      if ABS(temp)<1.0 then k:=ARCSIN(temp)
                                       else k:=Pi/2;

                      RESULT:=retrorgy+retr*COS(k);        // return curve section.
                    end;
                    ////////////////////////////////////////////////////////////

                    procedure do_turnout_curve;

                    begin
                      rto:=tradius-g/2;

                      if ABS(rto)<minfp      // abandon ship ???
                         then begin
                                abandon_calcs:=True;
                                RESULT:=0;
                                k:=0;
                                EXIT;
                              end;

                      xlim:=ABS(rto)*0.98;   // 0.98*radius, arbitrary x limits to turnout curve.  0.79.a

                      repeat
                        if xs>(torgx+xlim)
                           then begin
                                  x:=xlim;   // forward limit x from rad centre.
                                  BREAK;
                                end;

                        if xs<(torgx-xlim)
                           then begin
                                  x:=0-xlim; // backward limit x from rad centre.
                                  BREAK;
                                end;

                        x:=xs-torgx;         // ok, x from rad centre.
                        BREAK;
                      until 0<>0;

                      try
                        temp:=SQR(rto)-SQR(x);
                        RESULT:=torgy-SQRT(temp)*SGZ(rto);   // calc turnout curve.
                        k:=ARCSIN(x/rto);                    // and gradient angle.

                      except
                        abandon_calcs:=True;             // abandon ship. Cannot be if x is under 90 degrees ?
                        RESULT:=0;
                        k:=0;
                      end;//try
                    end;
                    ////////////////////////////////////////////////////////////

                    procedure do_regular_beyond_curve_end;

                    begin

                      if xs<tcpx        // in front of crossing, for regular, parallel
                         then begin
                                RESULT:=g/2+(xs-dpx)/k3n;       // straight centre-line from CESP to crossing.
                                k:=k3;
                                EXIT;
                              end;

                      if (xing_calc_i=0) and (retpar_i=1)       // parallel crossing...
                         then begin
                                do_parallel_xing;
                                EXIT;
                              end;

                        // regular or generic crossing beyond TCP...

                      RESULT:=g/2+(xs-dpx)/k3n;   // straight section beyond crossing.
                      k:=k3;
                    end;
                    ////////////////////////////////////////////////////////////

begin
  RESULT:=g/2;    // init defaults
  k:=0;

  if (half_diamond=True) or (gaunt=True)
     then curve_changex:=0
     else curve_changex:=torgx+(tradius-g/2)*SIN(k2);  // 226c  switch curve end / turnout curve start

  if (gaunt=True) and (xs<torgx)
     then begin
            do_gaunt;
            EXIT;
          end;

  if (plain_track=True)
  or ( (half_diamond=False) and (xs<toemidx) )     // 226c approach/switch front
     then begin
            RESULT:=g/2;
            k:=0;
            EXIT;
           end;

  if xs<curve_changex      // 226c switch section added  (small error on k compared with true radial result)
     then begin
            y_rail:=aq3offset(xs,ks);   // 226c get offset to stock rail and angle for switch section
            RESULT:=y_rail-g/2/COS(ks);
            k:=ks;
            EXIT;
          end;


  if xing_calc_i=1     // curviform crossing, all templates.
     then begin
            do_turnout_curve;  // turnout curve runs through
            EXIT;
          end;

  if tradius_is_straight=True // regular diamonds, straight line from DP to TCP ...
     then begin
            do_regular_beyond_curve_end;
            EXIT;
          end;

  curve_endx:=torgx+(tradius-g/2)*SIN(k3);  // turnout curve end. CESP or TCP.

  if xs<curve_endx
     then begin
            do_turnout_curve;
            EXIT;
          end;

  do_regular_beyond_curve_end;

end;
//___________________________________________________________________________________________

procedure turnroad_cl;            // turnout road track centre-line.

var
  xs,ys,ks,xe:extended;

begin
  if plain_track=True then EXIT;

  if (half_diamond=True) or (gaunt=True)         // mods 213a
     then xs:=blank_start(0)                     // start at origin
     else xs:=blank_start(toemidx);              // turnout start

  case turnout_road_i of       // 209a

        -1: xe:=txpx;     // stop at crossover mid-point.

       0,1: begin
              if retpar_i=1 then begin                                     // parallel crossing...
                                   if turnout_road_i=1 then xe:=turnoutx   // long turnout road - stop at end of turnout.
                                                       else xe:=trpx;
                                 end
                            else xe:=vendx;     // stop at end of vee rail.
            end;

       2,3: if turnoutx>(xorg+min_turnout_road_endx) then xe:=xorg+turnout_road_endx      // 2=adjustable turnout road      3 added  217a
                                                     else xe:=turnoutx;

       else xe:=turnoutx;  // ???

  end;//case

  if (turnout_road_i<2) and (xe>turnoutx) then xe:=turnoutx;   // 209a           was <>2 217a

  if xs>(xe-minfp) then EXIT;  // !!! 0.76.a  18-1-02.

  repeat
    if xs>xe then xs:=xe;      // ensure we hit the end.

    ys:=aq25offset(xs,ks);     // get offset to centre-line and angle.

    if (f28000(25,xs,ys)=1) or (xs>=xe) then BREAK;  // do curving on xs,ys and put in array (aq=25).
    xs:=xs+incx;                                     // increment xs.
  until 0<>0;
end;
//______________________________________________________________________________

procedure knuckle(aq:integer);     // knuckle radius  214a

var
  dummy:integer;


                    ////////////////////////////////////////////////////////////

                    procedure do_kuckle_curve(aq:integer; kr,xb,xe:extended);            // do a turnout curve.

                    var
                     xs,ys,temp:extended;

                    begin
                      if xb>turnoutx then EXIT;             // !!! mod 26-3-99
                      if xe>turnoutx then xe:=turnoutx;     // !!! mod 26-3-99

                      xs:=blank_start(xb);
                      if xs>(xe-minfp) then EXIT;  // !!! 0.76.a  18-1-02.

                      ys:=0;    // keep compiler happy..
                      temp:=0;

                      repeat
                        if xs>xe then xs:=xe;  // ensure we hit the end.

                        case aq of

                          1,31,9,39: begin                        // main road
                                       temp:=SQR(kr)-SQR(xs-xb);  // SQRT protection
                                       if temp<minfp then EXIT;
                                     end;

                         2,32,10,40: begin                        // turnout road
                                       temp:=SQR(kr)-SQR(xe-xs);  // SQRT protection
                                       if temp<minfp then EXIT;
                                     end;
                        end;//case

                        case aq of

                          1,31: ys:=g+kr-SQRT(temp);      // main road rail, gauge-face
                          9,39: ys:=g+j+kr-SQRT(temp);    // main road rail, outer edge

                          2,32: ys:=g-fw-kr+SQRT(temp);      // turnout road rail, gauge-face
                         10,40: ys:=g-fw-j-kr+SQRT(temp);    // turnout road rail, outer edge

                        end;//case

                        if (f28000(aq,xs,ys)=1) or (xs>=xe) then BREAK;      // fill list.

                        xs:=xs+inscale;    // 1 inch steps on x
                      until 0<>0;
                    end;
                    ////////////////////////////////////////////////////////////

begin

  case aq of

    1,31: do_kuckle_curve(aq,knuck_rad,wingcx_minus,wingcx_plus);      // main road rail, gauge-face
    9,39: do_kuckle_curve(aq,knuck_rad-j,wingcox_minus,wingcox_plus);  // main road rail, outer edge

    2,32: do_kuckle_curve(aq,knuck_rad,wingx_minus,wingx_plus);      // turnout road rail, gauge-face
   10,40: do_kuckle_curve(aq,knuck_rad-j,wingox_minus,wingox_plus);  // turnout road rail, outer edge

  end;//case
end;
//______________________________________________________________________________

procedure cuwing(aq:integer);      // turnout-side wing rail.

  // 0.93.a also main-side K check rail

begin
         // 0.93.a  // mods 0.79.b 23-09-04...

  if aq=26       // K-crossing MS check rail gauge-face...
     then begin
            xb:=kckmsx;       // start at centre knuckle.
            xe:=kckmsflx;     // do up to flare start.
            docrossing(aq,{fl,}xb,xe,1);

            xb:=xe;
            xe:=kckmsflendx;  // stop at end of flare.
            docrossing(aq,{fl,}xb,xe,1);

            EXIT;
          end;

  if aq=27       // K-crossing MS check rail outer-edge...
     then begin
            xb:=kckmsox;       // start at centre knuckle.
            xe:=kckmsflox;     // do up to flare start.
            docrossing(aq,{fl,}xb,xe,1);

            xb:=xe;
            xe:=kckmsflendox;  // stop at end of flare.
            docrossing(aq,{fl,}xb,xe,1);

            EXIT;
          end;

      // mods end

  if xing_calc_i=-1        // generic crossing.
     then begin            // first do short curve to f.p. :
            case aq of     // which rail-line ?

                 1: begin                   // straight turnout rail, gauge-face.
                      xb:=wingcx_plus;      // 214a start at end of knuckle_rad.

                      xe:=wingcfpx;         // stop curve opposite f.p.
                      doradcurve(aq,xb,xe);

                      xb:=wingcfpx;
                      xe:=flarecx;              // !!! 30-5-00  do up to flare start.
                      docrossing(aq,xb,xe,1);

                      xb:=flarecx;
                      xe:=flcendx;              // stop at end of wing rail.
                      docrossing(aq,xb,xe,1);

                    end;

                 9: begin                    // ditto, outer-edges.
                      xb:=wingcox_plus;      // 214a start at end of knuckle_rad.

                      xe:=wingcfpox;
                      doradcurve(aq,xb,xe);

                      xb:=wingcfpox;
                      xe:=flarecox;
                      docrossing(aq,xb,xe,1);

                      xb:=flarecox;
                      xe:=flcendox;
                      docrossing(aq,xb,xe,1);

                    end;

               else run_error(31);
            end;//case
          end//then

     else begin                            // normal straight or curviform V-crossing :
            case aq of                     // which rail-line ?

                 1: begin                  // straight turnout rail, gauge-face.
                      xb:=wingcx_plus;     // 214a start at end of knuckle_rad.

                      xe:=flarecx;                // !!! 30-5-00  do up to flare start.
                      docrossing(aq,xb,xe,1);

                      xb:=xe;
                      xe:=flcendx;               // stop at end of wing rail.
                      docrossing(aq,xb,xe,1);
                    end;

                 9: begin                    // ditto, outer-edges.
                      xb:=wingcox_plus;      // 214a start at end of knuckle_rad.
                      
                      xe:=flarecox;
                      docrossing(aq,xb,xe,1);

                      xb:=xe;
                      xe:=flcendox;
                      docrossing(aq,xb,xe,1);
                    end;
               else run_error(32);
            end;//case
          end;//else
end;
//______________________________________________________________________________

procedure retrails(aq:integer);       // return curve rails.

var
  xb,retrmod:extended;

begin
  if xing_calc_i=1 then run_error(33); // should not be here for curviform V-crossing.

  xb:=0;                   // to keep the compiler happy.
  retrmod:=0;              // ditto.

  if turnout_road_i=1 then xe:=turnoutx       // long turnout road - rails full length.
                      else xe:=trpx;          // end turnout road at return point.

  case aq of               // which rail ?

       5: begin            // vee rail:
            xb:=vendx;
            retrmod:=0-g/2;       // gf
          end;

      13: begin
            xb:=vendox;
            retrmod:=0-g/2-j;     // oe
          end;

       3: begin            // curved stock rail:
            xb:=csrendx;
            retrmod:=g/2;       // gf
          end;

      11: begin
            xb:=csrendox;
            retrmod:=g/2+j;     // oe
          end;

     else run_error(33);
  end;//case

  if xb<xe then doreturn(aq,xb,xe,retrmod);   // go do return curve. (unless turnout too short).

end;
//______________________________________________________________________________

procedure curails(aq:integer);    //  central curved part of turnout road.

var
  do_joggle:boolean;
  plox_done:boolean;
  bnw,xbn:extended;
  segment_index:integer;

         // fill the curved rail lists.
begin
  for segment_index:=0 to 3 do begin      // !!! not to 4, uses index+1...

    xb:=seg_termx[segment_index,aq];      // start x.
    xe:=seg_termx[segment_index+1,aq];    // end x.

    if xb=nodraw then CONTINUE;   // xb = nodraw, nothing to do yet, next segment.

    if xe=nodraw then EXIT;  // nothing more on this rail.

    if (aq=2) and (xe>wingx_minus) then xe:=wingx_minus;       // 214a mods for knuckle bend
    if (aq=10) and (xe>wingox_minus) then xe:=wingox_minus;    // 214a mods for knuckle bend

    if xb>turnoutx then EXIT;  // nothing more on this rail.

    if xb>xe then CONTINUE;    // 217a

    if (plain_track=False) and (joggled=True) and ({cri.} main_road_crossing_rail_flag=True) and ( (aq=3) or (aq=11) )   // joggled turnout-side stock rail  v:0.71.a . mod v:0.76.a 27-4-02.
       then begin
              do_joggle:=True;
              if segment_index=0 then xe:=xe-joggle_long;  // v:0.71.a  13-4-01. stop stock front at joggle.
              if segment_index=1 then xb:=xb-inscale;      // v:0.71.a  13-4-01. start full joggled depth 1" in front of blade tips (arbitrary).
            end
       else do_joggle:=False;

    if (aq=2) and (segment_index=3) and (half_diamond=True) and (fixed_diamond=True)
       then begin
                  // diagonal road crossing rail
                  // leave K-crossing flangeway...
                  // blunt K-nose is 1/2" for both FB and BH (some companies 3/4", GWR is 11/16")

                  // first do blunt nose..

              bnw:=0.5*inscale;  // 227a (bn_wide-0.125)*inscale;    // width of blunt nose.

              xbn:=xb+bnw*SIN(hdk/2);

              if (xbn>startx) and (xbn<turnoutx)
                 then begin
                        if f28000(aq,xbn,fw)=1 then EXIT;  // first put blunt nose at start of aq=2 list.
                      end;
            end;

    if isolated_crossing=True    // 217a
       then begin
              if aq=2 then xs:=blank_start(wingx_minus-scale/6)                  // 2" arbitrary
                      else if aq=10 then xs:=blank_start(wingox_minus-scale/6)
                                    else xs:=blank_start(xb);
            end
       else xs:=blank_start(xb);      // start xs from xb.

    if xs>xe then CONTINUE;   // next segment.

    if (half_diamond=True) and (fixed_diamond=True)
       then plox_done:=True             // need full blade infill for fixed diamond. 25-8-02 0.77.a.
       else plox_done:=False;           // init 18-8-01.

    if (joggled=True) and (aq=2) and (xs=toex)   // start aq=2 curved switch blade with the blade tip mark if joggled.
       then begin
              if f28000(aq,xs,0-joggle_deep)=1 then EXIT;  // do curving on xs,ys and put in array.
            end;

    if xe>turnoutx
       then begin
              if (turnout_road_i>1) and (xing_calc_i=1)                            // 209a mods   curviform crossing           was turnout_road_i=2 217a
                 then begin                                                        // adjustable..
                        if ((aq<>3) and (aq<>11)) or (turnoutx<(xorg+min_turnout_road_endx))
                           then xe:=turnoutx;                                      // all other rails stop at end of template
                      end
                 else xe:=turnoutx;      // stop at end of template
            end;

    repeat
                  // !!! mods 18-8-01 0.73.a to ensure plox occurs in the list (for neater blade infill on printing)...

      if (aq=2) and (segment_index=2) and (switch_type=0) and (xb<plox) and (xs>plox) and (plox_done=False)   // planing and switch curve all in one for straight and curved switches...
         then begin
                xs:=blank_start(plox);
                plox_done:=True;
              end;

                  // calc ys at this xs and put in list...

      if xs>xe then xs:=xe;     // ensure final point is hit.

      case segment_index of     // get ys (global)..

               0: toe(aq);
               1: case switch_type of
                      -1: straight_planing(aq,do_joggle); // semi-curved switch.

                       0: swcurve(aq,do_joggle);      // for straight switch or curved switch, the planing is part of the switch curve.
                                                      // so this section draws only the joggle length in front of the toe (if joggled, and nothing otherwise, xb=xe).

                       1: double_curved_planing(aq);  // double-curved switch planing.
                     else run_error(68);
                  end;//case

               2: swcurve(aq,(do_joggle=True) and (switch_type=0));  // this section includes planing for curved and straight switches.

               3: ys:=radcurve(aq,xs);

             else run_error(69);
      end;//case

      if f28000(aq,xs,ys)=1 then EXIT;     // go curve and put in list. EXIT if list full for this aq.
                                           //  !!! ys global?

      if ((half_diamond=False) or (fixed_diamond=False)) and (aq=2) and (xs<(plox+minfp{plox+incx mod 18-8-01})) then list_planing_mark_aq2:=nlmax_array[aq]; // mod 25-8-98: keep note of where end of planing is in the list.
                                                                                                                                    // (curved turnout rail - turnout road blade), but not for a fixed-diamond.

      if xs>=xe then BREAK;    // segment done.
      xs:=xs+incx;
    until 0<>0;           // loop.

  end;// next segment_index.
end;
//_______________________________________________________________________________________

procedure stxrail(aq:integer{; fl:extended});       // straight crossing part of curved turnout rail.

begin

  case aq of

       2: begin                       // turnout rail, gauge-face:
            xb:=seg_termx[4,aq];      // start at end of curved part.
            xe:=wingx_minus;          // 214a end at start of knuckle bend
          end;

      10: begin                       // ditto, outer-edge:
            xb:=seg_termx[4,aq];
            xe:=wingox_minus;         // 214a end at start of knuckle bend
          end;

     else run_error(34);
  end;//case

  if xe>xb then docrossing(aq,xb,xe,1);

end;
//______________________________________________________________________________

procedure stwing(aq:integer);   // do main-side wing rail.

begin
  case aq of                     // which edge ?

       2: begin
            ys:=g-fw;       // wing rail gauge face.

            xb:=wingx_plus;        // 214a start at end of knuckle bend

            xe:=wflarex;         // start of flare-out.
            dostr(aq,xb,xe,ys);  // fill list for parallel part.
            xb:=xe;
            xe:=wingendx_ms;                                                                         // end of wing rail.
            stflare(aq,flen_mr,k5_mr,xb,xe,ys,1,-1,(get_flare_type(ccd.end_diff_mr.type_diff)=0));   // fill list for flare-out.
          end;

      10: begin
            ys:=g-fw-j;     // ditto, outer edge.

            xb:=wingox_plus;        // 214a start at end of knuckle bend

            xe:=wflarox;
            dostr(aq,xb,xe,ys);  // fill list for parallel part.
            xb:=xe;
            xe:=wingendox;
            stflare(aq,flen_mr,k5_mr,xb,xe,ys,1,-1,(get_flare_type(ccd.end_diff_mr.type_diff)=0));
          end;
     else run_error(35);
  end;//case
end;
//______________________________________________________________________________

procedure doreturn(aq:integer; xb,xe,radmod:extended);              // do return curve

var
  xs,ys:extended;

begin
  xs:=blank_start(xb);

  if xe>turnoutx then xe:=turnoutx;     // stop at end of turnout. v:0.76.a  28-4-02

  if xs>(xe-minfp) then EXIT;  // !!! 0.76.a  18-1-02.

  repeat
    if xs>xe then xs:=xe;                             // ensure we hit the end.
    ys:=returncurve(xs,radmod);
    if abandon_calcs=True then EXIT;                  // snag in the calc of ys.

    if (f28000(aq,xs,ys)=1) or (xs>=xe) then BREAK;    // fill list.
    xs:=xs+incx;
  until 0<>0;
end;
//_______________________________________________________________________________________________________________________________

procedure docrossing(aq:integer; xb,xe:extended; flway:integer);

  // stcurails and cuckrail come here also.
  // K-crossing MS check rails also.

var
  xs,ys,xend:extended;

begin
  if isolated_crossing=True    // 217a
     then begin
            if aq=2 then xs:=blank_start(wingx_minus-scale/6)                  // 2" arbitrary
                    else if aq=10 then xs:=blank_start(wingox_minus-scale/6)
                                  else xs:=blank_start(xb);
          end
     else xs:=blank_start(xb);      // start xs from xb.

  xend:=xe;            // for wing angle calcs.

  if xe>turnoutx
     then begin
            if turnout_road_i<2      // 209a mods  not adjustable            was turnout_road_i<>2 216c
               then xe:=turnoutx      // stop at end of template
               else begin                       // adjustable..
                      if (aq<>5) and (aq<>13) and (aq<>3) and (aq<>11)   // 209a not turnout exit rails
                         then xe:=turnoutx;                              // all other rails stop at end of template
                    end;
          end;

  if xs>(xe-minfp) then EXIT;  // !!! 0.76.a  18-1-02.

  ys:=0;               //  to keep the compiler happy.

  repeat
    if xs>xe then xs:=xe;              // ensure we hit the end.

    case aq of
       1,9,26,27: ys:=turnoutst_wing(aq,xb,xend,xs);   // go calc TS wing rail (mod 27-5-01 0.71.a).

            7,15: ys:=turnoutst_check(aq,xb,xend,xs);  // go calc TS check rail (mod 27-5-01 0.71.a).

             else begin

                    if xing_calc_i<>1
                       then ys:=turnoutst_main(aq,xb,xend,xs)      // go calc straight crossing.
                       else ys:=radcurve(aq,xs);                   // go calc curved crossing part.
                  end;
    end;//case

    if abandon_calcs=True then EXIT;                   // snag in the calc of ys.

    if (f28000(aq,xs,ys)=1) or (xs>=xe) then BREAK;    // fill list.
    xs:=xs+incx;
  until 0<>0;
end;
//_______________________________________________________________________________________________________________________________

procedure dostr(aq:integer;xb,xe,ys:extended);       // fill all straight rails.

var
  xs:extended;        //  enter with constant ys for straight rails (except K-crossing checks, platform rear edges).
  stxmax:extended;
  xxs,yys:extended;
  sp,w1,w2,wnow:extended;

  y_at_aq3,k_at_aq3,aq3_endx:extended;  // 207b
  aq3_done:boolean;                     // 207b

begin
  yys:=ys;  // for all except K-crossing check rails, platform rear edges, trackbed edges.

  if plain_track=False then stxmax:=turnoutx     // stop at end of turnout.
                       else begin
                              stxmax:=xorg-pt_blank;           // or stop at rail joint for plain track (approach track) only   - plain track blanking 234d.
                              if stxmax<0 then stxmax:=0;
                            end;

  if xb>stxmax then EXIT;        // !!! mod 26-3-99
  if xe>stxmax then xe:=stxmax;  // !!! mod 26-3-99

  xs:=blank_start(xb);
  if xs>(xe-minfp) then EXIT;  // !!! 0.76.a  18-1-02.

  if (plain_track=False) and (joggled=True) and (aq=1) and (xs=setx)   // start the aq=1 straight switch blade with the blade tip mark if joggled.
     then begin
            if f28000(aq,xs,yys+joggle_deep)=1 then EXIT;
          end;

  aq3_done:=False;  // init for trackbed edges

  if ((aq=16) or (aq=17)) and (plain_track=False) and (adjacent_edges=True)        // TS platform rear/front
     then begin
            if (xe>tvjpx) and (xe<=(mvjpx+scale*3)) then xe:=tvjpx;   // end at TVJP if not enough exit track (3ft arbitrary).
          end;

  repeat
    if xs>xe then xs:=xe;                           // ensure we hit the end.
    if abandon_calcs=True then EXIT;                // snag in the calc of ys.

        // temp assume regular diamond ..

    if (aq=26) or (aq=27) then yys:=ys+(xs-xb)/hdkn;   // K-crossing check rails (working part).

    if adjacent_edges=True      // mod straight edges
       then begin

              if aq=16           // rear edge of TS platform
                 then begin
                        w1:=platform_ts_start_width_ins*inscale;
                        w2:=platform_ts_end_width_ins*inscale;

                        sp:=platform_ts_front_edge_ins*inscale-g/2;    // rail to front edge

                        if ABS(xe-xb)<minfp     // no div by zero
                           then wnow:=sp+w1                            // rail to rear edge
                           else wnow:=sp+w1+(xs-xb)*(w2-w1)/(xe-xb);

                        if wnow<sp then wnow:=sp;

                        if (plain_track=False) and (xs>=(mvjpx-incx/2)) and (xs<=(mvjpx+incx/2))   // lock list step to avoid juddering
                           then xs:=mvjpx;

                        if (plain_track=True) or (xs>mvjpx)    // no turnout road, or on exit track
                           then yys:=g+wnow
                           else begin
                                  y_at_aq3:=aq3offset(xs,k_at_aq3);

                                  yys:=y_at_aq3+wnow*COS(k_at_aq3);
                                  xxs:=xs-wnow*SIN(k_at_aq3);

                                  if (f28000(aq,xxs,yys)=1) or (xxs>=xe) then BREAK;  // do curving on xxs,yys and put in array.

                                  xs:=xs+incx;        // increment xs.

                                  CONTINUE;  // array done
                                end;
                      end;

              if aq=20          // rear edge of MS platform
                 then begin
                        w1:=platform_ms_start_width_ins*inscale;
                        w2:=platform_ms_end_width_ins*inscale;
                        sp:=platform_ms_front_edge_ins*inscale-g/2;        // 215a  sp from rail

                        if ABS(xe-xb)<minfp     // no div by zero
                           then yys:=0-sp-w1
                           else yys:=0-sp-w1-(xs-xb)*(w2-w1)/(xe-xb);

                        if yys>(0-sp) then yys:=0-sp;  // 207a rear edge can't go in front of front edge
                      end;


              if (aq=17) and (plain_track=False)    // 207b TS platform front edge, modify ys to follow turnout curve...
                 then begin

                        aq3_endx:=mvjpx;

                        if (xs>=(aq3_endx-incx/2)) and (xs<=(aq3_endx+incx/2))
                           then xs:=aq3_endx;        // lock list step

                        if xs<=aq3_endx    // platform edge on turnout curve
                           then begin
                                  y_at_aq3:=aq3offset(xs,k_at_aq3);

                                  yys:=y_at_aq3+(platform_ts_front_edge_ins*inscale-g/2)*COS(k_at_aq3);
                                  xxs:=xs-(platform_ts_front_edge_ins*inscale-g/2)*SIN(k_at_aq3);

                                  if (f28000(aq,xxs,yys)=1) or (xxs>=xe) then BREAK;  // do curving on xxs,yys and put in array.

                                  xs:=xs+incx;        // increment xs.

                                  CONTINUE;
                                end
                           else yys:=ys;    // yys platform edge on exit track

                      end;


              if (aq=18) and (aq3_done=False) and (plain_track=False)    // 215a TS trackbed edge inner, modify ys to follow turnout curve...
                 then begin

                        aq3_endx:=mvjpx+scale*6;  // 215a to arbitrary 6ft beyond rail joints.

                        if (xs>=(aq3_endx-incx/2)) and (xs<=(aq3_endx+incx/2))
                           then begin
                                  xs:=aq3_endx;
                                  aq3_done:=True;
                                end;

                        if (xs>toex) and (xs<=aq3_endx)    // trackbed edge on turnout curve
                           then begin
                                  y_at_aq3:=aq3offset(xs,k_at_aq3);

                                  yys:=y_at_aq3+(trackbed_ts_width_ins*inscale-g/2)*COS(k_at_aq3);
                                  xxs:=xs-(trackbed_ts_width_ins*inscale-g/2)*SIN(k_at_aq3);

                                  if (f28000(aq,xxs,yys)=1) or (xxs>=xe) then BREAK;  // do curving on xxs,yys and put in array.

                                  if aq3_done=True
                                     then begin
                                            xs:=xxs;
                                            yys:=ys;   // reset yys on exit track
                                          end
                                     else xs:=xs+incx;        // increment xs.

                                  CONTINUE;
                                end;
                      end;

              if (aq=19) and (aq3_done=False) and (plain_track=False)    // 215a TS trackbed edge outer, modify ys to follow turnout curve...
                                                                         // go further along curve to ensure an overlap with inner at short angles
                 then begin
                        if draw_ts_trackbed_cess_edge=True
                           then aq3_endx:=mvjpx+scale*6+cess_ts_width_ins*inscale*3/2  // cess arbitrary
                           else aq3_endx:=mvjpx+scale*6+j*3/4;                         // cut line arbitrary

                        if (xs>=(aq3_endx-incx/2)) and (xs<=(aq3_endx+incx/2))
                           then begin
                                  xs:=aq3_endx;
                                  aq3_done:=True;
                                end;

                        if (xs>toex) and (xs<=aq3_endx)
                           then begin
                                  y_at_aq3:=aq3offset(xs,k_at_aq3);

                                  if draw_ts_trackbed_cess_edge=True
                                     then begin
                                            yys:=y_at_aq3+((trackbed_ts_width_ins+cess_ts_width_ins)*inscale-g/2)*COS(k_at_aq3);
                                            xxs:=xs-((trackbed_ts_width_ins+cess_ts_width_ins)*inscale-g/2)*SIN(k_at_aq3);
                                          end
                                     else begin
                                            yys:=y_at_aq3+(trackbed_ts_width_ins*inscale+j/2-g/2)*COS(k_at_aq3);
                                            xxs:=xs-(trackbed_ts_width_ins*inscale+j/2-g/2)*SIN(k_at_aq3);
                                          end;

                                  if (f28000(aq,xxs,yys)=1) or (xxs>=xe) then BREAK;  // do curving on xxs,yys and put in array.

                                  if aq3_done=True
                                     then begin
                                            xs:=xxs;
                                            yys:=ys;   // reset yys on exit track
                                          end
                                     else xs:=xs+incx;        // increment xs.

                                  CONTINUE;
                                end;
                      end;

            end;

    if (f28000(aq,xs,yys)=1) or (xs>=xe) then BREAK;  // do curving on xs,ys and put in array.

    if ((half_diamond=False) or (fixed_diamond=False)) and (aq=1) and (xs<(stox+minfp{stox+incx mod 18-8-01}))
       then list_planing_mark_aq1:=nlmax_array[aq]; // mod 25-8-98: keep note of where end of planing is in the list
                                                    // (straight turnout rail - main road blade) but not for a fixed-diamond.

    xs:=xs+incx;                                    // increment xs.
  until 0<>0;
end;
//______________________________________________________________________________

procedure dostr_joggle(aq:integer;xb,xe,ys:extended);   // fill joggled part of straight stock rail.

var
  xs,x,len,jog:extended;
  stxmax:extended;

begin
  if abandon_calcs=True then EXIT;                // snag in the calc of ys.

  if plain_track=True then EXIT;

  stxmax:=turnoutx;

  if xb>stxmax then EXIT;        // !!! mod 26-3-99
  if xe>stxmax then xe:=stxmax;  // !!! mod 26-3-99

  xs:=blank_start(xb);
  if xs>(xe-minfp) then EXIT;  // !!! 0.76.a  18-1-02.

  repeat
    if xs>xe then xs:=xe;                           // ensure we hit the end.

    len:=plox-toex;   // length of joggle.

    if len>minfp
       then begin
              x:=xs-xb-inscale;              // length along planing (xb is 1" arbitrary in front of toe).
              jog:=(len-x)/len*joggle_deep;  // run out the joggle. 0.71.a 13-4-01.
            end
       else jog:=0;    // !!! (shouldn't be here) no joggle offset.

    if (f28000(aq,xs,ys-jog)=1) or (xs>=xe) then BREAK;  // do curving on xs,ys and put in array.

    xs:=xs+incx;                                    // increment xs.
  until 0<>0;
end;
//________________________________________________________________________________________

procedure stcurail(aq:integer);   // straight crossing part of curved stock rail.

begin
  if plain_track=False            // not needed if plain track only in force.
     then begin
            case aq of

                 3: begin                     // curved stock rail, gauge-face:
                      xb:=seg_termx[4,aq];    // start at end of curved part.

                      if (turnout_road_i>1)       // adjustable turnout road         was turnout_road_i=2 217a
                         then begin
                                if turnoutx>(xorg+min_turnout_road_endx)    // template long enough?
                                   then xe:=csrendx
                                   else xe:=turnoutx;
                              end
                         else xe:=csrendx;
                    end;

                11: begin                     // ditto for outer-edge:
                      xb:=seg_termx[4,aq];

                      if (turnout_road_i>1)       // adjustable turnout road       was turnout_road_i=2 217a
                         then begin
                                if turnoutx>(xorg+min_turnout_road_endx)    // template long enough?
                                   then xe:=csrendox
                                   else xe:=turnoutx;
                              end
                         else xe:=csrendox;
                    end;

               else run_error(36);
            end;//case

            docrossing(aq,{fl,}xb,xe,1);              // go calc and fill list.

          end;//then
end;
//______________________________________________________________________________

procedure stvee(aq:integer);          // straight vee rail.

var
  xb,xe,ys:extended;

  endx:extended;  //217a

begin
  xb:=0;
  xe:=0;
  ys:=0;            //  these to keep the compiler happy.

  if main_road_i<>0 then endx:=xorg+main_road_endx      // stop at end of main-road exit  217a
                    else endx:=turnoutx;                // stop at end of template.

  case aq of

       4: begin                        // gauge-face.
            ys:=g;
            xb:=bnx;
            xe:=endx;   //  217a  turnoutx;
          end;

      12: begin                       // outer edge.
            ys:=g+j;
            xb:=bnox;
            xe:=endx;   //  217a  turnoutx;

          end;

     else run_error(37);
  end;//case

  dostr(aq,xb,xe,ys);                   // fill list.

end;
//_______________________________________________________________________________________________________________________________

procedure cuvee(aq:integer);    // curved vee rail (up to return curve if any).

var
  xb,xe:extended;

begin

  xb:=0;
  xe:=0;           //  these to keep the compiler happy.


  if (turnout_road_i>1) and (turnoutx<(xorg+min_turnout_road_endx))  // 209a ...      was turnout_road_i=2 217a
     then begin
            case aq of

                 5: begin                        // curved vee rail, gauge-face.
                      xb:=bnxx;
                      xe:=turnoutx;
                    end;

                13: begin                       // ditto, outer-edge.
                      xb:=bnox;
                      xe:=turnoutx;
                    end;

               else run_error(38);
            end;//case
          end
     else begin
            case aq of

                 5: begin                        // curved vee rail, gauge-face.
                      xb:=bnxx;
                      xe:=vendx;
                    end;

                13: begin                       // ditto, outer-edge.
                      xb:=bnox;
                      xe:=vendox;
                    end;

               else run_error(38);
            end;//case
          end;

  docrossing(aq,xb,xe,1);              // go calc it.
end;
//_______________________________________________________________________________________________________________________________

function returncurve(xs,radmod:extended):extended;   // calc ys, ks for return curve.
                                                     // enter with xs.  (aq not needed.)
var
  ys:extended;
  ysmod_sq:extended;
  kscos:extended;

begin
  if xs>=retrorgx                         // on parallel part.
     then begin
            ys:=retrorgy+retr+radmod;     // continue parallel straight beyond return curve.
            ks:=0;
            RESULT:=ys;
            EXIT;
          end;

  ysmod_sq:=SQR(retr+radmod)-SQR(retrorgx-xs);
  if (ysmod_sq<minfp) or (xs<=(retrorgx-retr-radmod)) or (xs<fpx)    // no curve fits or xs too short.
     then begin
            abandon_calcs:=True;     // abandon ship.
            RESULT:=0;
            EXIT;
          end;

  ys:=retrorgy+SQRT(ysmod_sq);    // on return curve.
  if ABS(retr+radmod)>minfp
     then begin
            kscos:=(ys-retrorgy)/(retr+radmod);                     // mod 2-9-98
            if ABS(kscos)<(1.0-minfp) then ks:=ARCCOS(kscos)        // mod 10-5-99 angle for timbering.
                                      else ks:=0;
          end
     else ks:=0;       // zero radius? surely not. (just for completeness).
  RESULT:=ys;
end;
//______________________________________________________________________________

function radcurve(aq:integer; {fl,k5,}xs:extended{; flway:integer}):extended;

     // return ys along turnout curve at this xs.

var
  rto,x:extended;
  ytemp,costemp,temp:extended;

begin
  RESULT:=0;      // default init.

  if tradius_is_straight=True    // turnout radius is straight at k3 crossing angle.
     then begin
            costemp:=1/COS(k3);
            ytemp:=h+(xs-heelx)*TAN(k3);  // offset.

            case aq of                    // which rail ?
                 2: RESULT:=ytemp;                    // turnout-road crossing rail, gauge-face.
                 3: RESULT:=ytemp+g*costemp;          // turnout-road stock rail, gauge-face.
                10: RESULT:=ytemp-j*costemp;          // turnout-road crossing rail, outer-edge.
                11: RESULT:=ytemp+(g+j)*costemp;      // turnout-road stock rail, outer-edge.
                26: RESULT:=ytemp+fw*costemp;         // K-crossing MS check rail, gauge-face.
                27: RESULT:=ytemp+(fw+j)*costemp;     // K-crossing MS check rail, outer-edge.
                 else run_error(67);
            end;//case

            ks:=k3;
          end
     else begin
            rto:=tradius;    // to keep the compiler happy.

            case aq of                // which rail ?
               2,5: rto:=tradius;                // trg radius.
             10,13: rto:=tradius+j;              // tro radius.
                 3: rto:=tradius-g;              // srg radius.
                11: rto:=tradius-g-j;            // sro radius.
               1,9: rto:=flarerad(aq,xs);        // turnout-side wing rail, go calc radius (flare-out).

              7,15: rto:=flarerad(aq,xs);  // turnout-side check rail, (flare in or out).
               else run_error(68);
            end;//case

            x:=0;       // keep compiler happy.

            repeat
              if xs>(torgx+ABS(rto)*0.98)        // 0.98*radius, arbitrary x limits to turnout curve.  0.79.a
                 then begin
                        x:=ABS(rto)*0.98;        // limit x from rad centre.
                        BREAK;
                      end;

              if xs<(torgx-ABS(rto)*0.98)
                 then begin
                        x:=0-ABS(rto)*0.98;      // limit x from rad centre.
                        BREAK;
                      end;

              x:=xs-torgx;    // ok, x from rad centre.
              BREAK;
            until 0<>0;

            try
              temp:=SQR(rto)-SQR(x);
              RESULT:=torgy-SQRT(temp)*SGZ(rto);    // calc turnout curve.
              ks:=ARCSIN(x/rto);                    // and gradient angle.
            except
              abandon_calcs:=True;       // abandon ship. Cannot be if x is under 90 degrees ?

              RESULT:=0;
              EXIT;
            end;//try
          end;
end;
//______________________________________________________________________________

function turnoutst_main(aq:integer;xb,xe,xs:extended):extended;     // calc ys, ks for main turnout rails on straight section.

var
  x,yst,k3cos:extended;

begin

  if xing_calc_i=1 then run_error(70);   // should not be here for curviform V-crossing.

  k3cos:=COS(k3);

  if ABS(k3cos)<minfp
     then begin
            ys:=g;         // div zero abort.
            ks:=0;
            RESULT:=ys;
            EXIT;
          end;

  x:=0;
  yst:=0;           //  these to keep the compiler happy.

  case aq of        // straight crossing, which rail ?

              // first calc offset at xb or xe point of each rail.

       2: begin
	  yst:=th;                               // curved turnout rail, gauge-face.
	  x:=xs-xb;
	  end;

      10: begin
	  yst:=th-j*COS(k3);                     // ditto, outer-edge.
	  x:=xs-xb;
	  end;

       3: begin
	  yst:=th+g*COS(k3);                     // curved stock rail, gauge-face.
	  x:=xs-xb;
	  end;

      11: begin
	  yst:=th+(g+j)*COS(k3);                 // ditto, outer edge.
	  x:=xs-xb;
	  end;

       5: begin
	  yst:=g+(bnx-fpx)/k3n;                 // curved vee rail, gauge face.
	  x:=xs-xb;
	  end;

      13: begin
	  yst:=g+j;                              // ditto, outer edge.
	  x:=xs-xb;
	  end;

     else run_error(73);
  end;//case

  ys:=yst+x/k3n;                   // calc along straight crossing at this x.
  ks:=k3;                          // gradient angle constant.

  RESULT:=ys;
end;
//______________________________________________________________________________

function turnoutst_wing(aq:integer; xb,xe,xs:extended):extended;     // calc ys, ks for turnout-side wing rail.

     // calc ys, ks for turnout-side wing rail.
     // also for K-crossing MS check rail

var
  y_temp,fl_offset:extended;

  k_temp:extended;  // 0.93.a

begin

  k_temp:=0; // keep compiler happy

  case aq of
               // TS wing rail: 0.71.a 27-5-01 now using aq2offset for the TS wing rail (for long wing rail, return curve, etc.)

       1: begin
            if ccd.end_diff_tr.type_diff=3      // no flare
               then fl_offset:=0
               else fl_offset:=cuflare(flen_tr,k5_tr,flarecx,flcendx,xs,1); // flare-out offset.

            y_temp:=aq2offset(xs,k_temp);   // get y_temp to running rail and angle k_temp.   // 0.93.a  bug fix (was ks).

            if ABS(COS(k_temp))>minfp then ys:=y_temp+(fw+fl_offset)/COS(k_temp)  // to wing rail gauge face.
                                      else ys:=fw+fl_offset;  //!!!
          end;

       9: begin

            case get_flare_type(ccd.end_diff_tr.type_diff) of
                        1: fl_offset:=cuflare(flen_tr,k5_tr,flarecox,flcendox,xs,1);  // bent flares.
                      else fl_offset:=0;                                              // machined flares or none, plain outer-edge.
            end;//case

            y_temp:=aq2offset(xs,k_temp);   // get y_temp to running rail.

            if ABS(COS(k_temp))>minfp then ys:=y_temp+(fw+j+fl_offset)/COS(k_temp) // to check rail outer edge.
                                      else ys:=fw+j+fl_offset; //!!!
          end;

                // MS K-crossing check rail...

      26: begin
            fl_offset:=cuflare(flen_mk,k5_mk,kckmsflx,kckmsflendx,xs,1); // flare-out offset.

            y_temp:=aq2offset(xs,k_temp);   // get y_temp to running rail and angle.

            if ABS(COS(k_temp))>minfp then ys:=y_temp+(fw+fl_offset)/COS(k_temp)  // to wing rail gauge face.
                                      else ys:=fw+fl_offset;  //!!!
          end;

      27: begin
            case get_flare_type(ccd.end_diff_mk.type_diff) of
                   1: fl_offset:=cuflare(flen_mk,k5_mk,kckmsflox,kckmsflendox,xs,1); // bent flares.
                 else fl_offset:=0;                                                  // machined flares, plain outer-edge.
            end;//case

            y_temp:=aq2offset(xs,k_temp);   // get y_temp to running rail.

            if ABS(COS(k_temp))>minfp then ys:=y_temp+(fw+j+fl_offset)/COS(k_temp) // to check rail outer edge.
                                      else ys:=fw+j+fl_offset; //!!!
          end;

     else run_error(79);   // shouldn't be here for any other rail.

  end;//case

  RESULT:=ys;

end;
//______________________________________________________________________________

function turnoutst_check(aq:integer; xb,xe,xs:extended):extended;     // calc ys, ks for turnout-side check rail.

var
  y_temp,fl_offset:extended;

begin
  case aq of
              // TS check rail: 0.71.a 27-5-01 now using aq3offset for the TS check rail (for long check rails, return curve, etc.)

       7: begin
            if xb<cuckfpx then fl_offset:=cuflare(flen_tw,k5_tw,cuckx,cuckfwx,xs,-1)    // flare-in offset.
	                  else fl_offset:=cuflare(flen_te,k5_te,cuckflx,cuckendx,xs,1); // flare-out offset.

            y_temp:=aq3offset(xs,ks);                                     // y_temp to running rail.
            if ABS(COS(ks))>minfp then ys:=y_temp-(fw+fl_offset)/COS(ks)  // to check rail gauge face.
                                  else ys:=g-fw-fl_offset;  //!!!
          end;

      15: begin                // TS check rail outer-edge...
            if xb<cuckfpox
               then begin
                      case get_flare_type(ccd.end_diff_tw.type_diff) of
                           1: fl_offset:=cuflare(flen_tw,k5_tw,cuckox,cuckfwox,xs,-1)
                         else fl_offset:=0;    // machined flares, plain outer-edge.
                      end;//case
                    end
               else begin
                      case get_flare_type(ccd.end_diff_te.type_diff) of
                           1: fl_offset:=cuflare(flen_te,k5_te,cuckflox,cuckendox,xs,1)
                         else fl_offset:=0;    // machined flares, plain outer-edge.
                      end;//case
                    end;

            y_temp:=aq3offset(xs,ks);                                      // y_temp to running rail.
            if ABS(COS(ks))>minfp then ys:=y_temp-(fw+j+fl_offset)/COS(ks) // to check rail outer edge.
                                  else ys:=g-fw-j-fl_offset; //!!!
          end;

     else run_error(80);   // should only be here for TS check rail.
  end;//case

  RESULT:=ys;
end;
//______________________________________________________________________________________
// mods 31-5-00..

procedure cuckrail(aq:integer);      // turnout-side check rail.

var
  xb,xe:extended;

begin
  case aq of

       7: begin                // gauge-face...

            if k3n<1.25         // 228a short angles...
               then begin
                      xb:=xorg-14*inscale-ccd.end_diff_tw.len_diff*inscale;         // arbitrary
                      xe:=cuckfpx;                 // stop opposite f.p.
                      docrossing(aq,xb,xe,-1);     // fill list up to f.p.
                    end
               else begin    //  228a  normal...

                      if (cuckx>=0) and (cuckfwx>0) and (cuckox>=0) and (cuckfwox>0)  // tidy start - do flare only if it's all in the template. 0.71.a  29-5-01.
                         then begin
                                xb:=cuckx;                   // check rail start.
                                xe:=cuckfwx;                 // stop at end of flare-in
                                docrossing(aq,xb,xe,-1);     // fill list along flare-in
                              end;

                      xb:=cuckfwx;                   // start at end of flare-in
                      if xb<0 then xb:=0;            // ensure tidy start  0.71.a  29-5-01.
                      xe:=cuckfpx;                   // stop opposite f.p.
                      docrossing(aq,xb,xe,-1);       // fill list up to f.p.
                    end;

            xb:=xe;                        // then do from f.p. to flare-out.
            xe:=cuckflx;
            docrossing(aq,xb,xe,1);        // fill list to flare out.

            xb:=xe;                        // then do flare-out.
            xe:=cuckendx;
            docrossing(aq,xb,xe,1);        // fill list to end of flare.
          end;

      15: begin                    // ditto, inner-edge...

            if k3n<1.25         // 228a short angles...
               then begin
                      xb:=xorg-14*inscale-ccd.end_diff_tw.len_diff*inscale;         // 14 arbitrary
                      xe:=cuckfpox;                // stop opposite f.p.
                      docrossing(aq,xb,xe,-1);     // fill list up to f.p.
                    end
               else begin      // 228a normal...
               
                      if (cuckx>=0) and (cuckfwx>0) and (cuckox>=0) and (cuckfwox>0)  // tidy start - do flare only if it's all in the template. 0.71.a  29-5-01.
                         then begin
                                xb:=cuckox;                // check rail start.
                                xe:=cuckfwox;              // stop at end of flare-in
                                docrossing(aq,xb,xe,-1);   // fill list along flare-in
                              end;

                      xb:=cuckfwox;                   // start at end of flare-in
                      if xb<0 then xb:=0;             // ensure tidy start  0.71.a  29-5-01.
                      xe:=cuckfpox;                   // stop opposite f.p.
                      docrossing(aq,xb,xe,-1);        // fill list up to f.p.

                    end;

            xb:=xe;                        // then do from f.p. to flare-out.
            xe:=cuckflox;
            docrossing(aq,xb,xe,1);        // fill list to flare out.

            xb:=xe;                        // then do flare-out.
            xe:=cuckendox;
            docrossing(aq,xb,xe,1);        // fill list to end of flare.

          end;

       else run_error(87);
  end;//case
end;
//______________________________________________________________________________

procedure stckrail(aq:integer);        // main-side check rail.

var
  xb,xe,ys:extended;

begin
  case aq of
       6: begin                     // gauge-face
            if (ckx_ms>=0) and (ckfwx>0) and (ckox>=0) and (ckfwox>0)  // tidy start - do flare only if it's all in the template. 0.71.a  29-5-01.
               then begin
                      xb:=ckx_ms;
                      xe:=ckfwx;
                      stflare(aq,flen_mw,k5_mw,xb,xe,fw,-1,1,(get_flare_type(ccd.end_diff_mw.type_diff)=0));     // flare-in.
                    end;

            xb:=ckfwx;
            if xb<0 then xb:=0;             // ensure tidy start  0.71.a  29-5-01.
            xe:=ckflarex;
            ys:=fw;
            dostr(aq,xb,xe,ys);            // parallel part.

            xb:=xe;
            xe:=ckendx;
            stflare(aq,flen_me,k5_me,xb,xe,fw,1,1,(get_flare_type(ccd.end_diff_me.type_diff)=0));   // flare-out.
          end;

      14: begin                    // outer-edge.
            if (ckx_ms>=0) and (ckfwx>0) and (ckox>=0) and (ckfwox>0)  // tidy start - do flare only if it's all in the template. 0.71.a  29-5-01.
               then begin
                      xb:=ckox;
                      xe:=ckfwox;
                      stflare(aq,flen_mw,k5_mw,xb,xe,fw+j,-1,1,(get_flare_type(ccd.end_diff_mw.type_diff)=0));   // flare-in.
                    end;

            xb:=ckfwox;
            if xb<0 then xb:=0;             // ensure tidy start  0.71.a  29-5-01.
            xe:=ckflarox;
            ys:=fw+j;
            dostr(aq,xb,xe,ys);             // parallel part.

            xb:=xe;
            xe:=ckendox;
            stflare(aq,flen_me,k5_me,xb,xe,fw+j,1,1,(get_flare_type(ccd.end_diff_me.type_diff)=0));  // flare-out.
          end;

      else run_error(39);
  end;//case
end;
//______________________________________________________________________________

procedure stflare(aq:integer; fl,flk,xb,xe,yfl:extended; fldir:integer; flsgn:extended; machined_end:boolean);      // fill list for straight flare.

                 //  flsgn  flag used in calcs, so it's a float.
var
  xs,ys,xend:extended;

begin
  xs:=blank_start(xb);

  xend:=xe;                             // for cuflare angle calcs.
  if xe>turnoutx then xe:=turnoutx;     // stop at end of turnout. v:0.76.a  28-4-02

  if xs>(xe-minfp) then EXIT;  // !!! 0.76.a  18-1-02.

  repeat
    if xs>xe then xs:=xe;                           // ensure we hit the end.

    if (machined_end=True) and ( (aq=10) or (aq=14) {or (aq=27)} or (aq=29)) // MS wing or check rail outer-edges
       then ys:=yfl                                                          // machined flares.
       else ys:=yfl+cuflare(fl,flk,xb,xend,xs,fldir)*flsgn; // bent flares.

    if abandon_calcs=True then EXIT;                 // snag in the calc of ys.

    if (f28000(aq,xs,ys)=1) or (xs>=xe) then BREAK;
    xs:=xs+incx;                                     // increment xs.
  until 0<>0;
end;
//______________________________________________________________________________

procedure k_checkrail_ds(aq:integer);        // K-crossing check rail, DS.

var
  xb,xe,ys:extended;

begin
  case aq of
      28: begin                     // gauge-face
            xb:=kckdsx;
            xe:=kckdsflx;
            ys:=g-fw;
            dostr(aq,xb,xe,ys);     // parallel part.

            xb:=xe;
            xe:=kckdsflendx;
            stflare(aq,flen_dk,k5_dk,xb,xe,ys,1,-1,(get_flare_type(ccd.end_diff_dk.type_diff)=0))   // fill list for flare-out.
          end;

      29: begin                    // outer-edge.
            xb:=kckdsox;
            xe:=kckdsflox;
            ys:=g-fw-j;
            dostr(aq,xb,xe,ys);     // parallel part.

            xb:=xe;
            xe:=kckdsflendox;
            stflare(aq,flen_dk_oe,k5_dk,xb,xe,ys,1,-1,(get_flare_type(ccd.end_diff_dk.type_diff)=0))   // fill list for flare-out.
          end;

     else run_error(139);
  end;//case
end;
//______________________________________________________________________________

procedure doradcurve(aq:integer; {fl,}xb,xe:extended);            // do a turnout curve.

var
  xs,ys:extended;

begin
  if xb>turnoutx then EXIT;             // !!! mod 26-3-99
  if xe>turnoutx then xe:=turnoutx;     // !!! mod 26-3-99

  if isolated_crossing=True    // 217a
     then begin
            if aq=2 then xs:=blank_start(wingx_minus-scale/6)                  // 2" arbitrary
                    else if aq=10 then xs:=blank_start(wingox_minus-scale/6)
                                  else xs:=blank_start(xb);
          end
     else xs:=blank_start(xb);      // start xs from xb.

  if xs>(xe-minfp) then EXIT;  // !!! 0.76.a  18-1-02.

  repeat
    if xs>xe then xs:=xe;                                // ensure we hit the end.
    ys:=radcurve(aq,{fl,k5,}xs{,1});                     // go calc curved part.
    if abandon_calcs=True then EXIT;                     // snag in the calc of ys.

    if (f28000(aq,xs,ys)=1) or (xs>=xe) then BREAK;       // fill list.
    xs:=xs+incx;
  until 0<>0;
end;
//______________________________________________________________________________

function cuflare(fl,flk,flb,fle,xs:extended; fld:integer):extended;   // calc flare-out on turnout-side wing rail.

    // enter with fl flare length along rail edge, flk flare angle
    // flb,fle x to flare start, flare end.
    // and fld flare direction. -1 = flare-in. +1 = flare-out.
    // return flare offset at this xs.

                      // n.b. this routine does not need aq.
var
  gfl:extended;

begin

  gfl:=0;     //  to keep the compiler happy.

  RESULT:=0;          //  in case of EXIT.

  if ABS(fle-flb)<minfp then EXIT;                       // division by zero.

  case fld of

          1: begin                                       // +1 = flare-out.
             if xs<flb
                then EXIT
                else gfl:=(xs-flb)/(fle-flb)*fl;         // length along flare-out.
             end;

         -1: begin                                       // -1 = flare-in
             if xs>fle
                then EXIT
                else gfl:=(fle-xs)/(fle-flb)*fl;         // length along flare-in
             end;

        else run_error(59);
  end;//case
RESULT:=gfl*SIN(flk);       // offset to this point on flare.
end;
//______________________________________________________________________________

function flarerad(aq:integer; xs:extended):extended;   // calc instantaneous radius in curved flares.

begin

  case aq of            // which rail ?
                                                                                // turnout-side wing rail, gauge face.
         1: RESULT:=tradius-fw-cuflare(flen_tr,k5_tr,flarecx,flcendx,xs,1);     // rad to this xs.
         9: RESULT:=tradius-fw-j-cuflare(flen_tr,k5_tr,flarecox,flcendox,xs,1); // ditto, outer edge

         7: if xs<cuckfpx            // turnout-side check rail, gauge_face     x to check rail opposite f.p.
               then RESULT:=csradius+fw+cuflare(flen_tw,k5_tw,cuckx,cuckfwx,xs,-1)      // flare-in.
               else RESULT:=csradius+fw+cuflare(flen_te,k5_te,cuckflx,cuckendx,xs,1);   // flare-out.

        15: if xs<cuckfpox           // turnout-side check rail, inner-edge:
               then RESULT:=csradius+fw+j+cuflare(flen_tw,k5_tw,cuckox,cuckfwox,xs,-1)    // flare-in.
               else RESULT:=csradius+fw+j+cuflare(flen_te,k5_te,cuckflox,cuckendox,xs,1); // flare-out.

       else begin
              RESULT:=0;
              run_error(95);
            end;

  end;//case
end;
//______________________________________________________________________________

function calc_slew(x,slew,slew_length:extended; var y:extended):boolean;

             //  x is distance into slewing zone.
             //  slew_2_kmax is used to determine the maximum value of theta for TANH. (gets closer to +/-1).
var
  theta:extended;
  pin,pout:Tpex;
  temp:extended;

begin
  RESULT:=False;

  if ABS(slew_length)<minfp
     then begin
            y:=0;
            EXIT;
          end;

  try
    case slew_mode of

      1: begin                         // COS curve method.
           theta:=x*Pi/slew_length;    // theta is a dummy angle - runs from 0 to pi in the slewing zone.
           y:=(1-COS(theta))*slew/2;   // track follows stretched cosine curve, COS(theta) runs from +1 to -1 in the slewing zone.
         end;

      2: begin         // rotated TANH curve method.

           temp:=slew2_ymax-slew2_ymin;
           if ABS(temp)<minfp
              then begin
                     y:=0;
                     EXIT;
                   end;

           pin.x:=0-slew2_kmax + x*(slew2_kmax*2)/slew_length;     // this k for tanh.
           pin.y:=TANH(pin.x);                                       // tanh at this k.
           dotransform(0-slew2_rot,0,0,pin,pout);                    // rotate to modify y (ignore x).

           y:=(pout.y-slew2_ymin)*slew/temp;   // return shift mm  at this x mm.

         end;
    end;//case

    RESULT:=True;
  except
    y:=0;
    RESULT:=False;
  end;//try
end;
//______________________________________________________________________________

procedure docurving(transform_flag,slew_flag:boolean; xs,ys:extended; var xc,yc,tn,rn:extended);    // calc curving, and call any transforms wanted.

               //   Do  any slewing/curving as required.
               //   Enter with xs,ys (measured along the curve).
               //   (ys is from straight stock rail);
               //   Return xc,yc  ,  tn rads (from template origin) ,  rn radius.

               //   If transform_flag = True, perform any transformations on xc,yc.

var
  slew_over, slew_over1, slew_over2, x1,x2, y1,y2, slew_twist:extended;
  delta_xs, xc1, xc2, yc1, yc2, xc_mid, yc_mid, offs:extended;
  dummy1, dummy2,temp:extended;

  pc:Tpex;                        //  point  xc,yc  after slewing/curving.
  pf:Tpex;                        //  point  xc,yc  after any transforms.

                //////////////////////////////////////////////////////////////////

                procedure do_curve_calcs(xs,ys:extended; var xc,yc,tn,rn:extended);

                var
                  ymod, tsn, xonr2, xn, yn:extended;

                        //======= do plain curve along r1...

                        procedure do_r1;

                        begin
                          xc:=xt1+(r1+ymod)*SIN(tn);
                          yc:=yt1-(r1+ymod)*COS(tn);
                        end;
                        //==========================================

                begin
                  if (ABS(nomrad)>max_rad_test) and (spiral=False)  // straight template..
                     then begin
                             xc:=xs;
                             yc:=ys;
                             tn:=0;
                             rn:=max_rad;
                             EXIT;
                          end;

                  ymod:=ssrad1-ys-r1;    // signed offset from nominal curve line.

                  if spiral=False
                     then begin          // no transition, plain curve.
                            tn:=xs/r1;   // angle turned through. (start square to origin, at tn=0).
                            do_r1;       // do plain curve along r1.
                            rn:=r1;
                          end
                     else begin                       // do transition, r1-transition-r2.
                            if xs>os
                               then begin             // zone is not r1
                                      tsn:=xs-os+ts1;
                                      xonr2:=tsn-ts1-tst;
                                      if xonr2>0
                                         then begin                      // we are in r2 zone.
                                                tn:=t2+xonr2/r2;
                                                xc:=xt2+(r2+ymod)*SIN(tn);
                                                yc:=yt2-(r2+ymod)*COS(tn);
                                                rn:=r2;
                                              end
                                         else begin         // we are in the transition zone.

                                                if transcalcs(False,False,trans_k,tsn,xn,yn,tn,rn)=True
                                                   then begin
                                                          xc:=xt0+xn+ymod*SIN(tn);
                                                          yc:=yn-ymod*COS(tn);
                                                        end
                                                   else begin       // transition calcs failed
                                                          xc:=xs;
                                                          yc:=ys;
                                                          tn:=0;
                                                        end;
                                              end;
                                    end//then

                               else begin               // we are in r1 zone.
                                      tn:=xs/r1+t0;     // angle turned through.
                                      do_r1;
                                      rn:=r1;
                                    end;
                          end;//else                      // end of transition coding.
                end;
                ////////////////////////////////////////////////////////////

begin
      // first do any slewing required (includes curving calcs if curved=True) ...

  if (slew_flag=True) and (slewing=True) and (slew<>0) and (xs>slew_s)    // into the slewing zone or beyond...
     then begin
            try
              if (ABS(slew_l)<minfp) or ( xs>(slew_s+slew_l) )   // beyond slewing zone..
                 then begin
                        do_curve_calcs(xs,ys,xc,yc,tn,rn);  // do the normal curving calcs,

                        xc:=xc - slew*SIN(slew_t);          // and then push over at the slewing angle..
                        yc:=yc + slew*COS(slew_t);

                      end
                 else begin         // in slewing zone...

                        delta_xs:=0.1;
                                               // get 3 slew_over values...

                        calc_slew(xs-slew_s-delta_xs,slew,slew_l,slew_over1);  // 0.1 mm behind the current xs
                        calc_slew(xs-slew_s,         slew,slew_l,slew_over);   // at the current xs
                        calc_slew(xs-slew_s+delta_xs,slew,slew_l,slew_over2);  // 0.1 mm in front of the current xs.

                        do_curve_calcs(xs-delta_xs,g/2,xc1,yc1,dummy1,dummy2);    // points on track centre-line each side.
                        do_curve_calcs(xs+delta_xs,g/2,xc2,yc2,dummy1,dummy2);

                        x1:=xc1-slew_over1*SIN(slew_t);        // push them over at slewing angle.
                        x2:=xc2-slew_over2*SIN(slew_t);

                        y1:=yc1+slew_over1*COS(slew_t);
                        y2:=yc2+slew_over2*COS(slew_t);


                        if ABS(x2-x1)<minfp
                           then slew_twist:=Pi*SGZ(y2-y1)/2            // +/-90 degrees.
                           else slew_twist:=ARCTAN((y2-y1)/(x2-x1));   // and get actual slew centre-line angle.

                        if (x2-x1)<0 then slew_twist:=slew_twist+Pi;   // !!!!! correct any foldover.

                        do_curve_calcs(xs,g/2,xc_mid,yc_mid,dummy1,rn);  // get current centre-line point (and return rn).

                        offs:=ys-g/2;               // offset from centre-line.

                        xc:=xc_mid - slew_over*SIN(slew_t) - offs*SIN(slew_twist); // recalc into the slewing zone...
                        yc:=yc_mid + slew_over*COS(slew_t) + offs*COS(slew_twist);

                        tn:=slew_twist;     // for peg calcs.
                      end;
            except
                    xc:=xs;
                    yc:=ys;
                    tn:=0;
                    rn:=max_rad;
            end;//try
          end             // end of any slewing calcs.

     else if (ABS(nomrad)<max_rad_test) or (spiral=True)  // curved template...
             then do_curve_calcs(xs,ys,xc,yc,tn,rn)       // do the normal curving calcs.
             else begin
                    xc:=xs;        // normal straight track.
                    yc:=ys;
                    tn:=0;
                    rn:=max_rad;
                  end;

      // finally, perform any transformations...

  if transform_flag=True
     then begin
            pc.X:=xc;
            pc.Y:=yc;
            dotransform(kform,xform,yform,pc,pf);

            xc:=pf.X+xshift;     // and shifts.
            yc:=pf.Y+yshift;
          end;
end;
//______________________________________________________________________________

function f28000(aq:integer; xs,ys:extended):integer;   // calc any curving and put in list.

var
  pc:Tpex;
  xc,yc,tc,rc:extended;

begin
  RESULT:=0;

  if xs<(startx-minfp) then EXIT;       // 1-11-99

      // mods 209a for adjustable turnout-road exit...

  if ((aq=3) or (aq=11) or (aq=5) or (aq=13) or(aq=25))   // turnout road
  and (turnout_road_i>1)                                  // was turnout_road_i=2 217a
     then begin
            if xs>(xorg+turnout_road_endx+incx*2) then EXIT; // no data allowed beyond this. // 209a
          end
     else begin
            if xs>(turnoutx+incx*2) then EXIT; // no data allowed beyond this.
          end;

  if nlnow_array[aq]=0          // start point of rail edge.   14-4-99
     then begin
	    xy_ends[aq,0].x:=xs;
	    xy_ends[aq,0].y:=ys;
	  end;

  xy_ends[aq,1].x:=xs;   // cumulative end point of rail edge.
  xy_ends[aq,1].y:=ys;

  docurving(True,True,xs,ys,xc,yc,tc,rc);    // curve and transform this xs,ys to xc,yc. (tc, rc not used here)

  pc.x:=xc;
  pc.y:=yc;
  RESULT:=f29000(aq,pc);             // put in data array, and return array full flag.
end;
//______________________________________________________________________________

function f29000(aq:integer; pc:Tpex):integer;       //  Put xc,yc (in pc) in rail-data array.
						    //  Change hand if required.
var
  plist:TPoint;
  n,xlist,ylist,nl:integer;

                             //////////////////////////////////////////////////////

                             function check_rings(n:integer):boolean;   // check for any infringement of this ring.

                                            // if n=0 keep record of infringement amount (current ring).
                                            // all calcs in 1/100ths mm.
                                            // this code assumes aq remains constant on consecutive calls until line finished.

                             var
                               diag_distance,stepx,stepy,ring_rad:extended;
                               i,xlist_old,ylist_old:integer;

                                             //============================================

                                             function check_ring_point(x,y:extended):extended;  // calc a single point distance from ring centre.

                                             begin
                                               RESULT:=maxfp;  // default init, no infringement.
                                               try
                                                 RESULT:=SQRT(SQR(x-rings[n,0]*100) + SQR(y+(y_datum-rings[n,1])*100));
                                               except
                                                 RESULT:=maxfp;
                                               end;//try

                                               if (n=0) and (RESULT<min_ring_distance) then min_ring_distance:=RESULT;
                                             end;
                                             //===========================================

                             begin
                               RESULT:=False;  // default init.

                               diag_distance:=check_ring_point(xlist,ylist);

                               if (cl_only=True) or (warn_centrelines=True)
                                  then ring_rad:=rings[n,2]*50   // DIA!!! in 1/100ths mm (*100/2). centre-lines infringe inner diameter.
                                  else ring_rad:=rings[n,3]*50;  // DIA!!! in 1/100ths mm (*100/2). rails (gauge-faces) infringe outer diameter.

                               if diag_distance<(ring_rad+incx125)   // closer than 1.25*incx to ring.  1.25 arbitrary

                                                    // and might be inside ring.
                                                    // go back to previous point and micro-step forwards to ths one.
                                  then begin
                                         if (rings_checkpoints[n,2]=aq) and (rings_checkpoints[n,3]=1)   // same line and previous point was also.
                                            then begin
                                                   xlist_old:=rings_checkpoints[n,0];
                                                   ylist_old:=rings_checkpoints[n,1];

                                                   stepx:=(xlist-xlist_old)/40;    // 40 arbitrary.
                                                   stepy:=(ylist-ylist_old)/40;

                                                   for i:=1 to 40 do begin

                                                     if check_ring_point(xlist_old+stepx*i, ylist_old+stepy*i)<ring_rad
                                                        then begin
                                                               RESULT:=True;         // this point infringed ring.
                                                               if n<>0 then BREAK;   // don't need minimum distance for ring copies
                                                             end;
                                                   end;//for
                                                 end
                                            else begin                        // previous was more than 1.25*incx outside ring (or was a different aq).
                                                                              // so impossible to have crossed through ring,
                                                                              // and no need to go back and use smaller steps.
                                                   if diag_distance<ring_rad  // but now actually inside ring?
                                                      then RESULT:=True;      // must be the first point on a new line.
                                                 end;

                                         rings_checkpoints[n,0]:=xlist;  // for next time...
                                         rings_checkpoints[n,1]:=ylist;
                                         rings_checkpoints[n,3]:=1;

                                       end
                                  else rings_checkpoints[n,3]:=0;    // for next time - this point more than 1.25*incx outside ring.

                               rings_checkpoints[n,2]:=aq;   // now on this line.
                             end;//func
                             ////////////////////////////////////////////////////////

begin

  //  first go convert to scale/hand, ensure all inputs are within range for 32-bits,
  //  do re-origination and distortions, round off and convert to integer.

  //  if list already full, new data overwrites final entry.

  plist:=xy_to_list(pc);

  xlist:=plist.X;
  ylist:=plist.Y;

  nl:=nlnow_array[aq];         // pick up next free index for this aq.
  nlmax_array[aq]:=nl;         // and return it as current max.

  if intolist(aq,nl,0,xlist)<>0 then run_error(47);      // fill the x data.
  if intolist(aq,nl,1,ylist)<>0 then run_error(48);      // fill the y data.

  if nl>=(nldim_array[aq]-1)         // max index.
     then RESULT:=1                  // flag array full. Do not bump index.
     else begin
	    INC(nlnow_array[aq]);    // bump index and save for next time.
            RESULT:=0                // flag array not full.
          end;

  if (ring_warn=True) or (ring_copies_warn=True)
     then begin
            if ( (cl_only=False) and (warn_centrelines=False) and (aq<8) )               // gauge-faces for main rails only (check against OUTER ring).
            or ( ((cl_only=True) or (warn_centrelines=True)) and ((aq=24) or (aq=25)) )  // centre-lines
               then begin
                      if ring_warn=True
                         then begin
                                if check_rings(0)=True then ring_infringed:=True;
                              end;
                      if (ring_copies_warn=True) and (ring_index>0) and (ring_copies_infringed=False) // no need to go on checking once we have an infringement,
                         then begin
                                for n:=1 to ring_index do begin
                                  if check_rings(n)=True then ring_copies_infringed:=True;
                                end;//for
                              end;
                    end;
          end;
end;
//___________________________________________________________________________________________

function intolist(aq,nl,xy,d:integer):integer;

             // insert data d for x or y (xy=0/1) in rail list,
             // and return 0 if o.k. ...
var
  p:Pointer;

begin
  RESULT:=1;       // init default return

  if (nl<0) or (xy<0) or (xy>1) or (aq<0) or (aq>aq_max_c)
     then begin
            run_error(51);
            EXIT;
          end;

  p:=xy_p[aq,xy];              // pointer to list.

  if p=nil
     then begin
            run_error(52);
            EXIT;
          end;

  if nl>intarray_max(p)
     then begin
            run_error(53);     //  abandon ship if index outside limits.
            EXIT;
          end;

  intarray_set(p,nl,d);
  RESULT:=0;                  //  put data in list and flag o.k.

  if d>xy_max[xy] then xy_max[xy]:=d;     // update max/min values for scaling calcs.
  if d<xy_min[xy] then xy_min[xy]:=d;
end;
//_____________________________________________________________________________________

function outoflist(aq,nl,xy:integer):integer;

               // return a value from rail list for x or y  (xy=0/1)...
var
  p:Pointer;

begin
  RESULT:=0;     // keep compiler happy.

  if (nl<0) or (xy<0) or (xy>1) or (aq<0) or (aq>aq_max_c)
     then begin
            run_error(61);
            EXIT;
          end;

  p:=xy_p[aq,xy];              // pointer to list.

  if p=nil
     then EXIT;              // return zero if there is no list for this aq. ?? how did you get here??
                             // erasing lines that were never drawn?

  if nl>intarray_max(p)
     then begin
            run_error(63);     //  abandon ship if index outside limits.
            EXIT;
          end;

  RESULT:=intarray_get(p,nl);    // get the data.
end;
//_____________________________________________________________________________________

function calc_curving:boolean;    // calc curving/transition constants.

var
  temp,xn,yn,tn,rn,x9,y9:extended;

  slew_pull_back, slew_trans:extended;    //!!! 1-11-99

  pin,pout:Tpex;

begin
  RESULT:=False;                        // in case of error.

  if slewing=True   //!!! 1-11-99
       then begin
              if (ABS(slew)>slew_l) or (ABS(slew)<minfp)   // safety checks for SQRT, div by zero.
                 then begin
                        slew_pull_back:=0;
                        slew_angle:=0;
                      end
                 else begin
                        try
                          slew_pull_back:=( slew_l-SQRT(SQR(slew_l)-SQR(slew)) )*2;    //!!! 1-11-99  *2 is arbitrary approx for shortening effect of S-curve.
                          slew_angle:=ARCTAN(slew_pull_back/slew);
                        except
                          slew_pull_back:=0;
                          slew_angle:=ARCTAN(2.0);
                        end;//try
                      end;
              slew_t:=slew_angle;       //  slewing angle is same for straight track.

              if slew_mode=2
                 then begin      // calc constants once only...

                        if slew2_kmax<0.02 then slew2_kmax:=0.02;         // safety.

                        slew2_rot:=ARCTAN(1-SQR(TANH(slew2_kmax)));     // dy/dx TANH(x) = 1-TANH^2(x).  rotation angle needed in trig units.

                        pin.x:=slew2_kmax;
                        pin.y:=TANH(slew2_kmax);
                        dotransform(0-slew2_rot,0,0,pin,pout);

                        slew2_ymax:=pout.y;
                        slew2_ymin:=0-slew2_ymax;
                      end;
            end;

  if spiral=False                       // no transition
     then begin
            clrad1:=nomrad;         // track centre-line radius.
            ssrad1:=clrad1+g/2;     // radius in main road stock rail : g/2 adjustment aways +ve, even for -ve rad.
            r1:=clrad1;             // fixed rad at track centre-line.
            xt1:=0;                 // centre of radius...
            yt1:=r1+g/2;            // put straight stock rail gauge-face at datum level.

            if slewing=True then slew_t:=slew_angle+(slew_s+(slew_l/2))/r1;   // slewing angle at centre of slewing zone.

          end
     else begin                   // transition wanted - calc constants.

                 //!!! these calcs are relative to the template datum.
                 //!!! see also calc_transition (math_unit) for calcs based on the transition origin.

            clrad1:=nomrad1{+ycurv};  // track centre-line radius.
            ssrad1:=clrad1+g/2;       // radius in main road stock rail : g/2 adjustment aways +ve, even for -ve rad.

            clrad2:=nomrad2{+ycurv};
            ssrad2:=clrad2+g/2;       // radius in main road stock rail : g/2 adjustment aways +ve, even for -ve rad.

            r1:=clrad1;           // initial rad is at track centre-line.
            r2:=clrad2;           // final rad ditto.

            temp:=r1-r2;

            if ABS(temp)<minfp
               then ts1:=maxfp*SGZ(temp)
               else ts1:=tst*r2/temp;      // length along curve from transition origin to r1 point.

            trans_k:=ts1*r1;                 // transition constant.

            t1:=ts1/2/r1;                    // angle at r1 point.
            t2:=(tst+ts1)/2/r2;              // angle at r2 point.

            if transcalcs(False,True,trans_k,ts1,x9,y9,tn,rn)=False then EXIT;  // get  x9, y9  from transition equations (ignore tn (=t1) ).

            tos:=os/r1;                     // angle turned along r1 from template origin.
            t0:=t1-tos;          //-ve!     // angle from centre of r1 back to template origin (-ve backwards).

            xt1:=0-r1*SIN(t0);   //+ve      // x to centre of r1.
            yt1:=y9+r1*COS(t1);             // y to centre of r1.

            xtrans1:=xt1+r1*SIN(t1);        // x to r1 point.
            ytrans1:=y9;                    // y to r1 point.
            xt0:=xtrans1-x9;                // x to transition datum.

            if transcalcs(False,True,trans_k,(ts1+tst),xn,yn,tn,rn)=False then EXIT;

            xtrans2:=xt0+xn;               // ditto for r2 point...
            ytrans2:=yn;
            xt2:=xtrans2-r2*SIN(t2);
            yt2:=ytrans2+r2*COS(t2);

            if slewing=True    // 1-11-99
               then begin
                      docurving(False, False, (slew_s+(slew_l/2)), g/2, xn,yn, slew_trans, rn);   // get slew_trans angle at centre of slewing zone (with slew-flag off, xn,yn,rn ignored).
                      slew_t:=slew_angle+slew_trans;
                    end;
          end;
  RESULT:=True;
end;
//_________________________________________________________________________________________

function randomizing_warn(pdf:boolean):boolean;    // pre-init printing.

          // return False if he cancels.

const
  random_help_str:string='    `0Output  Randomized  Timbering`9'
  +'||When timber randomizing is in force, every re-draw of the control template on the trackpad produces a fresh randomized effect.'
  +'||To "freeze" a randomized timbering layout for printing identical multiple copies of a template, click the `0MAIN > STORE & BACKGROUND`1 menu item to copy the template to the background drawing,'
  +' and then click the `0OUTPUT > PRINT BACKGROUND TEMPLATES (TRACK PLAN)`1 menu item.'

  +'||To create a fresh randomized layout for a background template, click the `0GENERATOR > REBUILD`1 menu options.'

  +'||You should make as many print copies as you will need all in one Templot session, because it is not possible to save the exact timbering layout between sessions.'
  +' When reloaded from a data file, the template will be rebuilt with a fresh randomized timbering layout.'

  +'||Alternatively, if you create a PDF file you can print identical copies at any time using a PDF reader program.';

var
  i:integer;


begin
  RESULT:=False;       // default init.

  if (no_printer_available=True) and (pdf=False)
     then begin
            alert(6,'   no  printer',
                    'Templot is unable to locate a printer on this system.'
                   +'||If you have recently installed a printer, please save your work and restart Templot.',
                    '','','','','','O K',0);
            EXIT;
          end;

  if (random_flag=True) and (print_entire_pad_flag=False)
     then begin
            repeat
              i:=alert(3,'     timber  randomizing',
                         '||You are about to print or create a PDF file of the control template with timber randomizing in force.'
                        +'||It will not be possible to repeat the same timbering arrangement again later.'
                        +'||To make multiple identical prints of a template containing randomized timbers, the template must be a background template.',
                         '','','','more  information','cancel  output','O K  - continue',4);
              case i of
                  4: alert_help(0,random_help_str,'');
                  5: EXIT;
              end;//case
            until i<>4;
          end;

  RESULT:=True;
end;
//_______________________________________________________________________________________________________________

function small_scale_hints:boolean;

const
  small_help_str:string='When printing a complete track plan at a reduced size, you will not usually need the timber outline extension marks, which are intended as a full-size construction aid.'
    +'||Similarly the timber centre-line markings may not be needed, and the timber numbers may be printed illegibly small.'

    +'||For a neater printed result, all these features can be switched off.'
    +' To do this, cancel printing then de-select the|`0GENERATOR > GENERATOR SETTINGS > OUTLINE EXTENSION MARKS`1 and/or|`0GENERATOR > GENERATOR SETTINGS > TIMBER CENTRE-LINES`1 menu items.'
    +'||Then if you are printing the entire trackpad click the|`0GENERATOR > REBUILD ALL BACKGROUND`1 menu item.'
    +'||When printing at a very small size, you may prefer to omit the timbering entirely. To do this, instead of the above you can simply select the '
    +'`0OUTPUT > OUTPUT DRAWING OPTIONS > ELEMENT OPTIONS...`1 menu item and de-select the `0TIMBERING`1 option. In this case the timbering on the screen is unchanged.'

    +'||The timber numbering can be omitted from reduced size printing by selecting the '
    +'`0OUTPUT > OUTPUT DRAWING OPTIONS > TIMBER NUMBERING > ON FULL-SIZE OUTPUT ONLY or OMIT ALL TIMBER NUMBERING`1 menu options.'

    +'||When printing the timber numbers, Templot normally scales the font size to match the output size, and omits the numbers if they would be too small to be of any use.'

    +'||Alternatively, if the `0OUTPUT > OUTPUT DRAWING OPTIONS > TIMBER NUMBERING > SCALE TIMBER NUMBERING`1 menu option is de-selected,'
    +' the numbers will be printed at whatever font size you specify by selecting the `0OUTPUT > OUTPUT DRAWING OPTIONS > TIMBER NUMBERING > FONT FOR TIMBER NUMBERING`1 menu item.';
var
  i:integer;
  size_str:string;

begin
  RESULT:=False;

  if small_print_msg_pref=True then EXIT;

  with pad_form do begin
    if ((out_factor<0.7) or (fit_single_sheet=True)) and {(print_timbering_menu_entry.Checked=True)}  // 0.82.b
       (print_settings_form.output_timbering_checkbox.Checked=True)
       then begin
              if out_factor<0.7 then size_str:=round_str(out_factor*100,2)+'% of full-size'
                                else size_str:='a reduced size';

              if   ( (timber_outlines_menu_entry.Checked=True) and ((outline_extensions_menu_entry.Checked=True) or (timber_centres_menu_entry.Checked=True)) )
                 or ( (print_timber_numbering_menu_entry.Checked=True) and (timber_numbers_menu_entry.Checked=True) )

                 then begin

                        alert_box.preferences_checkbox.Checked:=False;
                        alert_box.preferences_checkbox.Show;

                        repeat
                          i:=alert(3,'    small  size  output',
                                     '||You are about to print or create a PDF file at '+size_str+' with timber outline extension marks or timber centre-lines switched on.'
                                    +'||For a neater result when printing at a reduced size these features can be switched off.',
                                     '','','','more  information','cancel  output','O K',4);
                          if i=4 then alert_help(0,'    `0Small  Size  Output`9||You are about to print or create a PDF file at '+size_str+'.||'+small_help_str,'');

                        until i<>4;

                        small_print_msg_pref:=alert_box.preferences_checkbox.Checked;
                        alert_box.preferences_checkbox.Hide;

                        if i=5 then RESULT:=True;

                      end;
            end;
  end;//with
end;
//______________________________________________________________________________

function print_control_template(pdf:boolean):integer;  // 0.91.d     // return number of pages output

var
  i,kludge_count:integer;
  saved_extensions:boolean;

begin
  RESULT:=0;  // init

  saved_extensions:=outline_extensions;

  if (print_settings_form.output_timber_extensions_checkbox.Checked=False) and (print_settings_form.output_timbering_checkbox.Checked=True)   // 223d
     then begin
            pad_form.outline_extensions_menu_entry.Checked:=False;
            outline_extensions:=False;
            redraw(False);                     // remove them
          end;

  if pdf=True then export_form.Hide;

  print_form.diagram_mode_radiobutton.Enabled:=False;
  pdf_laz_form.diagram_mode_radiobutton.Enabled:=False;   // 245a

try
  if output_diagram_mode=True
     then begin
            repeat
              i:=alert(2,'    diagram  mode',
                         '||The output is currently set for diagram mode.'
                        +'||The control template can not be printed or output in diagram mode.'
                        +'||Diagram mode is intended for background templates only, to display a track plan.| ',
                         '','','','? output  mode  -  help','cancel','change  to  detail  mode  and  continue',4);

              if i=4 then alert_help(-300,output_mode_help_str,'');

            until i<>4;

            if i=5 then EXIT;

            pad_form.output_detail_mode_menu_entry.Click;
          end;

  print_entire_pad_flag:=False;  // output the control template only

  if print_now_bang=False   // 0.93.a
     then begin

            if small_scale_hints=True then EXIT;         // he cancelled.
            if randomizing_warn(pdf)=False then EXIT;    // he cancelled.

            if (pdf=False) and (printer_setup_done=False) then printer_setup(False,False);   // must do setup at least once.
          end;

  if paper_bunching=True then cancel_paper_bunching;  // don't want bunching in the printing.

  kludge_count:=create_fb_kludge_templates;  // 0.94.a  if any

  if pdf=True       // 0.91.d pdf
     then begin
            pad_caption('    create  PDF  file  from  the  control  template');

            if show_margins=1 then show_margins:=2;   // change page outlines on pad, if showing for printer

            RESULT:=pdf_laz_draw;  // go  create EMF files for external PDF.
          end
     else begin        // normal print

            pad_caption('F11    print  the  control  template');

            if show_margins=2 then show_margins:=1;   // change page outlines on pad, if showing for PDF

            RESULT:=print_draw;   // go do the print.
          end;

  if kludge_count>0 then delete_fb_kludge_templates;  // 0.94.a  if any


  if (print_settings_form.output_timber_extensions_checkbox.Checked=False) and (print_settings_form.output_timbering_checkbox.Checked=True) and (saved_extensions=True)   // 223d
     then begin
            pad_form.outline_extensions_menu_entry.Checked:=True;     // restore them
            outline_extensions:=True;
          end;

  cancel_adjusts(False);                    //  update the caption.

  redraw_pad(False,False);                  //  force a redraw when finished.

  if pdf=True then do_external_pdf(RESULT);  // transfer EMF files to PDF_creator

finally
  if (pdf=True) and (external_window_showing=False)
     then begin
            export_form.Show;
            export_form.BringToFront;
          end;
end;//try

end;
//______________________________________________________________________________

function print_entire_pad(pdf:boolean):integer;  // 0.91.d  return number of pages output

var
  kludge_count:integer;
  saved_extensions:boolean;

begin
  RESULT:=0; // init

  saved_extensions:=False;  // init

  if pdf=True then export_form.Hide;

  print_form.diagram_mode_radiobutton.Enabled:=True;
  pdf_laz_form.diagram_mode_radiobutton.Enabled:=True;    // 245a

try

  print_entire_pad_flag:=True;

  if print_now_bang=False   // 0.93.a
     then begin

            if small_scale_hints=True then EXIT;         // he cancelled.
            if randomizing_warn(pdf)=False then EXIT;    // he cancelled.

            if (pdf=False) and (printer_setup_done=False) then printer_setup(False,False);   // must do setup at least once.
          end;

  if classic_templot=False then store_and_background(False,True);  // 0.93.a Quick mode - first store existing control template

  if (print_settings_form.output_timber_extensions_checkbox.Checked=False) and (print_settings_form.output_timbering_checkbox.Checked=True)   // 223d   rebuild templates if necessary
     then begin
            saved_extensions:=outline_extensions;

            if print_group_only_flag=True
               then pad_form.remove_extensions_from_group_menu_entry.Click
               else begin
                      pad_form.remove_extensions_from_all_menu_entry.Tag:=1;       // flag no messages
                      pad_form.remove_extensions_from_all_menu_entry.Click;
                    end;
          end;

  if paper_bunching=True then cancel_paper_bunching;  // don't want bunching in the print.

  if output_diagram_mode=False
     then kludge_count:=create_fb_kludge_templates  // 0.94.a  if any
     else kludge_count:=0;

  if pdf=True       // 0.91.d pdf
     then begin
            if print_group_only_flag=True
               then pad_caption('    create  PDF  file  from  group  only')
               else pad_caption('    create  PDF  file  from  all  background  templates');

            if show_margins=1 then show_margins:=2;   // change page outlines on pad, if showing for printer

            RESULT:=pdf_laz_draw;  // go  create EMF files for external PDF.
          end
     else begin        // normal print

            if print_group_only_flag=True
               then pad_caption('    print  group  only')
               else pad_caption('CTRL-F11    print  all  background  templates');

            if show_margins=2 then show_margins:=1;   // change page outlines on pad, if showing for PDF

            RESULT:=print_draw;         // go do the print.
          end;

  if kludge_count>0 then delete_fb_kludge_templates;  // 0.94.a  if any

  if saved_extensions=True then pad_form.restore_timber_extension_marks_menu_entry.Click;   // 223d

  cancel_adjusts(False);                    //  update the caption.

  redraw_pad(False,False);                  //  force a redraw when finished.

  if pdf=True then do_external_pdf(RESULT);  // transfer EMF files to PDF_creator

finally
  if (pdf=True) and (external_window_showing=False)
     then begin
            export_form.Show;
            export_form.BringToFront;
          end;
end;//try

end;
//______________________________________________________________________________

procedure action_panel_hint(hint_str:string);   // 205c  set hollow-triangle mouse action hint

   // if hint_str='' set to default (last mouse action).

begin
  with action_form do begin
    if hint_str='' then image_panel.Hint:=' repeat last mouse action '
                   else image_panel.Hint:=' '+hint_str+' ';

    action_1_image.Hint:=image_panel.Hint;
    action_2_image.Hint:=image_panel.Hint;

  end;//with
end;
//______________________________________________________________________________

procedure cancel_adjusts(new_action:boolean);  //  cancel all adjusts and restore flags, etc..
                                               //  if new_action=True we are swapping actions, no need to close the action panel.
var
  mps:TPoint;

begin
  ensure_selected_clip_pairs_match;    // 234c update adjusted brick clip shapes if any

  slide_adjust_radius:=False;    // 244d
  slide_adjust_centres:=False;

  with pad_form do begin

    mouse_draw_lines:=False;
    mouse_drawing_in_progress:=False;

    draw_template_direct:=False;   // 233b

    mouse_action_release(True);
    mouse_modify:=-1;          // and then cancel any action selected.

    pad_caption('..    [  no  mouse  actions  active  ]');   // 0.82.d

    pad_form.cancel_mouse_action_popup_entry.Enabled:=False;

              // save current adjust for repeat, and do any updates ...

    if spot_mod=1         then repeat_last_index:=0;
    if zoom_mod=1         then repeat_last_index:=1;
    if datum_mod=1        then repeat_last_index:=2;
    if peg_mod=1          then repeat_last_index:=3;
    if notch_mod=1
       then begin repeat_last_index:=4;
                  INC(notch_index);                             // to next rollback slot.
                  if notch_index>notch_c then notch_index:=0;
                  undo_notch[notch_index]:=get_current_notch;   // save notch in this slot...
            end;

    if out_factor_mod=1   then repeat_last_index:=5;
    if shove_along_mod=1  then repeat_last_index:=6;
    if shove_throw_mod=1  then repeat_last_index:=7;
    if trans_length_mod=1 then repeat_last_index:=8;
    if trans_start_mod=1  then repeat_last_index:=9;
    if slew_amount_mod=1  then repeat_last_index:=10;
    if slew_length_mod=1  then repeat_last_index:=11;
    if slew_start_mod=1   then repeat_last_index:=12;
    if length_mod=1       then repeat_last_index:=13;
    if approach_mod=1     then repeat_last_index:=14;
    if shift_mod=1        then repeat_last_index:=15;
    if slide_mod=1        then repeat_last_index:=16;
    if ring_mod=1         then repeat_last_index:=17;
    if shift_keeps_mod=1  then repeat_last_index:=18;
    if twist_keeps_mod=1  then repeat_last_index:=19;
    if zoffset_mod=1      then repeat_last_index:=20;
    if twist_mod=1        then repeat_last_index:=21;
    if orbit_mod=1        then repeat_last_index:=22;
    if both_mod=1         then repeat_last_index:=23;
    if sx_mod=1           then repeat_last_index:=24;
    if curving_mod=1      then repeat_last_index:=25;
    if roam_mod=1         then repeat_last_index:=26;
    if slew_factor_mod=1  then repeat_last_index:=27;
    if bunch_mod=1        then repeat_last_index:=28;
    if porg_mod=1         then repeat_last_index:=29;

    if corner1_mod=1      then repeat_last_index:=30;
    if corner2_mod=1      then repeat_last_index:=31;

    if oneshape_shift_mod=1   then repeat_last_index:=32;
    if allshapes_shift_mod=1  then repeat_last_index:=33;

    if snake_mod=1            then repeat_last_index:=34;

    if allshapes_scale_mod=1  then repeat_last_index:=35;
    if allshapes_rotate_mod=1 then repeat_last_index:=36;

    if oneshape_scale_mod=1   then repeat_last_index:=37;

    if shove_length_mod=1    then repeat_last_index:=38;
    if shove_width_mod=1     then repeat_last_index:=39;
    if shove_twist_mod=1     then repeat_last_index:=40;

    if blanking_mod=1        then repeat_last_index:=41;

    if roll_mod=1            then repeat_last_index:=42;

    if ringdia_mod=1         then repeat_last_index:=43;

    if ruler1_mod=1          then repeat_last_index:=44;   // 0.78.a
    if ruler2_mod=1          then repeat_last_index:=45;

    if swell_mod=1           then repeat_last_index:=46;   // 0.78.c

    if shove_crab_mod=1      then repeat_last_index:=47;   // 0.78.c

    if move_labels_mod=1                                   // 0.82.d
       then begin
              if (xshift_labels<>0) or (yshift_labels<>0)
                 then begin
                        save_done:=False;
                        backup_wanted:=True;      // save moved labels if any.
                      end;
              repeat_last_index:=48;
            end;

    if entry_straight_mod=1  then repeat_last_index:=49;   // 0.91.b


    if plat_ts_start_mod=1   then repeat_last_index:=50;  // 0.93.a ...
    if plat_ts_length_mod=1  then repeat_last_index:=51;
    if plat_ts_width1_mod=1  then repeat_last_index:=52;
    if plat_ts_width2_mod=1  then repeat_last_index:=53;

    if plat_ms_start_mod=1   then repeat_last_index:=54;
    if plat_ms_length_mod=1  then repeat_last_index:=55;
    if plat_ms_width1_mod=1  then repeat_last_index:=56;
    if plat_ms_width2_mod=1  then repeat_last_index:=57;

    if gaunt_offset_mod=1    then repeat_last_index:=58;  // SHIFT+F12  // 0.93.a ex 081
    if vee_mod=1             then repeat_last_index:=59;  // F9         // 0.93.a ex 081
    if hdk_mod=1             then repeat_last_index:=60;  // F10        // 0.93.a ex 081

    if rect_left_mod=1       then repeat_last_index:=61;  // output boundary rectangle 0.93.a ...
    if rect_right_mod=1      then repeat_last_index:=62;
    if rect_top_mod=1        then repeat_last_index:=63;
    if rect_bottom_mod=1     then repeat_last_index:=64;
    if rect_move_mod=1       then repeat_last_index:=65;

    if check_diffs_len_mod=1    then repeat_last_index:=66;    // 0.94.a
    if check_diffs_flare_mod=1  then repeat_last_index:=67;    // 0.94.a
    if check_diffs_gap_mod=1    then repeat_last_index:=68;    // 0.94.a

    if dv_mod=1                 then repeat_last_index:=69;    // 0.98.a
    if dv_clear_mod=1           then repeat_last_index:=70;    // 0.98.a


    if plat_ms_skew1_mod=1      then repeat_last_index:=71;    // 207a
    if plat_ms_skew2_mod=1      then repeat_last_index:=72;    // 207a
    if plat_ts_skew1_mod=1      then repeat_last_index:=73;    // 207a
    if plat_ts_skew2_mod=1      then repeat_last_index:=74;    // 207a

    if turnout_road_mod=1       then repeat_last_index:=75;    // 209a  CTRL+F12

    if adjacent_ts_mod=1       then repeat_last_index:=76;     // 213a
    if adjacent_ms_mod=1       then repeat_last_index:=77;     // 213a

    if edge_ts_start_mod=1   then repeat_last_index:=78;       // 215a...
    if edge_ts_length_mod=1  then repeat_last_index:=79;

    if edge_ms_start_mod=1   then repeat_last_index:=80;
    if edge_ms_length_mod=1  then repeat_last_index:=81;

    if main_road_mod=1       then repeat_last_index:=82;       // 217a

    if gaunt_radius_mod=1    then repeat_last_index:=83;       // 217b

    if locator_mod=1         then repeat_last_index:=84;       // 227a

    if clip_size_mod=1       then repeat_last_index:=85;       // 229a..
    if clip_angle_mod=1      then repeat_last_index:=86;

    if pt_blanking_mod=1     then repeat_last_index:=87;       // 229a

    if (adjacent_ts_mod=1) and (grid_form.Showing=False)
       then begin
              if alert(3,'    adjust  adjacent  track  centres  TS',
                         '||Do you now want to return the centre-line to its normal position ?'
                        +'||This will not change the setting you have made for the TS adjacent track centres.'
                        +'||If you want to roll the dummy vehicle tool along the adjacent centre-line, leave it where it is for now.',
                         '','','','leave  unchanged','','return  centre - line  to  normal',0)=6
                 then begin
                        cl_options_code:=0;
                        redraw(True);
                      end;
            end;

    if (adjacent_ms_mod=1) and (grid_form.Showing=False)
       then begin
              if alert(3,'    adjust  adjacent  track  centres  MS',
                         '||Do you now want to return the centre-line to its normal position ?'
                        +'||This will not change the setting you have made for the MS adjacent track centres.'
                        +'||If you want to roll the dummy vehicle tool along the adjacent centre-line, leave it where it is for now.',
                         '','','','leave  unchanged','','return  centre - line  to  normal',0)=6
                 then begin
                        cl_options_code:=0;
                        redraw(True);
                      end;
            end;

            //  then cancel them...

    spot_mod:=0;
    zoom_mod:=0;
    datum_mod:=0;
    peg_mod:=0;
    notch_mod:=0;
    out_factor_mod:=0;

    shove_along_mod:=0;
    shove_throw_mod:=0;
    shove_crab_mod:=0;
    shove_length_mod:=0;
    shove_width_mod:=0;
    shove_twist_mod:=0;

    trans_length_mod:=0;
    trans_start_mod:=0;

    slew_factor_mod:=0;
    slew_amount_mod:=0;
    slew_length_mod:=0;
    slew_start_mod:=0;

    length_mod:=0;
    approach_mod:=0;
    blanking_mod:=0;
    pt_blanking_mod:=0;   // 229a
    shift_mod:=0;
    slide_mod:=0;
    snake_mod:=0;
    ring_mod:=0;
    ringdia_mod:=0;
    shift_keeps_mod:=0;
    twist_keeps_mod:=0;
    zoffset_mod:=0;

    locator_mod:=0;  // 227a

    porg_mod:=0;

    dv_mod:=0;        // 0.98.a
    dv_clear_mod:=0;  // 0.98.a

    corner1_mod:=0;
    corner2_mod:=0;
    oneshape_shift_mod:=0;
    oneshape_scale_mod:=0;

    clip_size_mod:=0;         // 229a..
    clip_angle_mod:=0;

    allshapes_shift_mod:=0;
    allshapes_scale_mod:=0;
    allshapes_rotate_mod:=0;

    bunch_mod:=0;
    twist_mod:=0;
    orbit_mod:=0;
    both_mod:=0;
    sx_mod:=0;
    curving_mod:=0;
    roam_mod:=0;
    roll_mod:=0;

    entry_straight_mod:=0;  // 0.91.b

    ruler1_mod:=0;
    ruler2_mod:=0;

    swell_mod:=0;

    move_labels_mod:=0;  // 0.82.d


    plat_ts_start_mod:=0;  // 0.93.a ...
    plat_ts_length_mod:=0;
    plat_ts_width1_mod:=0;
    plat_ts_width2_mod:=0;

    plat_ms_start_mod:=0;
    plat_ms_length_mod:=0;
    plat_ms_width1_mod:=0;
    plat_ms_width2_mod:=0;

    gaunt_offset_mod:=0;  // SHIFT+F12  // 0.93.a ex 081
    vee_mod:=0;           // F9         // 0.93.a ex 081
    hdk_mod:=0;           // F10        // 0.93.a ex 081

    rect_left_mod:=0;  // output boundary rectangle 0.93.a ...
    rect_right_mod:=0;
    rect_top_mod:=0;
    rect_bottom_mod:=0;
    rect_move_mod:=0;

    check_diffs_len_mod:=0;     // 0.94.a
    check_diffs_flare_mod:=0;   // 0.94.a
    check_diffs_gap_mod:=0;     // 0.94.a

    plat_ms_skew1_mod:=0;  // 207a
    plat_ms_skew2_mod:=0;  // 207a
    plat_ts_skew1_mod:=0;  // 207a
    plat_ts_skew2_mod:=0;  // 207a

    turnout_road_mod:=0;   // 209a  CTRL+F12

    adjacent_ts_mod:=0;    // 213a
    adjacent_ms_mod:=0;    // 213a

    edge_ts_start_mod:=0;       // 215a...
    edge_ts_length_mod:=0;

    edge_ms_start_mod:=0;
    edge_ms_length_mod:=0;

    main_road_mod:=0;     // 217a

    gaunt_radius_mod:=0;  // 217b

    //------------

    select_centre:=False;

    zoom_rectangle:=False;
    group_fence_rectangle:=False;
    output_boundary_rectangle:=False;  // 0.93.a
    drawn_shape_rectangle:=False;            // 214a

    rectangle_in_progress:=False;
    rectangle_exists:=False;

    panning_form.zoom_rectangle_latching_toolbutton.Down:=False;
    pad_form.zoom_rectangle_latching_toolbutton.Down:=False;
    pad_form.group_fence_latching_toolbutton.Down:=False;
    pad_form.output_rectangle_latching_toolbutton.Down:=False;  // 0.93.a

    click_bgnd_select:=False;
    shift_click:=False;
    name_highlighted:=-1;    // remove any selection highlighting.

    lock_xing_popup_entry.Enabled:=False;      // these only valid in F5...
    lock_switch_popup_entry.Enabled:=False;

    unlock_both;

    if dummy_label_panel.Showing=True    // finished any label moving.
       then begin
              clicked_keep_index:=-1;    // so can popup again.
              dummy_label_panel.Hide;    // finished moving.
            end;

    if (xshift_keeps<>0) or (yshift_keeps<>0)      // update after mouse shift all keeps.
      then begin
             Screen.Cursor:=crHourglass;
             shift_all_group(False);
             if kform_keeps=0 then rebuild_group(False,False);   // no need to do this if it will be done again below.
           end;

    if kform_keeps<>0      // update after mouse twist all keeps.
      then begin
             Screen.Cursor:=crHourglass;
             twist_all_group(False);
             rebuild_group(False,False);
            end;

    action_panel_hint(''); // 205c  reset hollow-triangle hint to default (last mouse action).

    Screen.Cursor:=crDefault;
    Cursor:=crDefault;

    if GetCursorPos(mps)=False then begin mps.X:=0; mps.Y:=0; end;

    SetCursorPos(mps.X, mps.Y);    // need a mouse move event to ensure pad cursor setting after any screen cursor change.

  end;//with

  if bgnd_form_closed=True then bgnd_form.Show;   // was closed for mouse actions from the shapes form buttons, so show it again now.
  bgnd_form_closed:=False;

  //bgpad_timb_infill_style:=his_bgpad_timb_infill_style;     // restore if changed for mouse action (adjusting brick clips)

     //  !!! problems closing action panel if rapid mouse action swaps. panel closing now done via a timer...  15-4-00

  if new_action=False
     then begin
            action_form.action_panel_timer.Enabled:=True;
            allow_idle:= NOT control_room_form.fast_100_menu_entry.Checked;  // no mouse action - allow idle again (belt and braces - should have been done when the mouse action was released).
          end;
end;
//________________________________________________________________________________________

procedure trail_length(X:integer);

var
  dir:extended;

begin
  if (peg_code=11) or (peg_code=600)   //  0.79.a  29-05-06
     then dir:=-1                      //  change direction if peg on length.
     else dir:=1;

  turnoutx:=endx_now+(X-length_now)*dir*ffx;             //  ffx in mm per pixel at mouse-down.
  if turnoutx>turnoutx_max then turnoutx:=turnoutx_max;

  if plain_track=False
     then begin
            if turnoutx<xorg then turnoutx:=xorg;        //  limit min to approach track for turnouts.
          end
     else begin
            if turnoutx<0 then turnoutx:=0;              //  limit min to zero for plain track,
            xorg:=turnoutx;                              //  and ensure xorg matches.
          end;

  turnout_i:=1;      // length locked at new turnoutx.

  if (peg_code=11) or (peg_code=600)   // CTRL-9 or TOLP        // 0.79.a  29-05-06
     then begin                                                        // peg is on length...
            pegx:=turnoutx;
            if spiral=True then os:=os_now-(pegx_now-pegx);            //  keep transition start constant.
            if slewing=True then slew_s:=slew_s_now-(pegx_now-pegx);   //  slewing ditto.
            peg_curve;        // keep peg constant.
          end;

  if peg_code=19   // peg on mid-length, extend both ways.  0.76.a  5-11-01.
     then begin
            pegx:=turnoutx/2;                                          // get new mid pegx.
            if spiral=True then os:=os_now-(pegx_now-pegx);            //  keep transition start constant.
            if slewing=True then slew_s:=slew_s_now-(pegx_now-pegx);   //  slewing ditto.
            peg_curve;
          end;

end;
//__________________________________________________________________________________________

procedure trail_approach(X:integer);

var
  dir:extended;

begin
  if peg_code=0 then dir:=1
                else dir:=-1;                       //  change direction unless peg is reset on rail-end.

  xorg:=xorg_now+(X-approach_now)*dir*ffx;          //  ffx in mm per pixel at mouse-down.
  if xorg<0 then xorg:=0;                           //  xorg mustn't go negative.

  if plain_track=False
     then begin
            turnoutx:=turnoutx_now-xorg_now+xorg;                     //  xorg changes reflected in turnoutx.
            if turnoutx>turnoutx_max then turnoutx:=turnoutx_max;
            if turnoutx<xorg then turnoutx:=xorg;
          end
     else begin
            if xorg>turnoutx_max then xorg:=turnoutx_max;     // keep turnoutx=xorg for plain track
            turnoutx:=xorg;
          end;

  turnout_i:=1;      // length locked at new turnoutx.

  if (peg_code<>0) and (peg_code<>19)
     then begin
            pegx:=pegx_now-xorg_now+xorg;                            //  pegx changes with xorg unless peg is reset on rail-end, or at mid-point.
            if spiral=True then os:=os_now-xorg_now+xorg;            //  os transition start changes with xorg ditto.
            if slewing=True then slew_s:=slew_s_now-xorg_now+xorg;   //  slewing ditto.
          end;

  if peg_code=19   // peg on mid-length, extend both ways.  0.76.a  5-11-01.
     then begin
            pegx:=turnoutx/2;                                               // get new mid pegx.
            if spiral=True then os:=os_now+(pegx_now-pegx)-xorg_now+xorg;            //  os transition start changes with xorg ditto.
            if slewing=True then slew_s:=slew_s_now+(pegx_now-pegx)-xorg_now+xorg;   //  slewing ditto.
          end;

  udpegx:=udpegx_now-xorg_now+xorg;      //  user-defined peg position.

  peg_curve;  // keep template on the peg.
end;
//___________________________________________________________________________________________

procedure trail_roam(X:integer);      // change xorg but maintain turnoutx constant.

begin
  if plain_track=True then EXIT;                //  !!! shouldn't be here.

  xorg:=xorg_now+(X-roam_now)*ffx;                     //  ffx in mm per pixel at mouse-down.
  if xorg<0 then xorg:=0;                                  //  xorg mustn't go negative.
  if xorg>(turnoutx-toel*2) then xorg:=turnoutx-toel*2;    //  or greater than this (arbitrary).

  pegx:=pegx_now-xorg_now+xorg;                            //  correct peg position in case not on a pre-set.
  udpegx:=udpegx_now-xorg_now+xorg;                        //  and user-defined peg position.

  turnout_i:=1;      // length locked at turnoutx.
end;
//______________________________________________________________________________

procedure trail_roll(X:integer);      // CTRL-F4  roll rails and sleepers

begin
  tb_roll_percent:=tb_roll_percent_now+(roll_now-X)*tb_roll_factor;

  if tb_roll_percent<0 then tb_roll_percent:=0;
  if tb_roll_percent>100 then tb_roll_percent:=100;

                       //  ffx in mm per pixel at mouse-down.
end;
//______________________________________________________________________________

procedure trail_blanking(X:integer);      // change startx.

begin
  startx:=startx_now+(X-blanking_now)*ffx;                         // ffx in mm per pixel at mouse-down.
  if startx>(turnoutx-3*inscale) then startx:=turnoutx-3*inscale;  // 3" scale arbitrary maximum blanking.
  if (half_diamond=False) and (startx<0) then startx:=0;
end;
//______________________________________________________________________________

procedure trail_pt_blanking(X:integer);      // change pt_blank   plain track blanking    229a

begin
  pt_blank:=pt_blank_now-(X-pt_blanking_now)*ffx;                         // ffx in mm per pixel at mouse-down
  if pt_blank>(turnoutx-3*inscale) then pt_blank:=turnoutx-3*inscale;     // arbitrary maximum blanking   leave 3" minimum template
  if pt_blank<0 then pt_blank:=0;
end;
//______________________________________________________________________________


procedure trail_turnout_road(X:integer);   // 209a  CTRL+F12

begin
  if plain_track=True then EXIT;                //  !!! shouldn't be here.

  turnout_road_endx:=turnout_road_endx_now+(X-turnout_road_now_X)*ffx;  // ffx in mm per pixel at mouse-down.

  if turnout_road_endx<min_turnout_road_endx then turnout_road_endx:=min_turnout_road_endx;  // 213b

  if peg_code=241   // TEXITP        // 213b
     then begin
            pegx:=xorg+turnout_road_endx;
            peg_curve;        // keep peg constant.
          end;
end;
//______________________________________________________________________________

procedure trail_main_road(X:integer);   // 217a

begin
  if plain_track=True then EXIT;                //  !!! shouldn't be here.

  main_road_endx:=main_road_endx_now+(X-main_road_now_X)*ffx;  // ffx in mm per pixel at mouse-down.

  if main_road_endx<min_main_road_endx then main_road_endx:=min_main_road_endx;  // 213b

  if peg_code=261   // MEXITP        // 217a
     then begin
            pegx:=xorg+main_road_endx;
            peg_curve;                    // keep peg constant.
          end;
end;
//______________________________________________________________________________

procedure trail_dv_start(X:integer);      // roll dummy vehicle  0.98.a

begin
  cdvi.dv_start:=dv_start_now+((X-dv_now)*ffx)/inscale;      // ffx in mm per pixel at mouse-down.

  if cdvi.dv_start<(0-cdvi.dv_length) then cdvi.dv_start:=0-cdvi.dv_length;  // 205a limits

  if cdvi.dv_start>(turnoutx/inscale+cdvi.dv_length-cdvi.dv_wheelbase) then cdvi.dv_start:=turnoutx/inscale+cdvi.dv_length-cdvi.dv_wheelbase;  // 205a limits
end;
//______________________________________________________________________________

procedure trail_dv_clearance(Y:integer);      // dummy vehicle clearance  0.98.a

begin
  cdvi.dv_clearance:=dv_clearance_now+((Y-dv_clear_now)*ffy)/inscale;      // ffy in mm per pixel at mouse-down.

  if cdvi.dv_clearance<0 then cdvi.dv_clearance:=0;   // negative is confusing.
end;
//______________________________________________________________________________

procedure trail_adj_centres_ts(Y:integer);    // adjacent centres TS  213a

begin
  cpi.trtscent_pi:=trtscent_pi_now+(Y-ts_adj_now)*hand_i*ffy;  // ffy in mm per pixel at mouse-down.

  if cpi.trtscent_pi<(g/2) then cpi.trtscent_pi:=g/2;   // smaller is confusing.

  get_cpi;
end;
//______________________________________________________________________________

procedure trail_adj_centres_ms(Y:integer);    // adjacent centres MS  213a

begin
  cpi.trmscent_pi:=trmscent_pi_now+(ms_adj_now-Y)*hand_i*ffy;  // ffy in mm per pixel at mouse-down.

  if cpi.trmscent_pi<(g/2) then cpi.trmscent_pi:=g/2;   // smaller is confusing.

  get_cpi;
end;
//______________________________________________________________________________

procedure trail_platform_ts_start_mm(X:integer);   // TS platform start  0.93.a

begin
  platform_ts_start_mm:=platform_ts_start_mm_now+(X-platform_ts_start_now_X)*ffx;  // ffx in mm per pixel at mouse-down.

  if platform_ts_start_mm>turnoutx then platform_ts_start_mm:=turnoutx;

  if platform_ts_start_mm<0 then platform_ts_start_mm:=0;
end;
//______________________________________________________________________________

procedure trail_platform_ts_length_mm(X:integer);   // TS platform length  0.93.a

begin
  platform_ts_length_mm:=platform_ts_length_mm_now+(X-platform_ts_length_now_X)*ffx;  // ffx in mm per pixel at mouse-down.

  if turnoutx<(platform_ts_start_mm+platform_ts_length_mm) then platform_ts_length_mm:=turnoutx-platform_ts_start_mm;

  if platform_ts_length_mm<0 then platform_ts_length_mm:=0;
end;
//______________________________________________________________________________

procedure trail_platform_ts_start_width_ins(Y:integer);   // TS platform starting width in full-size inches  0.93.a

begin
  platform_ts_start_width_ins:=platform_ts_start_width_ins_now+(Y-platform_ts_start_width_now_Y)*ffy*hand_i/inscale;  // ffy in mm per pixel at mouse-down.

  if platform_ts_start_width_ins<0 then platform_ts_start_width_ins:=0;
end;
//______________________________________________________________________________

procedure trail_platform_ts_end_width_ins(Y:integer);   // TS platform ending width in full-size inches  0.93.a

begin
  platform_ts_end_width_ins:=platform_ts_end_width_ins_now+(Y-platform_ts_end_width_now_Y)*ffy*hand_i/inscale;  // ffy in mm per pixel at mouse-down.

  if platform_ts_end_width_ins<0 then platform_ts_end_width_ins:=0;
end;
//______________________________________________________________________________

procedure trail_platform_ms_start_mm(X:integer);   // MS platform start  0.93.a

begin
  platform_ms_start_mm:=platform_ms_start_mm_now+(X-platform_ms_start_now_X)*ffx;  // ffx in mm per pixel at mouse-down.

  if platform_ms_start_mm>turnoutx then platform_ms_start_mm:=turnoutx;

  if platform_ms_start_mm<0 then platform_ms_start_mm:=0;
end;
//______________________________________________________________________________

procedure trail_platform_ms_length_mm(X:integer);   // MS platform length  0.93.a

begin
  platform_ms_length_mm:=platform_ms_length_mm_now+(X-platform_ms_length_now_X)*ffx;  // ffx in mm per pixel at mouse-down.

  if turnoutx<(platform_ms_start_mm+platform_ms_length_mm) then platform_ms_length_mm:=turnoutx-platform_ms_start_mm;

  if platform_ms_length_mm<0 then platform_ms_length_mm:=0;
end;
//______________________________________________________________________________

procedure trail_platform_ms_start_width_ins(Y:integer);   // MS platform starting width in full-size inches  0.93.a

begin
  platform_ms_start_width_ins:=platform_ms_start_width_ins_now-(Y-platform_ms_start_width_now_Y)*ffy*hand_i/inscale;  // ffy in mm per pixel at mouse-down.

  if platform_ms_start_width_ins<0 then platform_ms_start_width_ins:=0;
end;
//______________________________________________________________________________

procedure trail_platform_ms_end_width_ins(Y:integer);   // MS platform ending width in full-size inches  0.93.a

begin
  platform_ms_end_width_ins:=platform_ms_end_width_ins_now-(Y-platform_ms_end_width_now_Y)*ffy*hand_i/inscale;  // ffy in mm per pixel at mouse-down.

  if platform_ms_end_width_ins<0 then platform_ms_end_width_ins:=0;
end;
//______________________________________________________________________________

procedure trail_platform_ms_start_skew_mm(X:integer);   // MS platform skew at start  207a

begin
  platform_ms_start_skew_mm:=platform_ms_start_skew_mm_now-(X-platform_ms_start_skew_now_X)*ffx;  // ffx in mm per pixel at mouse-down.

  if platform_ms_start_skew_mm>(25*scale) then platform_ms_start_skew_mm:=25*scale;     // +/-25ft arbitrary limits

  if platform_ms_start_skew_mm<(0-25*scale) then platform_ms_start_skew_mm:=0-25*scale;

end;
//______________________________________________________________________________

procedure trail_platform_ms_end_skew_mm(X:integer);     // MS platform skew at end  207a

begin
  platform_ms_end_skew_mm:=platform_ms_end_skew_mm_now+(X-platform_ms_end_skew_now_X)*ffx;  // ffx in mm per pixel at mouse-down.

  if platform_ms_end_skew_mm>(25*scale) then platform_ms_end_skew_mm:=25*scale;     // +/-25ft arbitrary limits

  if platform_ms_end_skew_mm<(0-25*scale) then platform_ms_end_skew_mm:=0-25*scale;

end;
//______________________________________________________________________________

procedure trail_platform_ts_start_skew_mm(X:integer);   // TS platform skew at start  207a

begin
  platform_ts_start_skew_mm:=platform_ts_start_skew_mm_now-(X-platform_ts_start_skew_now_X)*ffx;  // ffx in mm per pixel at mouse-down.

  if platform_ts_start_skew_mm>(25*scale) then platform_ts_start_skew_mm:=25*scale;     // +/-25ft arbitrary limits

  if platform_ts_start_skew_mm<(0-25*scale) then platform_ts_start_skew_mm:=0-25*scale;

end;
//______________________________________________________________________________

procedure trail_platform_ts_end_skew_mm(X:integer);     // TS platform skew at end  207a

begin
  platform_ts_end_skew_mm:=platform_ts_end_skew_mm_now+(X-platform_ts_end_skew_now_X)*ffx;  // ffx in mm per pixel at mouse-down.

  if platform_ts_end_skew_mm>(25*scale) then platform_ts_end_skew_mm:=25*scale;     // +/-25ft arbitrary limits

  if platform_ts_end_skew_mm<(0-25*scale) then platform_ts_end_skew_mm:=0-25*scale;
end;
//______________________________________________________________________________

procedure trail_trackbed_ts_start_mm(X:integer);   // TS trackbed start  215a

begin
  trackbed_ts_start_mm:=trackbed_ts_start_mm_now+(X-trackbed_ts_start_now_X)*ffx;  // ffx in mm per pixel at mouse-down.

  if trackbed_ts_start_mm>turnoutx then trackbed_ts_start_mm:=turnoutx;

  if trackbed_ts_start_mm<0 then trackbed_ts_start_mm:=0;
end;
//______________________________________________________________________________

procedure trail_trackbed_ts_length_mm(X:integer);   // TS trackbed length  215a

begin
  trackbed_ts_length_mm:=trackbed_ts_length_mm_now+(X-trackbed_ts_length_now_X)*ffx;  // ffx in mm per pixel at mouse-down.

  if turnoutx<(trackbed_ts_start_mm+trackbed_ts_length_mm) then trackbed_ts_length_mm:=turnoutx-trackbed_ts_start_mm;

  if trackbed_ts_length_mm<0 then trackbed_ts_length_mm:=0;
end;
//______________________________________________________________________________

procedure trail_trackbed_ms_start_mm(X:integer);   // MS trackbed start  215a

begin
  trackbed_ms_start_mm:=trackbed_ms_start_mm_now+(X-trackbed_ms_start_now_X)*ffx;  // ffx in mm per pixel at mouse-down.

  if trackbed_ms_start_mm>turnoutx then trackbed_ms_start_mm:=turnoutx;

  if trackbed_ms_start_mm<0 then trackbed_ms_start_mm:=0;
end;
//______________________________________________________________________________

procedure trail_trackbed_ms_length_mm(X:integer);   // MS trackbed length  215a

begin
  trackbed_ms_length_mm:=trackbed_ms_length_mm_now+(X-trackbed_ms_length_now_X)*ffx;  // ffx in mm per pixel at mouse-down.

  if turnoutx<(trackbed_ms_start_mm+trackbed_ms_length_mm) then trackbed_ms_length_mm:=turnoutx-trackbed_ms_start_mm;

  if trackbed_ms_length_mm<0 then trackbed_ms_length_mm:=0;
end;
//______________________________________________________________________________

procedure trail_entry_straight(X:integer);      // change fixed_sl.   0.91.b

var
  new_fixed_sl:extended;

begin
  new_fixed_sl:=fixed_sl_now-(X-entry_straight_now)*ffx;    // ffx in mm per pixel at mouse-down.

  if new_fixed_sl<0 then new_fixed_sl:=0;   // 214a   not beyond FP.

  if (g-new_fixed_sl/k3n)>min_sl_offset then fixed_sl:=new_fixed_sl;  // change value only if it will fit beyond switch heel.

  gocalc(0,0);   // calc it for peg
  peg_curve;     // maintain on peg
end;
//___________________________________________________________________________________________

procedure trail_slide(X:integer);

begin
  pegx:=pegx_now-(X-peg_now_x)*ffx;                 // gocalc runs peg along current rail or centre-line.
  gocalc(0,0);                                      // to get new pegy and arm_angle.
  peg_curve;                                        // shift onto previous position.
  rotate_turnout(pegangle_now-pegangle,False);      // and re-align to previous angle.

  //pad_form.Panel1.Caption:=floattostr(pegy); // 244d debug

  if slide_adjust_radius=True  // 244d
     then begin
            nomrad:=slide_adust_nomrad_start+(pegy-slide_adust_pegy_start);
            peg_curve;
          end;

  if slide_adjust_centres=True  // 244d
     then begin
            if slide_adjust_radius=False then peg_curve;  // not already done above  
            cpi.trtscent_pi:=slide_adust_trtscent_start+(pegy-slide_adust_pegy_start);
            get_cpi;
          end;

end;
//______________________________________________________________________________

procedure trail_snake(X:integer);        // (same as slide if no transition or slewing.)

begin
  pegx:=pegx_now-(X-peg_now_x)*ffx;                 // gocalc runs peg along current rail or centre-line.
  if spiral=True then os:=os_now-(X-peg_now_x)*ffx;
  if slewing=True then slew_s:=slew_s_now-(X-peg_now_x)*ffx;

  // !!! unlike slide (above) we don't need gocalc because snake is always on ms centre-line (pegy and angle constant).

  peg_curve;         // shift onto previous position.
end;
//___________________________________________________________________________________________

procedure trail_shift(X,Y:integer);

begin
  xshift:=xshift_now+(X-shift_now_x)*ffx;
  yshift:=yshift_now+(Y-shift_now_y)*ffy*hand_i;
end;
//___________________________________________________________________________________________

procedure trail_shift_keeps(X,Y:integer);

begin
  xshift_keeps:=xshift_now+(X-shift_now_x)*ffx;
  yshift_keeps:=yshift_now+(Y-shift_now_y)*ffy;
end;
//____________________________________________________________________________________________

procedure trail_twist_keeps(Y:integer);

begin
  kform_keeps:=kform_now+twist_dir*(Y-shift_now_y)*ffy*100/(mouse_rot_factor*fine_adjust*screenx);   // 100 arbitrary.
  normalize_angle(kform_keeps);
end;
//__________________________________________________________________________________________

procedure trail_shift_labels(X,Y:integer);      // 0.82.d

var
  n:integer;

begin
  xshift_labels:=xlabels_now+(X-labels_now_x)*ffx;
  yshift_labels:=ylabels_now+(Y-labels_now_y)*ffy;

  for n:=0 to (keeps_list.Count-1) do begin

    with Ttemplate(keeps_list.Objects[n]) do begin

      if group_selected=False then CONTINUE;     // not in group.
      if bg_copied=False then CONTINUE;          // not on background.

      with template_info.keep_dims.box_dims1 do begin

        mod_text_x:=mod_text_x-xshift_labels_old+xshift_labels;
        mod_text_y:=mod_text_y-yshift_labels_old+yshift_labels;

      end;//with
    end;//with
  end;//next template

  xshift_labels_old:=xshift_labels;   // save modifiers for next calc.
  yshift_labels_old:=yshift_labels;
end;
//____________________________________________________________________________________________

procedure trail_notch(X,Y:integer);

begin
  notchx:=notchx_now+(X-notch_now_x)*ffx;
  notchy:=notchy_now+(Y-notch_now_y)*ffy;
end;
//________________________________________________________________________________________

procedure trail_ring(X,Y:integer);

begin
  rings[0,0]:=xring_now+(X-ring_now_x)*ffx;
  rings[0,1]:=yring_now+(Y-ring_now_y)*ffy;
end;
//_________________________________________________________________________________________

procedure trail_ruler1(X,Y:integer);

begin
  ruler_startx:=ruler_startx_now+(X-ruler1_now_x)*ffx;
  ruler_starty:=ruler_starty_now+(Y-ruler1_now_y)*ffy;
end;
//_________________________________________________________________________________________

procedure trail_ruler2(X,Y:integer);

begin
  ruler_endx:=ruler_endx_now+(X-ruler2_now_x)*ffx;
  ruler_endy:=ruler_endy_now+(Y-ruler2_now_y)*ffy;
end;
//_________________________________________________________________________________________

procedure trail_ringdia(Y:integer);

begin
  ring_dia:=ring_dia_now+(Y-ringdia_now_y)*ffy;

  if ring_dia>screenx_max then ring_dia:=screenx_max;
  if ring_dia<0 then ring_dia:=0;

  rings[0,2]:=ring_dia;
  rings[0,3]:=ring_dia+railtop*2; // outer diameter.

  grid_form.dia_label.Caption:='dia: '+round_str(ring_dia,2)+' mm';
end;
//_________________________________________________________________________________________

procedure calc_peg_pad_pos;    // calculate position of peg on screen.

var
  peg_p:Tpex;
  dummy:extended;

begin
  docurving(True,True,pegx,pegy,peg_p.x,peg_p.y,now_peg_k,dummy);    // calc new curving angle and peg position on pad for caption.

  pad_peg_point:=xy_to_dwg100(peg_p);
  pad_peg_point.x:=pad_peg_point.x/100;           // position in mm. after curving, transforms and distortions, (Tpex),
  pad_peg_point.y:=pad_peg_point.y/100+y_datum;   // these dims used only for caption display on mouse peg move.
end;
//__________________________________________________________________________________________

procedure calc_locator_pad_pos;    // calculate position of locator on screen.    227a

var
  locator_p:Tpex;
  dummy:extended;

begin
  docurving(True,True,locatorx,locatory,locator_p.x,locator_p.y,locatork,dummy);    // calc new curving angle and peg position on pad for caption.

  pad_locator_point:=xy_to_dwg100(locator_p);
  pad_locator_point.x:=pad_locator_point.x/100;           // position in mm. after curving, transforms and distortions, (Tpex),
  pad_locator_point.y:=pad_locator_point.y/100+y_datum;
end;
//__________________________________________________________________________________________

procedure trail_peg(X,Y:integer);

var
  modin,modout:Tpex;

begin

  modin.x:=(X-peg_now_x)*ffx;
  modin.y:=(Y-peg_now_y)*ffy*hand_i;

  if peg_rail=0                           // free peg move...
     then begin
            dotransform(now_peg_k-kform,0,0,modin,modout);       // transform move at current angle.

            pegx:=pegx_now+modout.x;   // to keep movements matching the mouse.
            pegy:=pegy_now+modout.y;
          end
     else pegx:=pegx_now+modin.x;      // gocalc runs peg along current rail.

  calc_peg_pad_pos;      // calc screen peg position for mouse action caption.

end;
//__________________________________________________________________________________________

procedure trail_locator(X,Y:integer);       // 227a

var
  modin,modout:Tpex;

begin
  modin.x:=(X-locator_now_x)*ffx;
  modin.y:=(Y-locator_now_y)*ffy*hand_i;

  if locator_rail=0      // free locator move...
     then begin
            dotransform(locatork-kform,0,0,modin,modout);       // transform move at current angle.

            locatorx:=locatorx_now+modout.x;   // to keep movements matching the mouse.
            locatory:=locatory_now+modout.y;
          end
     else locatorx:=locatorx_now+modin.x;      // calc_draw runs locator along current rail.

  calc_locator_pad_pos;      // calc screen locator position for mouse action caption.
end;
//______________________________________________________________________________

procedure trail_zoffset(X,Y:integer);

begin
  zoom_offsetx:=zoom_offsetx_now-(X-zoff_now_x)*ffx;
  zoom_offsety:=zoom_offsety_now-(Y-zoff_now_y)*ffy;
end;
//____________________________________________________________________________________________

procedure trail_porg(X,Y:integer);

begin
  print_pages_top_origin:=print_pages_top_origin_now-(porg_now_x-X)*ffx;
  print_pages_left_origin:=print_pages_left_origin_now-(porg_now_y-Y)*ffy;
end;
//____________________________________________________________________________________________

procedure trail_rect_x1(X:integer);

begin
  output_rectangle_x1:=output_rectangle_x_now-(rect_now_X-X)*ffx;
end;
//______________________________________________________________________________

procedure trail_rect_x2(X:integer);

begin
  output_rectangle_x2:=output_rectangle_x_now-(rect_now_X-X)*ffx;
end;
//______________________________________________________________________________

procedure trail_rect_y1(Y:integer);

begin
  output_rectangle_y1:=output_rectangle_y_now-(rect_now_Y-Y)*ffy;
end;
//______________________________________________________________________________

procedure trail_rect_y2(Y:integer);

begin
  output_rectangle_y2:=output_rectangle_y_now-(rect_now_Y-Y)*ffy;
end;
//______________________________________________________________________________

procedure trail_rect_position(X,Y:integer);    // output boundary rectangle move

begin
  output_rectangle_x1:=output_rectangle_x_now-(rect_now_X-X)*ffx;
  output_rectangle_y1:=output_rectangle_y_now-(rect_now_Y-Y)*ffy;

  output_rectangle_x2:=output_rectangle_x1+output_rectangle_width;
  output_rectangle_y2:=output_rectangle_y1+output_rectangle_height;
end;
//______________________________________________________________________________

procedure trail_corner1(X,Y:integer);

begin
  shapes_shiftx_now:=(X-shapes_now_x)*ffx;
  shapes_shifty_now:=(Y-shapes_now_y)*ffy;

  shift_this_shape(shapes_shiftx_now-shifted_already_x,shapes_shifty_now-shifted_already_y,1);

  shifted_already_x:=shapes_shiftx_now;
  shifted_already_y:=shapes_shifty_now;
end;
//__________________________________________________________________________________________

procedure trail_corner2(X,Y:integer);

begin
  shapes_shiftx_now:=(X-shapes_now_x)*ffx;
  shapes_shifty_now:=(Y-shapes_now_y)*ffy;

  shift_this_shape(shapes_shiftx_now-shifted_already_x,shapes_shifty_now-shifted_already_y,2);

  shifted_already_x:=shapes_shiftx_now;
  shifted_already_y:=shapes_shifty_now;
end;
//__________________________________________________________________________________________

procedure trail_shape(X,Y:integer);

begin
  shapes_shiftx_now:=(X-shapes_now_x)*ffx;
  shapes_shifty_now:=(Y-shapes_now_y)*ffy;

  shift_this_shape(shapes_shiftx_now-shifted_already_x,shapes_shifty_now-shifted_already_y,0);

  shifted_already_x:=shapes_shiftx_now;
  shifted_already_y:=shapes_shifty_now;
end;
//__________________________________________________________________________________________

procedure trail_allshapes_shift(X,Y:integer);

begin
  shapes_shiftx_now:=(X-shapes_now_x)*ffx;
  shapes_shifty_now:=(Y-shapes_now_y)*ffy;

  shift_all_shapes(False,shapes_shiftx_now-shifted_already_x,shapes_shifty_now-shifted_already_y);    // False not sync 226b

  shifted_already_x:=shapes_shiftx_now;
  shifted_already_y:=shapes_shifty_now;
end;
//__________________________________________________________________________________________

procedure trail_allshapes_scale(Y:integer);

var
  shapes_scalefactor:extended;

begin
  if pad_form.ClientHeight<1 then EXIT;

  shapes_scalefactor:=1.0+(shapes_now_y-Y)/pad_form.ClientHeight;

  if shapes_scalefactor<0.01 then shapes_scalefactor:=0.01;   // 1/100th arbitrary limit in one go.

  if (shapes_scaled_already>minfp) and (shapes_scalefactor>minfp)
     then scale_all_shapes(shapes_scalefactor/shapes_scaled_already,shapes_scalefactor/shapes_scaled_already);

  shapes_scaled_already:=shapes_scalefactor;
end;
//__________________________________________________________________________________________

procedure trail_allshapes_rotate(Y:integer);

var
  shapes_rot:extended;

begin
  shapes_rot:=shapes_rot_now+twist_dir*(Y-shapes_now_y)*ffy*100/(mouse_rot_factor*fine_adjust*screenx);   // 100 arbitrary.
  rotate_all_shapes(False,False,shapes_rot-shapes_rotated_already);                                       // False,False = not sync from group rotate,  not picture shapes

  shapes_rotated_already:=shapes_rot;
end;
//__________________________________________________________________________________________

procedure trail_oneshape_scale(X:integer);

var
  shapes_scalefactor:extended;

begin
  if pad_form.ClientWidth<1 then EXIT;

  shapes_scalefactor:=1.0+(X-shapes_now_x)/pad_form.ClientWidth;

  if shapes_scalefactor<0.01 then shapes_scalefactor:=0.01;   // 1/100th arbitrary limit in one go.

  if (shapes_scaled_already>minfp) and (shapes_scalefactor>minfp)
     then scale_this_shape(shapes_scalefactor/shapes_scaled_already,shapes_scalefactor/shapes_scaled_already);

  shapes_scaled_already:=shapes_scalefactor;
end;
//_________________________________________________________________________________________

procedure trail_clip_size(X:integer);     // 229a

begin
  clip_size:=clip_size_now+(X-clip_size_now_X)*ffx;

  size_this_clip(clip_size);     // bgnd_unit
end;
//______________________________________________________________________________

procedure trail_clip_slab_angle(Y:integer);    // 229a

begin
  clip_angle:=clip_angle_now+(Y-clip_angle_now_Y)*ffy*300/(mouse_rot_factor*fine_adjust*screenx);   // 300 arbitrary

  rotate_this_clip_slab(clip_angle);  // bgnd_unit
end;
//______________________________________________________________________________

procedure trail_bunch(X,Y:integer);

begin
  bunching_jump:=bunching_jump_now+(bunch_now-X)*ffx;
  if bunching_jump<0 then bunching_jump:=0;

  bunching_shear:=bunching_shear_now+(Y-shear_now)*ffy;
end;
//__________________________________________________________________________________________

procedure trail_twist(Y:integer);

begin
  kform:=kform_now+twist_dir*(Y-shift_now_y)*ffy*hand_i*100/(mouse_rot_factor*fine_adjust*screenx);   // 100 arbitrary.
  normalize_kform;
end;
//___________________________________________________________________________________________

procedure trail_orbit(X:integer);

begin
  kform:=kform_now+orbit_dir*(X-shift_now_x)*ffx*100/(mouse_orbit_factor*fine_adjust*orbit_rad);   //  100 arbitrary.
  normalize_kform;
end;
//____________________________________________________________________________________________

procedure trail_zoom(X:integer);

var
  temp:extended;

begin
  if X<1 then X:=1;
  temp:=screenx;                                   // previous screenx.
  screenx:=screenx_zoom_now*(zoom_now/X);

  if screenx<screenx_min then screenx:=screenx_min; // minimum for screen width (max zoom in).
  if screenx>screenx_max then screenx:=screenx_max; // maximum zoom out.

  zoom_offsetx:=zoom_offsetx*screenx/temp;          // keep pad view zero lines stable.
  zoom_offsety:=zoom_offsety*screenx/temp;
end;
//________________________________________________________________________________________

procedure trail_spot(X,Yneg:integer);

var
  temp:extended;
  x_offset_mod:extended;

begin
  temp:=screeny/screenx;                            // aspect ratio

  if Yneg<1 then Yneg:=1;                           // no zero div.
  screenx:=screenx_spot_now*(spot_now/Yneg);

  if screenx<screenx_min then screenx:=screenx_min; // minimum for screen width (max zoom in).
                                                    // ( ! getting strange draw problems if zoom in any more).
  if screenx>screenx_max then screenx:=screenx_max; // maximum zoom out.

  x_offset_mod:=(screenx_spot_now-screenx)/2;
  zoom_offsetx:=zoom_offsetx_now+x_offset_mod;           // keep screen centre stable.
  zoom_offsety:=zoom_offsety_now+x_offset_mod*temp;

  if pad_form.spot_and_scale_menu_entry.Checked=True
     then begin
            screenx_zoom_now:=screenx;     // scale zoom also on X move.
            trail_zoom(X);
          end;
end;
//_______________________________________________________________________________________

procedure trail_datum(Y:integer);

begin
  y_datum:=y_datum_now-(datum_now-Y)/fy;        // fy is pixels per mm (fy -ve).
end;
//______________________________________________________________________________________

procedure trail_trans_start(X:integer);          // adjust transition start.

var
  old_os,dummy1,dummy2,dummy3,new_rad:extended;
  limit_code:integer;

begin
  old_os:=os;
  os:=os_now+(X-trans_start_now)/fx;

         // see if he wants to keep the spiral constant fixed by adjusting the 1st rad...

  if pad_form.zone_rollout_menu_entry.Checked=True     //!!! don't use clrad_at_x function (won't work when zone increasing).
     then begin
            if transcalcs(False,False,trans_k,ts1+os-old_os,dummy1,dummy2,dummy3,new_rad)=True   // get new 1st rad.
               then begin

                      if new_rad<0 then new_rad:=limits(0-max_rad,0-g*2,new_rad,limit_code)   // ensure radius within limits.
                                   else new_rad:=limits(g*2,max_rad,new_rad,limit_code);      // minimum 2*g arbitrary.

                      if ((os-old_os)<tst) and (limit_code=0)
                         then begin                       // don't let tst go negative or rads exceed limits.
                                nomrad1:=new_rad;         // ok, change 1st radius.
                                tst:=tst+old_os-os;       // keep transition unchanged.
                              end
                         else os:=old_os;       // not ok, no change.
                    end
               else os:=old_os;       // not ok, no change.
          end;
  peg_curve;            // keep transition on peg.
end;
//________________________________________________________________________________________

procedure trail_trans_length(X:integer);           // adjust transition length.

var
  old_tst,dummy1,dummy2,dummy3,new_rad:extended;
  limit_code:integer;

begin
  old_tst:=tst;     // in case we need to re-instate.

  tst:=tst_now+(X-trans_length_now)/fx;
  if tst<0 then tst:=0;

         // see if he wants to keep the spiral constant fixed by adjusting the 2nd rad...

  if pad_form.zone_rollout_menu_entry.Checked=True     //!!! don't use clrad_at_x function (won't work when zone increasing).
     then begin
            if tst<g then tst:=g; // 0.93.a  prevent crash in roll-out mode on very short transition zone. g arbitrary.

            if transcalcs(False,False,trans_k,(ts1+tst),dummy1,dummy2,dummy3,new_rad)=True   // get new 2nd rad.
               then begin

                      if new_rad<0 then new_rad:=limits(0-max_rad,0-g*2,new_rad,limit_code)   // ensure radius within limits.
                                   else new_rad:=limits(g*2,max_rad,new_rad,limit_code);      // minimum 2*g arbitrary.

                      if limit_code=0
                         then nomrad2:=new_rad          // ok, change 2nd radius.
                         else tst:=old_tst;             // not ok, no change.
                    end
               else tst:=old_tst;
          end;
  peg_curve;            // keep transition on peg.
end;
//________________________________________________________________________________________

procedure trail_slew_start(X:integer);           // adjust slewing start.

begin
  slew_s:=slew_s_now+(X-slew_start_now)/fx;      // neg OK.
  peg_curve;                                     // keep slew on peg.
end;
//________________________________________________________________________________________

procedure trail_slew_length(X:integer);           // adjust slewing length.

begin
  slew_l:=slew_l_now+(X-slew_length_now)/fx;
  if slew_l<ABS(slew) then slew_l:=ABS(slew);     // ??? arbitrary minimum. (can't go neg).
  if slew_l<1 then slew_l:=1;                     // 1 mm safety minimum (div by zero).
  peg_curve;                                      // keep slew on peg.
end;
//________________________________________________________________________________________

procedure trail_slew_amount(Y:integer);           // adjust amount of slew.

begin
  slew:=slew_now+(Y-slew_amount_now)*hand_i/fy;          // neg OK
  if ABS(slew)>slew_l then slew:=slew_l*SGZ(slew);       // arbitrary limit = slew length.
  peg_curve;                                             // keep slew on peg.
end;
//________________________________________________________________________________________

procedure trail_slew2_factor(X:integer);          // adjust slew mode 2 factor.

begin
  slew2_kmax:=slew2_kmax_now+(X-slew_factor_now)*6/(pad_form.ClientWidth+1);  // 6 arbitrary.

  if slew2_kmax<0.02 then slew2_kmax:=0.02;      // ??? arbitrary minimum. (can't go neg).
  if slew2_kmax>10 then slew2_kmax:=10;          // ??? arbitrary maximum.

  peg_curve;                                     // keep slew on peg.
end;
//________________________________________________________________________________________

procedure trail_printsize(X:integer);     // adjust print size.

begin
  if X<1 then X:=1;                       // prevent div 0.
  out_factor:=out_factor_now*(printsize_now_X/X);
end;
//__________________________________________________________________________________________

procedure trail_shove_along(X:integer);

begin
  shovex:=shovex_now+(X-shove_now_x)/fx/shove_mouse_factor/2;                      // /2 arbitrary.
  current_shoved_timbers[shove_index].shove_data.sv_x:=shovex;

  shovetimbx:=shovetimbx_now+(X-shovetimb_now)/fx/shove_mouse_factor/2;
end;
//______________________________________________________________________________

procedure trail_shove_throw(Y:integer);

begin
  shoveo:=shoveo_now+(Y-shove_now_y)*ffy*hand_i/shove_mouse_factor;
  current_shoved_timbers[shove_index].shove_data.sv_o:=shoveo;
end;
//________________________________________________________________________________________

procedure trail_shove_crab(X:integer);

begin
  shovec:=shovec_now+(X-shove_now_x)/fx/shove_mouse_factor/2;     // /2 arbitrary.
  current_shoved_timbers[shove_index].shove_data.sv_c:=shovec;
end;
//________________________________________________________________________________________

procedure trail_shove_length(Y:integer);

begin
  shovel:=shovel_now+(Y-shove_now_y)*ffy*hand_i/shove_mouse_factor;
  current_shoved_timbers[shove_index].shove_data.sv_l:=shovel;
end;
//________________________________________________________________________________________

procedure trail_shove_width(X:integer);

begin
  shovew:=shovew_now+(X-shove_now_x)/fx/shove_mouse_factor/4;                      // /4 arbitrary.
  current_shoved_timbers[shove_index].shove_data.sv_w:=shovew;
end;
//________________________________________________________________________________________

procedure trail_shove_twist(Y:integer);

begin
  shovek:=shovek_now+(Y-shove_now_y)*ffy*hand_i/(shove_mouse_factor*screenx);
  normalize_angle(shovek);
  current_shoved_timbers[shove_index].shove_data.sv_k:=shovek;
end;
//________________________________________________________________________________________

procedure set_xing_k_i;      // set selector list index to match current k3n.

begin

  if k3n<0.5 then k3n:=0.5;        // 228a

  xing_k_i:=16;  // default init "other".

  if ABS(k3n-4   )<minfp then xing_k_i:=0;   //  k3n:=4;
  if ABS(k3n-4.5 )<minfp then xing_k_i:=1;   //  k3n:=4.5;
  if ABS(k3n-4.75)<minfp then xing_k_i:=2;   //  k3n:=4.75;
  if ABS(k3n-5   )<minfp then xing_k_i:=3;   //  k3n:=5;
  if ABS(k3n-5.25)<minfp then xing_k_i:=4;   //  k3n:=5.25;
  if ABS(k3n-5.5 )<minfp then xing_k_i:=5;   //  k3n:=5.5;
  if ABS(k3n-5.75)<minfp then xing_k_i:=6;   //  k3n:=5.75;
  if ABS(k3n-6   )<minfp then xing_k_i:=7;   //  k3n:=6;
  if ABS(k3n-6.25)<minfp then xing_k_i:=8;   //  k3n:=6.25;
  if ABS(k3n-6.5 )<minfp then xing_k_i:=9;   //  k3n:=6.5;
  if ABS(k3n-7   )<minfp then xing_k_i:=10;   //  k3n:=7;
  if ABS(k3n-7.5 )<minfp then xing_k_i:=11;   //  k3n:=7.5;
  if ABS(k3n-8   )<minfp then xing_k_i:=12;   //  k3n:=8;
  if ABS(k3n-9   )<minfp then xing_k_i:=13;   //  k3n:=9;
  if ABS(k3n-10  )<minfp then xing_k_i:=14;   //  k3n:=10;
  if ABS(k3n-12  )<minfp then xing_k_i:=15;   //  k3n:=12;

  if xing_k_i=16            // not in the list...
     then other_k3n_i:=1    // other angle.
     else other_k3n_i:=0;

  xing_select_form.other_label.Caption:='1 : '+round_str(k3n,2);
end;
//___________________________________________________________________________________________

procedure trail_xing(X:integer);    //  adjust crossing size on new mouse X position.

var
  temp,dummy1,dummy2:extended;
  pc,pf:Tpex;

begin
  if xing_free=True                 //  ignore if he's locked it.
     then begin

            k3n:=k3n_now+(X-xing_now)*ffx*1000/(mouse_xing_factor*fine_adjust*screenx);   // 1000 arbitrary.

            if k3n<0.5 then k3n:=0.5;        // 227e   reduced bottom limit

            if half_diamond=False
               then begin
                      if f5_quarter_steps=True
                         then k3n:=INT(k3n*4+0.5)/4;       //  round unusual crossing angles to nearest .25.
                    end
               else begin
                      if f5_quarter_steps=True then k3n:=INT(k3n*4+0.5)/4;  //  round unusual crossing angles to nearest .25.
                      hdkn:=k3n;                                            // F5 maintains regular half-diamond (use F9, F10 for irregular)   
                    end;

            set_xing_k_i;

          end

     else begin
            temp:=X*12/(ABS(pad_form.ClientWidth)+1);  // range 0-12   (+1 in case div 0).

            switch_only:=Round(temp)+9;   //  so can change switch if crossing doesn't change. (range now 9-21)

          end;

  gocalc(0,0);   // recalc to get new pegx, pegy, arm_angle.

  docurving(False,True,pegx,pegy,pc.x,pc.y,dummy1,dummy2);  // first curve peg position.

  dotransform(kform,xform,yform,pc,pf);                // then rotate it (without shifts).

  xshift:=now_peg_x - pf.x;      // maintain peg position constant (now_peg includes the now_shift).
  yshift:=now_peg_y - pf.y;

  rotate_turnout(arm_angle_now-arm_angle,False);    // re-align to previous angle.
end;
//_______________________________________________________________________________________

procedure peg_curve;  // do curving calcs for the current peg position.
                      // called after radius changes, e.g. from F6 curving mouse action.
                      // N.B. this routine does not do a redraw - calcs only.
var
  peg_k:extended;
  peg600,peg_kkk:extended;
  dummy1,dummy2,dummy3:extended;
  pc,pf:Tpex;

begin
  if calc_curving=False then EXIT;    // calc new curving/transition constants.

  peg600:=0;  // for a curviform V-crossing in codes 241,600 (peg on TEXITP TOLP)   0.79.a  29-05-06

  case peg_code of
               15: pegx:=os;       // keep peg on transition start.
               16: pegx:=os+tst;   // keep peg on transition end.

          200,220: if (curving_mod=1)
                   or (swell_mod=1)
                   or (sx_mod=1)
                   or (vee_mod=1)
                   or (hdk_mod=1)
                   or (both_mod=1)
                   or (gaunt_offset_mod=1)
                   or (gaunt_radius_mod=1)   // 217b
                   or (entry_straight_mod=1)
                   or (turnout_road_mod=1)   // 213b
                      then gocalc(0,0);      // to keep peg on geometrical tangent points.

              241: if turnout_road_mod=1   // 241 = peg on TEXITP   213b
                      then begin
                             pegx:=xorg+turnout_road_endx;
                             pegy:=aq25offset(pegx,peg_kkk);
                             peg600:=now_peg600-peg_kkk;
                           end;

              300: pegx:=slew_s;          // keep peg on slewing start.
              301: pegx:=slew_s+slew_l;   // keep peg on slewing end.

              400: pegx:=os-ts1;   // keep peg on transition origin.

              600: if length_mod=1  // peg on TOLP    // 0.79.a
                      then begin
                             pegx:=turnoutx;
                             pegy:=aq25offset(pegx,peg_kkk);     // adjust peg angle for a curviform V-crossing.
                             peg600:=now_peg600-peg_kkk;
                           end;
  end;//case

  docurving(False,True,pegx,pegy,dummy1,dummy2,peg_k,dummy3);   // then calc new curving angle to peg.

  kform:=kform_now+now_peg_k-peg_k+peg600;     // adjust rotation angle to keep peg angle constant.  // 0.79.a
  normalize_kform;
                                               // then calc new peg position on pad...

  docurving(False,True,pegx,pegy,pc.x,pc.y,dummy1,dummy2);  // first curve it.

  dotransform(kform,xform,yform,pc,pf);                     // then rotate it.

  xshift:=now_peg_x - pf.x;      // finally maintain peg position constant (now_peg includes the now_shift).
  yshift:=now_peg_y - pf.y;
end;
//_______________________________________________________________________________________


procedure trail_gaunt_offset(Y:integer);

var
  dummy1,dummy2:extended;
  pc,pf:Tpex;
  old_dp,new_dp:extended;

begin
  old_dp:=dpx-xorg;

  gaunt_offset_in:=(gaunt_offset_now_mm+(Y-gaunt_now)*ffy*hand_i/(mouse_gaunt_offset_factor*fine_adjust*screenx))/inscale;

    // offset can't exceed the knuckle..

  if gaunt_offset_in>((g-fw-minfp)/inscale) then gaunt_offset_in:=(g-fw-minfp)/inscale;

    // or the entry straight if any (for curviform and generic crossings, th=g)..

  if gaunt_offset_in>((th-minfp)/inscale) then gaunt_offset_in:=(th-minfp)/inscale;

  gocalc(0,0);              // calc new dpx

  new_dp:=dpx-xorg;         // maintain overall length and V-crossing alignment
  xorg:=xorg+old_dp-new_dp;

  if xorg<0
     then begin
            turnoutx:=turnoutx-xorg;          // increase overall length to keep V-crossing and exit track.
            if spiral=True then os:=os-xorg;
            if slewing=True then slew_s:=slew_s-xorg;
            xorg:=0;
          end;

  if turnoutx>turnoutx_max then turnoutx:=turnoutx_max;
  if xorg>turnoutx then xorg:=turnoutx;

  gocalc(0,0);   // recalc to get new pegx, pegy, arm_angle.

  docurving(False,True,pegx,pegy,pc.x,pc.y,dummy1,dummy2);  // first curve peg position.

  dotransform(kform,xform,yform,pc,pf);      // then rotate it (without shifts).

  xshift:=now_peg_x - pf.x;      // maintain peg position constant (now_peg includes the now_shift).
  yshift:=now_peg_y - pf.y;

  rotate_turnout(arm_angle_now-arm_angle,False);    // re-align to previous angle.
end;
//_______________________________________________________________________________________

procedure trail_gaunt_curvature(Y:integer);

var
  dummy1,dummy2:extended;
  pc,pf:Tpex;
  old_dp,new_dp:extended;
  gaunt_rad,new_gaunt_offset:extended;

begin
  old_dp:=dpx-xorg;

  gaunt_curvature:=(gaunt_curvature_now+(Y-gaunt_rad_now)*ffy*hand_i/(mouse_gaunt_radius_factor*fine_adjust*screenx));

  try

    if gaunt_curvature>(1/(g*5))
       then gaunt_curvature:=1/(g*5);     // 5g radius bottom limit

    if gaunt_curvature<(1/(g*2000))
       then gaunt_curvature:=1/(g*2000);  // 2000g radius top limit (xorg becomes excessive)

    gaunt_rad:=1/gaunt_curvature+g/2;             // to rail.
    new_gaunt_offset:=gaunt_rad*(COS(k3)-1)+g;
  except
    EXIT;
  end;//try

  gaunt_offset_in:=new_gaunt_offset/inscale;

  gocalc(0,0);              // calc new dpx

  new_dp:=dpx-xorg;         // maintain overall length and V-crossing alignment
  xorg:=xorg+old_dp-new_dp;

  if xorg<0
     then begin
            turnoutx:=turnoutx-xorg;          // increase overall length to keep V-crossing and exit track.
            if spiral=True then os:=os-xorg;
            if slewing=True then slew_s:=slew_s-xorg;
            xorg:=0;
          end;

  if turnoutx>turnoutx_max then turnoutx:=turnoutx_max;
  if xorg>turnoutx then xorg:=turnoutx;

  gocalc(0,0);   // recalc to get new pegx, pegy, arm_angle.

  docurving(False,True,pegx,pegy,pc.x,pc.y,dummy1,dummy2);  // first curve peg position.

  dotransform(kform,xform,yform,pc,pf);      // then rotate it (without shifts).

  xshift:=now_peg_x - pf.x;      // maintain peg position constant (now_peg includes the now_shift).
  yshift:=now_peg_y - pf.y;

  rotate_turnout(arm_angle_now-arm_angle,False);    // re-align to previous angle.
end;
//_______________________________________________________________________________________

procedure trail_vee(X:integer);    //  F9 adjust V-crossing size on new mouse X position.

var
  temp,dummy1,dummy2,gaunt_rad,k3_angle:extended;
  pc,pf:Tpex;

  old_dp,new_dp:extended;

  old_k3n:extended;

  new_gaunt_offset:extended;

begin
  old_dp:=dpx-xorg;         // mods 217a for fixed gaunt radius ...
  gaunt_rad:=tradius;       // to gauge-face

  old_k3n:=k3n;

  k3n:=vee_now+(X-veexing_now)*ffx*1000/(mouse_xing_factor*fine_adjust*screenx);   // 1000 arbitrary.

  if f9_quarter_steps=True then k3n:=INT(k3n*4+0.5)/4;  //  round unusual V-crossing angles to nearest .25.

  if k3n<0.5 then k3n:=0.5;        // 228a

  if (gaunt=True) and (f9_gaunt_rad=True) and (xing_calc_i=1) and (half_diamond=False) and (plain_track=False) and (spiral=False)     // curviform only    217a ...
     then begin
            try
              k3_angle:=ARCTAN(1/k3n);
              new_gaunt_offset:=gaunt_rad*(COS(k3_angle)-1)+g;

              if new_gaunt_offset>(g-fw-minfp)
                 then begin                         // don't let gaunt_offset_in go out of range.
                        k3n:=old_k3n;
                        EXIT;
                      end
                 else gaunt_offset_in:=new_gaunt_offset/inscale;

              gocalc(0,0);              // calc new dpx

              new_dp:=dpx-xorg;         // maintain overall length and V-crossing alignment
              xorg:=xorg+old_dp-new_dp;

              if xorg<0
                 then begin
                        turnoutx:=turnoutx-xorg;          // increase overall length to keep V-crossing and exit track.
                        if spiral=True then os:=os-xorg;
                        if slewing=True then slew_s:=slew_s-xorg;
                        xorg:=0;
                      end;

              if turnoutx>turnoutx_max then turnoutx:=turnoutx_max;
              if xorg>turnoutx then xorg:=turnoutx;
            except
              do_nothing;
            end;//try
          end;

  if (half_diamond=True) and (f9_hd_rad=True) and (xing_calc_i=1) and (gaunt=False) and (plain_track=False) and (spiral=False)     // curviform only    218b ...
     then begin
            try
              k3_angle:=ARCTAN(1/k3n);
              hdk:=ARCCOS(COS(k3_angle)+g/tradius);
              hdkn:=1/TAN(hdk);
              if (creating_tandem=True) and (turnoutx<mvjpx) then turnoutx:=mvjpx;
            except
              do_nothing;
            end;//try
          end;

  gocalc(0,0);   // recalc to get new pegx, pegy, arm_angle.

  docurving(False,True,pegx,pegy,pc.x,pc.y,dummy1,dummy2);  // first curve peg position.

  dotransform(kform,xform,yform,pc,pf);                // then rotate it (without shifts).

  xshift:=now_peg_x - pf.x;      // maintain peg position constant (now_peg includes the now_shift).
  yshift:=now_peg_y - pf.y;

  rotate_turnout(arm_angle_now-arm_angle,False);    // re-align to previous angle.
end;
//_______________________________________________________________________________________

procedure trail_hdk(X:integer);    // F10 adjust K-crossing size on new mouse X position.

var
  temp,dummy1,dummy2:extended;
  pc,pf:Tpex;

begin
  if half_diamond=False then EXIT;    //??? no K-crossing?

  hdkn:=hdkn_now+(X-hdxing_now)*ffx*1000/(mouse_xing_factor*fine_adjust*screenx);   // 1000 arbitrary.

  if f10_quarter_steps=True then hdkn:=INT(hdkn*4+0.5)/4;  //  round unusual K-crossing angles to nearest .25.

  if hdkn<0.5 then hdkn:=0.5;  // 227e  1:0.5  63.43 degrees

  gocalc(0,0);   // recalc to get new pegx, pegy, arm_angle.

  docurving(False,True,pegx,pegy,pc.x,pc.y,dummy1,dummy2);  // first curve peg position.

  dotransform(kform,xform,yform,pc,pf);                // then rotate it (without shifts).

  xshift:=now_peg_x - pf.x;      // maintain peg position constant (now_peg includes the now_shift).
  yshift:=now_peg_y - pf.y;

  rotate_turnout(arm_angle_now-arm_angle,False);    // re-align to previous angle.
end;
//_______________________________________________________________________________________

procedure trail_curving(Y:integer);     //  adjust curving radius (pegged) on new mouse Y position.

var
  new_curvature:extended;
  dummy:integer;


begin
  new_curvature:=curvature_now+(Y-curving_now)*ffy*hand_i/(mouse_curv_factor*fine_adjust*scale*screenx);

  if Abs(new_curvature)>minfp
     then begin
            nomrad:=1/new_curvature;

            if nomrad<0 then nomrad:=limits(0-max_rad,0-g*2,nomrad,dummy)   // ensure radius within limits.
                        else nomrad:=limits(g*2,max_rad,nomrad,dummy);      // minimum 2*g arbitrary.

            new_curvature:=1/nomrad;
          end
     else begin
            nomrad:=max_rad;        // "straight"
            new_curvature:=0;
          end;

  if spiral=True
     then begin
            case adjust_trans_rad of
                               1: begin   // check spiral constant will not exceeed the limit, leave nomrad1 unchanged if so.

                                    temp:=nomrad-nomrad2;
                                    if ABS(temp)>minfp  // no change to nomrad1 if nomrad=nomrad2
                                       then begin
                                                    // no change to nomrad1 if spiral too gentle.

                                              if ABS(nomrad*nomrad2*tst/temp)<max_spiral_constant then nomrad1:=nomrad;
                                            end;
                                  end;

                               2: begin  // check spiral constant will not exceeed the limit, leave nomrad2 unchanged if so.

                                    temp:=nomrad1-nomrad;
                                    if ABS(temp)>minfp  // no change to nomrad2 if nomrad=nomrad1
                                       then begin
                                                     // no change to nomrad2 if spiral too gentle..

                                              if ABS(nomrad1*nomrad*tst/temp)<max_spiral_constant then nomrad2:=nomrad;
                                            end;
                                  end;
                             else run_error(197);
            end;//case
          end
     else begin                                     // constant radius
            if f6_swing_fixed=True      // 0.91.b   // maintain swing angle...
               then begin
                      turnoutx:=ABS(nomrad*f6_swing_angle);                  // nomrad may be negative.
                      if turnoutx>turnoutx_max then turnoutx:=turnoutx_max;
                      if plain_track=True then xorg:=turnoutx;               //  ensure xorg matches.
                      turnout_i:=1;                                          // length locked at new turnoutx.
                      gocalc(0,0);                                           // maintain peg position.
                    end;
          end;

  peg_curve;  // do curving calcs for the current peg position.
end;
//_________________________________________________________________________________________

procedure trail_swell(Y:integer);     //  adjust curving radius (swell) on new mouse Y position.

var
  new_curvature,temp:extended;
  dummy:integer;
  swing_sin:extended;

begin
  new_curvature:=curvature_now-(Y-curving_now)*ffy*hand_i/(mouse_curv_factor*fine_adjust*scale*screenx);

  swing_sin:=ends_apart_now*ABS(new_curvature)/2;      // SIN of swing angle.

  if swing_sin>(1-minfp) then EXIT;    // more than 180 degs swing, ignore until he reduces it.

  if Abs(new_curvature)>minfp
     then begin
            nomrad:=1/new_curvature;

            if nomrad<0 then nomrad:=limits(0-max_rad,0-g*2,nomrad,dummy)   // ensure radius within limits.
                        else nomrad:=limits(g*2,max_rad,nomrad,dummy);      // minimum 2*g arbitrary.

            new_curvature:=1/nomrad;
          end
     else begin
            nomrad:=max_rad;        // "straight"
            new_curvature:=0;
          end;

        // swell between fixed ends...
  try
    turnoutx:=ABS(nomrad)*ARCSIN(swing_sin)*2;
    kform:=kform_now+(curvature_now*turnoutx_now-new_curvature*turnoutx)/2;   // or for fixed ends.
    normalize_kform;
  except
    turnoutx:=turnoutx_now;
    kform:=kform_now;
    nomrad:=nomrad_now;
  end;//try

  if turnoutx>turnoutx_max then turnoutx:=turnoutx_max;
  if turnoutx<0 then turnoutx:=0;

  if plain_track=True then xorg:=turnoutx
                      else begin
                             if xorg>turnoutx then xorg:=turnoutx;
                             if xorg<0 then xorg:=0;
                           end;

  turnout_i:=1;    // lock length at new turnoutx.
end;
//_________________________________________________________________________________________

procedure  action_update(trail_str:string);

begin
  with action_form do begin
    trail_dim_label.Caption:=Trim(trail_str);

    trail_dim_label.Width:=Canvas.TextWidth(trail_dim_label.Caption)+2; // bug fix for Wine

    if (trail_dim_label.Left+trail_dim_label.Width)>ClientWidth then action_panel_resize;
  end;//with
end;
//_____________________________________________________________________________________________

procedure mouse_action_selected(caption_str,mouse_str,trail_str:string);

var
  mps:TPoint;
  i:integer;

begin
  hidden_on_store:=0;  // don't need to show the control automatically -- it will cancel the mouse action  205e

  if slide_on_store>0  // 206a cancel sliding after store
     then begin
            slide_on_store:=0;
            xshift:=stored_xshift;
            yshift:=stored_yshift;
          end;

  pad_form.cancel_mouse_action_popup_entry.Enabled:=True;

  pad_caption(caption_str);
  caption_add(trail_str);

  mouse_label_string:=mouse_str;

  with action_form do begin

    if mouse_click_action=0     // use drag mode only...
       then begin
              mouse_action_button_down:=True;                    // so start with button down.
              action_label.Caption:=' •  '+mouse_label_string;   // add bullet marker.
            end
       else action_label.Caption:=mouse_label_string;

    action_label.Width:=Canvas.TextWidth(action_label.Caption)+2;  // 205b bug fix for Wine

    action_update(trail_str);

    if mouse_labels=True
       then begin
              action_form.Tag:=0;        // allow it to resize for the new action.
              action_panel_resize;
              Show;
            end;

  end;//with

  pad_form.Cursor:=mouse_action_cursor;

  mouse_modify:=0;                         // 0=selected, but not in progress yet.

  if GetCursorPos(mps)=False then begin mps.X:=0; mps.Y:=0; end;

  SetCursorPos(mps.X, mps.Y);    // need a mouse move event to ensure pad cursor setting after any screen cursor change.
end;
//______________________________________________________________________________________

procedure pad_mouse_move(shift_state:TShiftState; X,Y:integer);

          // !!! N.B. also comes here on Mouse UP (after doing MouseUp).
var
  oppx,oppy:integer;
  mode,zoom_mode:integer;
  info_caption_str,jotter_abs_xy_str,jotter_rel_xy_str:string;
  trail_str:string;

  mouse_relx, mouse_rely:extended;
  measured_x, measured_y, measured_diag:extended;

  grid_factor:extended;
  grid_str:string;

                  /////////////////////////////////////

                  procedure opp_convert;

                  begin
                    if opposite_way=True                         // turnout facing opposite way ?
                       then begin
                              oppx:=pad_form.ClientWidth-X;      // so reverse sense of mouse movements.
                              oppy:=pad_form.ClientHeight-Y;
                            end
                       else begin
                              oppx:=X;
                              oppy:=Y;
                            end;
                    if oppx=0 then oppx:=1;    // prevent divisions by zero later (screen-edge clicked).
                    if oppy=0 then oppx:=1;
                  end;
                  /////////////////////////////////////

                  function do_edge_panning:boolean;

                  var
                    mps,mpp:TPoint;  // mouse position for edge scrolling.   whole screen, pad form.

                  begin
                    RESULT:=True;    // default = done some panning.

                    with pad_form do begin
                      if X>(ClientWidth-4)
                         then begin
                                lock_scaling_menu_entry.Click;

                                zoom_offsetx_now:=zoom_offsetx;
                                zoom_offsety_now:=zoom_offsety;

                                repeat
                                  pan_button_click(1.0,2,(ssCtrl in shift_state));
                                  if Application.Terminated=False then Application.ProcessMessages;
                                  if GetCursorPos(mps)=False then begin mps.X:=0; mps.Y:=0; end;
                                  mpp:=pad_form.ScreenToClient(mps);
                                until (mpp.X<(ClientWidth-4)) or (edge_panning_on_menu_entry.Checked=False) {or ((GetKeyState(VK_SCROLL)AND 1)<>0)} ;  // scroll locked - out 244b
                                EXIT;
                              end;

                      if (X<3) and (Y>(ClientHeight div 3))       // to avoid conflicts when accessing the control menu.
                         then begin
                                lock_scaling_menu_entry.Click;

                                zoom_offsetx_now:=zoom_offsetx;
                                zoom_offsety_now:=zoom_offsety;

                                repeat
                                  pan_button_click(1.0,1,(ssCtrl in shift_state));
                                  if Application.Terminated=False then Application.ProcessMessages;
                                  if GetCursorPos(mps)=False then begin mps.X:=0; mps.Y:=0; end;
                                  mpp:=pad_form.ScreenToClient(mps);
                                until (mpp.X>3) or (edge_panning_on_menu_entry.Checked=False) {or ((GetKeyState(VK_SCROLL)AND 1)<>0)} ;  // scroll locked - out 244b
                                EXIT;
                              end;

                      if Y>(ClientHeight-15)
                         then begin
                                lock_scaling_menu_entry.Click;

                                zoom_offsetx_now:=zoom_offsetx;
                                zoom_offsety_now:=zoom_offsety;

                                repeat
                                  pan_button_click(1.0,4,(ssCtrl in shift_state));
                                  if Application.Terminated=False then Application.ProcessMessages;
                                  if GetCursorPos(mps)=False then begin mps.X:=0; mps.Y:=0; end;
                                  mpp:=pad_form.ScreenToClient(mps);
                                until (mpp.Y<(ClientHeight-15)) or (edge_panning_on_menu_entry.Checked=False) {or ((GetKeyState(VK_SCROLL)AND 1)<>0)} ;  // scroll locked - out 244b
                                EXIT;
                              end;

                      if (Y<16) and (X>(ClientWidth-(ClientWidth div 5)))  // to avoid conflicts with the menu bar.
                         then begin
                                lock_scaling_menu_entry.Click;

                                zoom_offsetx_now:=zoom_offsetx;
                                zoom_offsety_now:=zoom_offsety;

                                repeat
                                  pan_button_click(1.0,3,(ssCtrl in shift_state));
                                  if Application.Terminated=False then Application.ProcessMessages;
                                  if GetCursorPos(mps)=False then begin mps.X:=0; mps.Y:=0; end;
                                  mpp:=pad_form.ScreenToClient(mps);
                                until (mpp.Y>16) or (edge_panning_on_menu_entry.Checked=False) {or ((GetKeyState(VK_SCROLL)AND 1)<>0)} ;  // scroll locked - out 244b
                                EXIT;
                              end;
                      RESULT:=False;     // no panning done.
                    end;//with pad_form
                  end;
                  /////////////////////////////////////////////////////////////

                  function jot_xy_text(d:extended):string;

                  begin
                    RESULT:=FormatFloat('###0.00;"- "###0.00',d*grid_factor);
                    if (omit_neg_brackets=False) and (d<0) then RESULT:='[ '+RESULT+' ]';
                  end;
                  //////////////////////////////////////////////////////////////////

begin

  if (X=old_mouse_move_X) and (Y=old_mouse_move_Y) then EXIT;  // 205e   no actual move

  old_mouse_move_X:=X;   // 205e
  old_mouse_move_Y:=Y;   // 205e

  if (shift_state=[ssMiddle]) or (allow_left_button_pan=True)    // 0.91.c    move pad on middle button down (or left).
     then begin
            zoom_offsetx:=zoom_offsetx_now-(X-zoff_now_x)*ffx;
            zoom_offsety:=zoom_offsety_now-(Y-zoff_now_y)*ffy;

            do_rollback:=False;       // no need to put this in rollback register on redraw.
            redraw(False);

            EXIT;
          end;

  if (mouse_modify>0) and (mouse_action_button_down=False) and (mouse_click_action<1)  // only need to do it once once (and if allowed).
     then begin
            if ( (ABS(X-pad_click_X)>10) or (ABS(Y-pad_click_Y)>10) ) and ( (ssLeft in shift_state=True) or (ssMiddle in shift_state=True) or (ssRight in shift_state=True) )
               then begin
                      mouse_action_button_down:=True;                                // moved with button down.
                      action_form.action_label.Caption:=' •  '+mouse_label_string;   // add bullet marker.
                      action_form.action_label.Width:=action_form.Canvas.TextWidth(action_form.action_label.Caption)+2;  // 205b bug fix for Wine
                    end;
          end;

  mouse_now_x:=mouse_x(X);     //  keep track of mouse position in mm.
  mouse_now_y:=mouse_y(X,Y);

  mouse_relx:=mouse_now_x-jotter_dx_org;
  mouse_rely:=mouse_now_y-jotter_dy_org;

  case jot_readout_units of
                       0: begin      // as grid
                            case grid_labels_code_i of
                                1: grid_str:='feet';     //  labels in feet.
                                2: grid_str:='inches';   //  labels in inches.
                                3: grid_str:='proto-feet'; //  labels in prototype feet.
                                4: grid_str:='cm';       //  labels in cm.
                                6: grid_str:='mm';       //  labels in mm.
                              else grid_str:='mm';       //  was run_error(223);
                            end;//case
                          end;
                       1: grid_str:='mm';
                       2: grid_str:='inches';
                     else grid_str:='mm';
  end;//case

  case jot_readout_units of
                      0: begin      // as grid
                           case grid_labels_code_i of
                                1: grid_factor:=1/304.8; //  labels in feet.
                                2: grid_factor:=1/25.4;  //  labels in inches.
                                3: grid_factor:=1/scale; //  labels in prototype feet.
                                4: grid_factor:=1/10;    //  labels in cm.
                                6: grid_factor:=1;       //  labels in mm.
                              else grid_factor:=1;       //  mm, was run_error(222);
                           end;//case
                         end;
                      1: grid_factor:=1.0;       // mm
                      2: grid_factor:=1/25.4;    // inches
                    else grid_factor:=1.0;       // mm
  end;//case

  info_caption_str:=' X : '+captext(mouse_now_x)+' mm     Y : '+captext(mouse_now_y)+' mm';
  jotter_abs_xy_str:='   X = '+jot_xy_text(mouse_now_x)+'     Y = '+jot_xy_text(mouse_now_y)+'  '+grid_str+' (grid)';  // abs dimensions for the jotter read-out.
  jotter_rel_xy_str:=' dX = '+jot_xy_text(mouse_relx)+'   dY = '+jot_xy_text(mouse_rely)+'   Diag = '+jot_xy_text(SQRT(SQR(mouse_relx)+SQR(mouse_rely)))+'  '+grid_str+' (moved)';  // rel dimensions for the jotter read-out.

  if Screen.Cursor<>crNone
     then begin
            info_form.mouse_now_panel.Caption:=info_caption_str;   // show current mouse position in the info.
            jotter_form.jotter_abs_xy_readout_panel.Caption:=jotter_abs_xy_str;    // 0.71.b 2-6-01
            jotter_form.jotter_rel_xy_readout_panel.Caption:=jotter_rel_xy_str;    // 0.71.c 4-6-01
          end
     else begin
            info_form.mouse_now_panel.Caption:=' ( mouse action )';
            jotter_form.jotter_abs_xy_readout_panel.Caption:=' ( mouse action )';
            jotter_form.jotter_rel_xy_readout_panel.Caption:=' ( mouse action )';
          end;

            // pad edge panning...

  if (pad_form.edge_panning_on_menu_entry.Checked=True) {and ((GetKeyState(VK_SCROLL)AND 1)=0)} and (mouse_modify<1)   // edge panning on and not locked off - out 244b
     then begin
            if do_edge_panning=True          // go do any edge-panning.
               then EXIT;

          end;

  if (mouse_draw_lines=True) or (draw_template_direct=True)     // 233b
     then begin
            caption_add(info_caption_str);     // show cross-hair position.
            if mouse_drawing_in_progress=True  // do a temporary drawn line...
               then begin

                      with pad_form.Canvas do begin
                        Pen.Width:=1;
                        Pen.Mode:=pmXor;              // so can erase

                        if bgnd_form.dotted_radio_button.Checked=True
                           then begin
                                  Brush.Color:=paper_colour;    // gaps in dotted lines
                                  Brush.Style:=bsClear;
                                  TextOut(0,0,'');      // !!! Delphi bug? This seems to be necessary before dotted lines will draw properly.
                                                        // TextOut obviously initialises some background mask property which I have been unable
                                                        // to find or set any other way.

                                  Pen.Style:=psDot;
                                end
                           else Pen.Style:=psSolid;

                        if check_dark_paper=False then Pen.Color:=paper_colour  // ditto with xor to give black line on any paper.
                                                  else Pen.Color:=clWhite;      // or white line on black paper.

                        MoveTo(draw_mouse_down_X,draw_mouse_down_Y);   // saved line starting point.
                        LineTo(temp_mouse_X,temp_mouse_Y);             // erase last temporary line.

                        MoveTo(draw_mouse_down_X,draw_mouse_down_Y);   // saved line starting point.
                        LineTo(X,Y);                                   // and draw a new one.
                      end;//with

                      temp_mouse_X:=X;    // for next time.
                      temp_mouse_Y:=Y;
                    end;
            EXIT;
          end;

  if (zoom_rectangle=True)
  or (group_fence_rectangle=True)
  or (output_boundary_rectangle=True)  // 0.93.a    // this is not a mouse action.
  or (drawn_shape_rectangle=True)
     then begin
            caption_add(info_caption_str);   // show cross-hair position.
            if (rectangle_in_progress=True)  // do a temporary rectangle...
               then begin

                      with pad_form.Canvas do begin

                        Pen.Width:=1;          // draw two rectangles (not for shapes) for bolder line, each with pen width 1,
                                               // this gives accurate square corners, unlike pen width 2.
                        Pen.Mode:=pmXor;       // so can erase
                        Pen.Style:=psSolid;

                        if check_dark_paper=False then Pen.Color:=paper_colour  // ditto with xor to give black line on any paper.
                                                  else Pen.Color:=clWhite;      // or white line on black paper.

                                // first erase last rectangles...

                        if rectangle_exists=True
                           then begin
                                  MoveTo(draw_mouse_down_X,draw_mouse_down_Y);   // saved inner rectangle top left.
                                  LineTo(temp_mouse_X,draw_mouse_down_Y);        // saved top edge.
                                  LineTo(temp_mouse_X,temp_mouse_Y);             // saved right edge.
                                  LineTo(draw_mouse_down_X,temp_mouse_Y);        // saved bottom edge.
                                  LineTo(draw_mouse_down_X,draw_mouse_down_Y);   // saved left edge.

                                  if drawn_shape_rectangle=False
                                     then begin
                                            MoveTo(draw_mouse_down_X-1,draw_mouse_down_Y-1);   // saved outer rectangle top left.
                                            LineTo(temp_mouse_X+1,draw_mouse_down_Y-1);        // saved top edge.
                                            LineTo(temp_mouse_X+1,temp_mouse_Y+1);             // saved right
                                            LineTo(draw_mouse_down_X-1,temp_mouse_Y+1);        // saved bottom edge..
                                            LineTo(draw_mouse_down_X-1,draw_mouse_down_Y-1);   // saved left edge.
                                          end;
                                end;

                                // then draw the new ones ...

                        MoveTo(draw_mouse_down_X,draw_mouse_down_Y);
                        LineTo(X,draw_mouse_down_Y);                   // new top edge.
                        LineTo(X,Y);                                   // new right edge.
                        LineTo(draw_mouse_down_X,Y);                   // new bottom edge.
                        LineTo(draw_mouse_down_X,draw_mouse_down_Y);   // new left edge.

                        if drawn_shape_rectangle=False
                           then begin
                                  MoveTo(draw_mouse_down_X-1,draw_mouse_down_Y-1);
                                  LineTo(X+1,draw_mouse_down_Y-1);                   // new top edge.
                                  LineTo(X+1,Y+1);                                   // new right edge.
                                  LineTo(draw_mouse_down_X-1,Y+1);                   // new bottom edge.
                                  LineTo(draw_mouse_down_X-1,draw_mouse_down_Y-1);   // new left edge.
                                end;

                        rectangle_exists:=True;
                      end;//with

                      temp_mouse_X:=X;    // for next time.
                      temp_mouse_Y:=Y;
                    end;
            EXIT;
          end;

  if (grid_form.Visible=True) and ((ring_mod=0) or (mouse_modify<1))    // no ring moving - measure to actual mouse position
     then begin
            measured_x:=mouse_now_x-measure_org_x;       // if ring moving must wait until after trail_ring to get its position.
            measured_y:=mouse_now_y-measure_org_y;

            measured_diag:=SQRT(SQR(measured_x)+SQR(measured_y));

            with grid_form do begin
              measuring_panel.Color:=clwhite;
              x_label.Caption:='X :   '+captext(measured_x)+'  mm';
              y_label.Caption:='Y :   '+captext(measured_y)+'  mm';
              diag_label.Caption:='Diagonal :   '+captext(measured_diag)+'  mm';
            end;//with
          end;

  if select_centre=True       // this is not a mouse action - just show cross-hair position.
     then begin
            caption_add(info_caption_str);
            EXIT;
          end;

  if mouse_modify<0 {adjust_in_progress=False}      // no mouse action, check for selection...
     then begin
            if ((GetKeyState(VK_CAPITAL) AND 1)<>0)     // caps lock on?
               //or (click_current=True)
               or (click_bgnd_select=True)
               or (shift_click=True)
               or (shift_state=[ssShift])

                                      // go find/highlight any background keep at this location.
               then begin
                      mouse_on_bgkeep(X,Y,False);      // (in keep_select)
                      EXIT;
                    end;
            //EXIT;
          end;

  if (shove_timber_form.Showing=True) and (mouse_modify<0) and (hide_current_flag=False) then mouse_on_timber_number(X,Y);  // see if it needs highlighting.

  if (check_diffs_form.Showing=True) and (mouse_modify<0) and (hide_current_flag=False) then mouse_on_check_label(X,Y);  // see if it needs highlighting.

  if (mouse_modify<0) and (hide_current_flag=False) and (brick_form.show_labels_checkbox.Checked=True) and (exp_chairing=True)
     then mouse_on_chair_label(X,Y);     // see if it needs highlighting   MW 05-08-2024  555a

             //   -----  mouse actions...

  if mouse_modify<1 then EXIT;   //  no mouse actions in progress.

          //  0.82.a  //  ignore adjust moves while wrong buttons down (Shift key or Left mouse button allowed)..

  if  (shift_state<>[])
  and (shift_state<>[ssLeft])
  and (shift_state<>[ssShift])
  and (shift_state<>[ssLeft,ssShift])
      then EXIT;

  if X=0 then X:=1;           //  prevent div 0 calc problems (mouse on screen-edge).
  if Y=0 then Y:=1;

  mode:=7;
  zoom_mode:=9;

  case mouse_modify of

             1: begin
                  opp_convert;

                  trail_curving(oppy);      // first change the curving.
                  trail_xing(oppx);         // then the size.

                                            // both size and curving

                  if draw_mode<>2 then mode:=zoom_mode;                 // free-scaling - change mode.

                  if trace_mouse=True then gocalc(2,mode{+first_click})
                                      else init_resize;                 // ensure switch mods if no tracing.

                  if spiral=True then trail_str:=captext(nomrad1)+' mm  /  '+captext(nomrad2)+' mm'
                                 else trail_str:=captext(nomrad)+' mm';
                end;

             2: begin
                  opp_convert;
                  trail_xing(oppx);      // F5  size

                  if draw_mode<>2 then mode:=zoom_mode;    // free-scaling - change mode.

                  if trace_mouse=True then gocalc(2,mode{+first_click})
                                      else init_resize;           // ensure switch mods if no tracing.

                  trail_str:='1 : '+captext(k3n);
                end;

             3: begin                            // F6  curving...
                  opp_convert;
                  trail_curving(oppy);

                  if trace_mouse=True then gocalc(2,mode{+first_click});

                  if spiral=True then trail_str:=captext(nomrad1)+' mm  /  '+captext(nomrad2)+' mm'
                                 else trail_str:=captext(nomrad)+' mm';
                end;

             4: begin
                  opp_convert;
                  trail_length(oppx);    // F4  overall length

                  if draw_mode<>2 then mode:=zoom_mode;                  // free-scaling - change mode.
                  if trace_mouse=True then gocalc(2,mode{+first_click});
                  trail_str:=captext(turnoutx)+' mm';
                end;

             5: begin
                  if f7_beginner=True
                     then f7_snap_allow:= NOT (ssShift in shift_state)   // F7 snapping 0.82.a
                     else f7_snap_allow:= (ssShift in shift_state);      // 215a

                  trail_shift(X,Y);      // F7  shift

                  if trace_mouse=True then gocalc(2,mode{+first_click});
                  trail_str:='X : '+captext(xshift)+' mm      Y : '+captext(yshift)+' mm';
                end;

             6: begin
                  trail_twist(Y);        // F8  rotate

                  if trace_mouse=True then gocalc(2,mode{+first_click});
                  trail_str:='by : '+captext((kform-kform_start)*hand_i*180/Pi)+' degrees.   peg  at : '+captext(arm_angle*hand_i*180/Pi)+' degrees'+k_ram_str(arm_angle*hand_i);
                end;

             7: begin
                  trail_zoom(X);         // scale zoom

                  if trace_mouse=True then gocalc(2,zoom_mode{+first_click});
                  trail_str:='pad  width : '+captext(screenx)+' mm';
                end;

             8: begin
                  opp_convert;
                  trail_approach(oppx);  // F3  approach length (F3 / F4  plain-track length)

                  if draw_mode<>2 then mode:=zoom_mode;    // free-scaling - change mode.

                  if trace_mouse=True then gocalc(2,mode{+first_click});
                  trail_str:=captext(xorg)+' mm';
                end;

             9: begin
                  trail_datum(Y);        // datum

                  mode:=zoom_mode;       // free-scaling - change mode.

                  if trace_mouse=True then gocalc(2,mode{+first_click});
                  trail_str:=captext(y_datum)+' mm';
                end;

            10: begin
                  opp_convert;
                  trail_trans_length(oppx);   // SHIFT+CTRL-F4  transition length

                  if trace_mouse=True then gocalc(2,mode{+first_click});
                  trail_str:=captext(tst)+' mm';
                end;

            11: begin
                  trail_zoffset(X,Y);    // move pad origin

                  if trace_mouse=True then gocalc(2,zoom_mode{+first_click});
                  trail_str:=' X : '+captext(zoom_offsetx)+' mm   Y : '+captext(zoom_offsety)+' mm';
                end;

            12: begin
                  trail_spot(X,pad_form.ClientHeight-Y);    // neg, so up screen zooms in.
                                                            // CTRL-F1  spot zoom

                  if trace_mouse=True then gocalc(2,zoom_mode{+first_click});
                  trail_str:='pad  width : '+captext(screenx)+' mm';
                end;

            13: begin
                  if peg_rail<>0            // peg moves along a rail.
                     then begin
                            opp_convert;
                            trail_peg(oppx,oppy);  // CTRL-F8  move peg along.
                          end
                     else trail_peg(X,Y);          // CTRL-F8  peg moves free.

                  if trace_mouse=True
                     then begin
                            gocalc(2,mode{+first_click});
                            //if notch_linked_to_peg=True then new_notch(get_peg_for_notch,True);
                          end;

                  trail_str:='X : '+captext(pad_peg_point.x)+' mm   Y : '+captext(pad_peg_point.y)+' mm';
                end;

            14: begin
                  trail_orbit(X);        // CTRL-F5  orbit

                  if trace_mouse=True then gocalc(2,mode{+first_click});
                  trail_str:='peg  at : '+captext(arm_angle*hand_i*180/Pi)+' degrees'+k_ram_str(arm_angle*hand_i);
                end;

            15: begin
                  trail_notch(X,Y);      // move notch

                  if mode=1 then mode:=3;      // otherwise doesn't erase on pad.

                  if trace_mouse=True then gocalc(2,mode{+first_click});

                  trail_str:='X : '+captext(notchx)+' mm   Y : '+captext(notchy)+' mm';
                end;

            16: begin
                  opp_convert;
                  trail_trans_start(oppx);  // SHFT+CTRL-F3  transition start

                  if trace_mouse=True then gocalc(2,mode{+first_click});
                  trail_str:=captext(os)+' mm';
                end;

            17: begin
                  trail_shift_keeps(X,Y);   // SHFT+CTRL-F7  shift group

                  if mode=1 then mode:=3;                                // otherwise doesn't erase on pad.
                  if trace_mouse=True then gocalc(2,mode{+first_click});
                  trail_str:='X  by : '+captext(xshift_keeps)+' mm   Y  by : '+captext(yshift_keeps)+' mm';
                end;

            18: begin
                  trail_twist_keeps(Y);     // SHIFT+CTRL-F8  rotate group

                  if mode=1 then mode:=3;                                // otherwise doesn't erase on pad.
                  if trace_mouse=True then gocalc(2,mode{+first_click});
                  trail_str:='by : '+captext(0-kform_keeps*180/Pi)+' degrees'+k_ram_str(0-kform_keeps);  // change of sign needed because of ??
                end;

            19: begin
                  trail_ring(X,Y);       // move ring

                  if mode=1 then mode:=3;                                // otherwise doesn't erase on pad.
                  if trace_mouse=True then gocalc(2,mode{+first_click});
                  trail_str:='X : '+captext(rings[0,0])+' mm   Y : '+captext(rings[0,1])+' mm';


                  if grid_form.Visible=True   // ring moving - can measure to ring now its been moved.
                     then begin
                            measured_x:=rings[0,0]-measure_org_x;      // ring moving - measure to ring.
                            measured_y:=rings[0,1]-measure_org_y;

                            measured_diag:=SQRT(SQR(measured_x)+SQR(measured_y));

                            with grid_form do begin
                              measuring_panel.Color:=clYellow;
                              x_label.Caption:='X :   '+captext(measured_x)+'  mm';
                              y_label.Caption:='Y :   '+captext(measured_y)+'  mm';
                              diag_label.Caption:='Diagonal :   '+captext(measured_diag)+'  mm';
                            end;//with
                          end;
                end;

            20: begin
                  opp_convert;
                  trail_slide(oppx);     // SHIFT+F9  slide

                  if trace_mouse=True then gocalc(2,mode{+first_click});
                  trail_str:='X : '+captext(0-pegx)+' mm';
                end;

            21: begin
                  opp_convert;
                  trail_slew_start(oppx);    // SHIFT+CTRL-F5  slew start

                  if trace_mouse=True then gocalc(2,mode{+first_click});
                  trail_str:=captext(slew_s)+' mm';
                end;

            22: begin
                  opp_convert;
                  trail_slew_length(oppx);   // SHIFT+CTRL-F6  slew length';

                  if trace_mouse=True then gocalc(2,mode{+first_click});
                  trail_str:=captext(slew_l)+' mm';
                end;

            23: begin
                  opp_convert;
                  trail_slew_amount(oppy);   // CTRL-F7  slew amount

                  if trace_mouse=True then gocalc(2,mode{+first_click});
                  trail_str:=captext(slew)+' mm';
                end;

            24: begin
                  trail_printsize(X);     // adjust print size.

                  if trace_mouse=True then gocalc(2,zoom_mode);
                  trail_str:=captext(out_factor*100)+' %';
                end;

            25: begin
                  trail_shove_along(X);          // shove along

                  if trace_mouse=True then gocalc(2,mode{+first_click});
                  trail_str:=current_shove_str+'  along  by : '+captext(shovex)+' mm';
                end;

            26: begin
                  trail_shove_throw(Y);         // shove throw

                  if trace_mouse=True then gocalc(2,mode{+first_click});
                  trail_str:=current_shove_str+'  throw  by : '+captext(shoveo)+' mm';
                end;

            27: begin
                  opp_convert;
                  trail_roam(oppx);      // CTRL-F9  roam along length

                  if draw_mode<>2 then mode:=zoom_mode;    // free-scaling - change mode.

                  if trace_mouse=True then gocalc(2,mode{+first_click});
                  trail_str:='approach  length : '+captext(xorg)+' mm';
                end;

            28: begin
                  opp_convert;
                  trail_slew2_factor(oppx);  // mode 2 slew factor

                  if trace_mouse=True then gocalc(2,mode{+first_click});
                  trail_str:=captext(slew2_kmax*50);
                end;

            29: begin
                  trail_bunch(X,Y);   // pad bunching

                  if trace_mouse=True then gocalc(2,zoom_mode{+first_click});
                  trail_str:='bunched  by  (X) : '+captext(bunching_jump)+' mm    sheared  by  (Y) : '+captext(bunching_shear)+' mm';
                end;

            30: begin
                  trail_porg(X,Y);    // SHIFT+CTRL-F10  page origin

                  if trace_mouse=True then gocalc(2,zoom_mode{+first_click});
                  trail_str:='X : '+captext(print_pages_top_origin)+' mm   Y : '+captext(print_pages_left_origin)+' mm';
                end;

            31: begin
                  trail_corner1(X,Y);    // shape corner 1

                  if trace_mouse=True then gocalc(2,zoom_mode{+first_click});
                  trail_str:='by  X : '+captext(shapes_shiftx_now)+' mm   Y : '+captext(shapes_shifty_now)+' mm';
                end;

            32: begin
                  trail_corner2(X,Y);    // shape corner 2

                  if trace_mouse=True then gocalc(2,zoom_mode{+first_click});
                  trail_str:='by  X : '+captext(shapes_shiftx_now)+' mm   Y : '+captext(shapes_shifty_now)+' mm';
                end;

            33: begin
                  trail_shape(X,Y);      // shift shape

                  if trace_mouse=True then gocalc(2,zoom_mode{+first_click});
                  trail_str:='by  X : '+captext(shapes_shiftx_now)+' mm   Y : '+captext(shapes_shifty_now)+' mm';
                end;

            34: begin
                  trail_allshapes_shift(X,Y);  // shift all shapes

                  if trace_mouse=True then gocalc(2,zoom_mode{+first_click});
                  trail_str:='by  X : '+captext(shapes_shiftx_now)+' mm   Y : '+captext(shapes_shifty_now)+' mm';
                end;

            35: begin
                  opp_convert;
                  trail_snake(oppx);     // CTRL-F6 snake.

                  if trace_mouse=True then gocalc(2,mode{+first_click});
                  trail_str:='X : '+captext(0-pegx)+' mm';
                end;

            36: begin
                  trail_allshapes_scale(Y);  // scale all shapes

                  if trace_mouse=True then gocalc(2,zoom_mode{+first_click});
                  trail_str:='by : '+captext(shapes_scaled_already*100)+' %';
                end;

            37: begin
                  trail_allshapes_rotate(Y);  // rotate all shapes

                  if trace_mouse=True then gocalc(2,zoom_mode{+first_click});
                  trail_str:='by : '+captext(shapes_rotated_already*180/Pi)+' degrees';
                end;

            38: begin
                  trail_oneshape_scale(X);  // scale one shape.

                  if trace_mouse=True then gocalc(2,zoom_mode{+first_click});
                  trail_str:='by : '+captext(shapes_scaled_already*100)+' %';
                end;


            39: begin
                  trail_shove_length(Y);          // shove length

                  if trace_mouse=True then gocalc(2,mode{+first_click});
                  trail_str:=current_shove_str+'  lengthened  by : '+captext(shovel)+' mm';
                end;

            40: begin
                  trail_shove_width(X);          // shove width

                  if trace_mouse=True then gocalc(2,mode{+first_click});
                  trail_str:=current_shove_str+'  widened  by : '+captext(shovew*2)+' mm';
                end;

            41: begin
                  trail_shove_twist(Y);          // shove twist

                  if trace_mouse=True then gocalc(2,mode{+first_click});
                  trail_str:=current_shove_str+'  twisted  by : '+captext(shovek*180/Pi)+' degrees'+k_ram_str(shovek);
                end;

            42: begin
                  opp_convert;
                  trail_blanking(oppx);  // blanking length

                  if trace_mouse=True then gocalc(2,mode{+first_click});
                  trail_str:=captext(startx)+' mm';
                end;

            43: begin
                  opp_convert;
                  trail_roll(oppx);      // CTRL-F4  roll rails and sleepers

                  if trace_mouse=True then gocalc(2,mode{+first_click});
                  trail_str:='rolled - in : '+captext(tb_roll_percent)+' %  of  a  rail';
                end;

            44: begin
                  trail_ringdia(Y);         // adjust ring diameter.
                  if mode=1 then mode:=3;   // otherwise doesn't erase on pad.
                  if trace_mouse=True then gocalc(2,mode{+first_click});
                  trail_str:='inner  dia : '+captext(ring_dia)+' mm';
                end;


            45: begin
                  trail_ruler1(X,Y);       // move ruler start end.

                  if mode=1 then mode:=3;                                // otherwise doesn't erase on pad.
                  if trace_mouse=True then gocalc(2,mode{+first_click});
                  trail_str:='X : '+captext(ruler_startx)+' mm   Y : '+captext(ruler_starty)+' mm';
                end;

            46: begin
                  trail_ruler2(X,Y);       // move ruler start end.

                  if mode=1 then mode:=3;                                // otherwise doesn't erase on pad.
                  if trace_mouse=True then gocalc(2,mode{+first_click});
                  trail_str:='X : '+captext(ruler_endx)+' mm   Y : '+captext(ruler_endy)+' mm';
                end;

            47: begin                            // curving swell...
                  opp_convert;
                  trail_swell(oppy);
                  if trace_mouse=True then gocalc(2,mode{+first_click});
                  trail_str:=captext(nomrad)+' mm';
                end;

            48: begin
                  trail_shove_crab(X);         // shove crab

                  if trace_mouse=True then gocalc(2,mode{+first_click});
                  trail_str:=current_shove_str+'  crab  by : '+captext(shovec)+' mm';
                end;

            49: begin      // move name labels  // 0.82.d

                  trail_shift_labels(X,Y);
                  if trace_mouse=True then gocalc(2,zoom_mode{+first_click});
                  trail_str:='X  by : '+captext(xshift_labels)+' mm   Y  by : '+captext(yshift_labels)+' mm';
                end;



   // 0.93.a mouse_modify matches repeat_last_index greater than 49 (see 99)...

            50: begin              // TS platform start  0.93.a
                  opp_convert;
                  trail_platform_ts_start_mm(oppx);

                  if trace_mouse=True then gocalc(2,mode);
                  trail_str:=captext(platform_ts_start_mm)+' mm';
                end;

            51: begin              // TS platform length  0.93.a
                  opp_convert;
                  trail_platform_ts_length_mm(oppx);

                  if trace_mouse=True then gocalc(2,mode);
                  trail_str:=captext(platform_ts_length_mm)+' mm';
                end;

            52: begin              // TS platform starting width  0.93.a
                  opp_convert;
                  trail_platform_ts_start_width_ins(oppy);

                  if trace_mouse=True then gocalc(2,mode);
                  trail_str:=captext(platform_ts_start_width_ins)+'  inches full-size';
                end;

            53: begin              // TS platform ending width  0.93.a
                  opp_convert;
                  trail_platform_ts_end_width_ins(oppy);

                  if trace_mouse=True then gocalc(2,mode);
                  trail_str:=captext(platform_ts_end_width_ins)+'  inches full-size';
                end;

            54: begin              // MS platform start  0.93.a
                  opp_convert;
                  trail_platform_ms_start_mm(oppx);

                  if trace_mouse=True then gocalc(2,mode);
                  trail_str:=captext(platform_ms_start_mm)+' mm';
                end;

            55: begin              // MS platform length  0.93.a
                  opp_convert;
                  trail_platform_ms_length_mm(oppx);

                  if trace_mouse=True then gocalc(2,mode);
                  trail_str:=captext(platform_ms_length_mm)+' mm';
                end;

            56: begin              // MS platform starting width  0.93.a
                  opp_convert;
                  trail_platform_ms_start_width_ins(oppy);

                  if trace_mouse=True then gocalc(2,mode);
                  trail_str:=captext(platform_ms_start_width_ins)+'  inches full-size';
                end;

            57: begin              // MS platform ending width  0.93.a
                  opp_convert;
                  trail_platform_ms_end_width_ins(oppy);

                  if trace_mouse=True then gocalc(2,mode);
                  trail_str:=captext(platform_ms_end_width_ins)+'  inches full-size';
                end;



            58: begin                  // SHIFT-F12   0.93.a
                  opp_convert;
                  trail_gaunt_offset(oppy);      // gaunt offset
                  if trace_mouse=True then gocalc(2,mode);
                  trail_str:=captext(gaunt_offset_in)+'  inches full-size';
                end;//case 58


            59: begin                  // F9   0.93.a
                  opp_convert;
                  trail_vee(oppx);      // V-crossing angle.
                  if trace_mouse=True then gocalc(2,mode);
                  trail_str:='1 : '+captext(k3n);
                end;

            60: begin                 // F10   0.93.a
                  opp_convert;
                  trail_hdk(oppx);      // K-crossing angle.
                  if trace_mouse=True then gocalc(2,mode);
                  trail_str:='1 : '+captext(hdkn);
                end;


            61: begin
                  trail_rect_x1(X);    // output boundary rectangle left

                  if trace_mouse=True then gocalc(2,zoom_mode);
                  trail_str:='X : '+captext(output_rectangle_x1)+' mm';
                end;

            62: begin
                  trail_rect_x2(X);    // output boundary rectangle right

                  if trace_mouse=True then gocalc(2,zoom_mode);
                  trail_str:='X : '+captext(output_rectangle_x2)+' mm';
                end;

            63: begin
                  trail_rect_y2(Y);    // output boundary rectangle top

                  if trace_mouse=True then gocalc(2,zoom_mode);
                  trail_str:='Y : '+captext(output_rectangle_y2)+' mm';
                end;

            64: begin
                  trail_rect_y1(Y);    // output boundary rectangle top

                  if trace_mouse=True then gocalc(2,zoom_mode);
                  trail_str:='Y : '+captext(output_rectangle_y1)+' mm';
                end;

            65: begin
                  trail_rect_position(X,Y);    // output boundary rectangle move

                  if trace_mouse=True then gocalc(2,zoom_mode);
                  trail_str:='X : '+captext(output_rectangle_x1)+' mm   Y : '+captext(output_rectangle_y1)+' mm';
                end;

            66: begin              // check diff length 0.94.a
                  opp_convert;
                  trail_check_len_mm(oppx);

                  if trace_mouse=True then gocalc(2,mode);
                  trail_str:='adjust  by : '+captext(mouse_diff.len_diff*inscale)+' mm';
                end;

            67: begin              // check diff flare length 0.94.a
                  opp_convert;
                  trail_check_flare_mm(oppx);

                  if trace_mouse=True then gocalc(2,mode);
                  trail_str:='adjust  by : '+captext(mouse_diff.flr_diff*inscale)+' mm';
                end;

            68: begin              // check diff end gap 0.94.a
                  opp_convert;
                  trail_check_gap_mm(oppy);

                  if trace_mouse=True then gocalc(2,mode);
                  trail_str:='adjust  by : '+captext(mouse_diff.gap_diff)+' mm';
                end;

            69: begin              // dummy vehicle start
                  opp_convert;
                  trail_dv_start(oppx);

                  if trace_mouse=True then gocalc(2,mode);
                  trail_str:='roll  to :  '+captext(cdvi.dv_start*inscale)+' mm';
                end;

            70: begin              // dummy vehicle clearance
                  opp_convert;
                  trail_dv_clearance(oppy);

                  if trace_mouse=True then gocalc(2,mode);
                  trail_str:=captext(cdvi.dv_clearance)+' inches  ( '+captext(cdvi.dv_clearance*inscale)+' mm )';
                end;

                    // 207a ...

            71: begin              // MS platform skew at start
                  opp_convert;
                  trail_platform_ms_start_skew_mm(oppx);

                  if trace_mouse=True then gocalc(2,mode);
                  trail_str:=captext(platform_ms_start_skew_mm)+' mm';
                end;

            72: begin              // MS platform skew at end
                  opp_convert;
                  trail_platform_ms_end_skew_mm(oppx);

                  if trace_mouse=True then gocalc(2,mode);
                  trail_str:=captext(platform_ms_end_skew_mm)+' mm';
                end;

            73: begin              // TS platform skew at start
                  opp_convert;
                  trail_platform_ts_start_skew_mm(oppx);

                  if trace_mouse=True then gocalc(2,mode);
                  trail_str:=captext(platform_ts_start_skew_mm)+' mm';
                end;

            74: begin              // TS platform skew at end
                  opp_convert;
                  trail_platform_ts_end_skew_mm(oppx);

                  if trace_mouse=True then gocalc(2,mode);
                  trail_str:=captext(platform_ts_end_skew_mm)+' mm';
                end;

            75: begin                  // turnout road CTRL+F12
                  opp_convert;
                  trail_turnout_road(oppx);

                  if trace_mouse=True then gocalc(2,mode);
                  trail_str:=captext(xorg+turnout_road_endx)+' mm';
                end;


            76: begin              // adjacent centres TS  213a
                  opp_convert;
                  trail_adj_centres_ts(oppy);

                  if trace_mouse=True then gocalc(2,mode);
                  trail_str:=captext(cpi.trtscent_pi)+' mm';
                end;

            77: begin              // adjacent centres MS  213a
                  opp_convert;
                  trail_adj_centres_ms(oppy);

                  if trace_mouse=True then gocalc(2,mode);
                  trail_str:=captext(cpi.trmscent_pi)+' mm';
                end;


            78: begin              // TS trackbed start  215a ...
                  opp_convert;
                  trail_trackbed_ts_start_mm(oppx);

                  if trace_mouse=True then gocalc(2,mode);
                  trail_str:=captext(trackbed_ts_start_mm)+' mm';
                end;

            79: begin              // TS trackbed length
                  opp_convert;
                  trail_trackbed_ts_length_mm(oppx);

                  if trace_mouse=True then gocalc(2,mode);
                  trail_str:=captext(trackbed_ts_length_mm)+' mm';
                end;


            80: begin              // MS trackbed start
                  opp_convert;
                  trail_trackbed_ms_start_mm(oppx);

                  if trace_mouse=True then gocalc(2,mode);
                  trail_str:=captext(trackbed_ms_start_mm)+' mm';
                end;

            81: begin              // MS trackbed length
                  opp_convert;
                  trail_trackbed_ms_length_mm(oppx);

                  if trace_mouse=True then gocalc(2,mode);
                  trail_str:=captext(trackbed_ms_length_mm)+' mm';
                end;

            82: begin                  // main road exit  217a
                  opp_convert;
                  trail_main_road(oppx);

                  if trace_mouse=True then gocalc(2,mode);
                  trail_str:=captext(xorg+main_road_endx)+' mm';
                end;

            83: begin                  // gaunt radius  217b
                  opp_convert;
                  trail_gaunt_curvature(oppy);
                  if trace_mouse=True then gocalc(2,mode);
                  trail_str:=captext(tradius-g/2{1/gaunt_curvature})+' mm';
                end;

            84: begin
                  if locator_rail<>0            // locator moves along a rail.
                     then begin
                            opp_convert;
                            trail_locator(oppx,oppy);  // move locator along.
                          end
                     else trail_locator(X,Y);          // locator moves free.

                  if trace_mouse=True then gocalc(2,mode);

                  trail_str:='X : '+captext(pad_locator_point.x)+' mm   Y : '+captext(pad_locator_point.y)+' mm';
                end;

            85: begin
                  trail_clip_size(X);

                  if trace_mouse=True then gocalc(2,zoom_mode);
                  trail_str:=captext(clip_size)+' mm';
                end;

            86: begin
                  trail_clip_slab_angle(Y);

                  if trace_mouse=True then gocalc(2,zoom_mode);
                  trail_str:=captext(clip_angle*180/Pi)+' degs';
                end;

            87: begin
                  opp_convert;
                  trail_pt_blanking(oppx);  // plain track blanking length

                  if trace_mouse=True then gocalc(2,mode);
                  trail_str:=captext(pt_blank)+' mm';
                end;



   // 0.93.a  99 was 50 ...

            99: begin           // adjust crossing entry straight  0.91.b
                  opp_convert;
                  trail_entry_straight(oppx);  // entry straight length

                  if trace_mouse=True then gocalc(2,mode{+first_click});
                  trail_str:=captext(fixed_sl)+' mm';
                end;



           else EXIT;  // 217b run_error(91);
  end;//case

  caption_add(trail_str);
  action_update(trail_str);

  do_linked_notch;   // link notch if wanted.

end;
//__________________________________________________________________________________________

procedure do_linked_notch;       // link notch to control template.

var
  temp_y, temp_k, dummy, linkx, linky, linkangle, link_arm_angle:extended;
  dummy_str:string;

  // 217b ..

  curving_rad,mod_linkangle,shrink_factor,tanx:extended;

begin

  if notch_linked_to_current=True   // notch linked to template?
     then begin

            old_notchx:=notchx;       // for group linking...
            old_notchy:=notchy;
            old_notch_angle:=notch_angle;

            linkx:=pegx;    //  default inits...
            linky:=pegy;
            linkangle:=pegangle;

                            // get notch data at the required peg position...

            dummy_str:=calc_peg_dims(notch_linked_code,linkx,linky,linkangle);    // don't need string result.
            normalize_angle(linkangle);
            docurving(True,True,linkx,linky,notchx,temp_y,temp_k,curving_rad);
            notchy:=temp_y*hand_i+y_datum;

              // 217b  gradient correction for radial shrink/enlarge ...

            if ABS(linkangle)<minfp
               then mod_linkangle:=linkangle
               else begin
                      try
                        shrink_factor:=(curving_rad-(linky-g/2))/curving_rad;
                        tanx:=1/TAN(linkangle);
                        tanx:=tanx*shrink_factor;
                        mod_linkangle:=ARCTAN(1/tanx);
                      except
                        mod_linkangle:=linkangle;
                      end;//try
                    end;

            link_arm_angle:=mod_linkangle+temp_k+kform;     // arm angle (actual on pad including curving and transforms).

            normalize_angle(link_arm_angle);
            notch_angle:=link_arm_angle*hand_i;

            if group_notch_linked=True then do_group_link_to_notch;
          end;
end;
//____________________________________________________________________________________

procedure normalize_kform;

begin
  normalize_angle(kform);
end;
//_____________________________________________________________________________________

procedure normalize_transforms;     // update shifts so that xform, yform can be zero,
                                    // i.e. any subsequent rotation is about the origin,
                                    // until xform, yform are modified again.
var
  x,y:extended;     // use current transform data to transform/shift the origin to x,y.

begin
  x:=xform-xform*COS(kform)+yform*SIN(kform)+xshift;
  y:=yform-xform*SIN(kform)-yform*COS(kform)+yshift;

  xshift:=x;                       // then normalise the data...
  yshift:=y;
  xform:=0;
  yform:=0;

  normalize_kform;
end;
//___________________________________________________________________________________

procedure click_bgnd_to_current(bgnd_options:boolean);

begin
  cancel_adjusts(False);  // cancel flags and update the pad caption.

  if (keeps_list.Count>0) and (clicked_keep_index>-1) and (clicked_keep_index<keeps_list.Count)
     then begin
            list_position:=clicked_keep_index;                     // make it current in the keeps box.
            copy_keep_to_current(False,bgnd_options,True,False);   // copy to pad.

            gocalc(0,0);       // direct recalc for shifts.

            if onto_notch=True then shift_onto_notch(False,False);
            if copy_datum=True then clear_transform_data;
            onto_notch:=False;                                                  // only once...
            copy_datum:=False;

            show_and_redraw(True,True);   // redraw pad when ready. (allow rollback).
          end;
end;
//______________________________________________________________________________

procedure click_bgnd_to_selected;

begin
  shift_click:=False;

  if (keeps_list.Count>0) and (clicked_keep_index>-1) and (clicked_keep_index<keeps_list.Count)
     then begin
            with Ttemplate(keeps_list.Objects[clicked_keep_index]) do begin
              if template_info.keep_dims.box_dims1.bgnd_code_077<>-1
                 then group_selected:= NOT group_selected
                 else group_selected:=False;                       // library template???
            end;//with
          end;

  if (any_selected<1) and (group_notch_linked=True) then unlink_group;
end;
//______________________________________________________________________________

procedure align_current_over_this(keep_dims:Tkeep_dims; clicked:boolean);

              // align the control template over other template.
              // (gauge, size, timbering, etc unchanged).
var
  y_offset:extended;

begin
  with keep_dims.box_dims1 do begin

    with transform_info do begin

      y_datum:=datum_y;                   // y datum (green dot).

      xform:=x1_shift;                    //  mm    shift info...
      yform:=y1_shift;                    //  mm

      kform:=k_shift;                     //  radians.
      normalize_kform;

      xshift:=x2_shift;                   //  mm
      yshift:=y2_shift;                   //  mm

    end;//with

    if (pad_form.align_match_length_menu_entry.Checked=True)  //  0.91.b match overall length.
    and (clicked=True)
       then begin
              turnoutx:=turnout_info1.turnout_length;
              if plain_track=True then xorg:=turnoutx;
              turnout_i:=1;                             // length locked at new turnoutx.
            end;

    with align_info do begin

      spiral:=trans_flag;      // True=transition, False=fixed radius curving.

      y_offset:=rad_offset;

      nomrad:=fixed_rad+y_offset;       // fixed radius mm.     (include any offset from old files pre 0.64.a)...
      nomrad1:=trans_rad1+y_offset;     // first transition radius mm.
      nomrad2:=trans_rad2+y_offset;     // second transition radius mm.

      tst:=trans_length;                // length of transition mm.
      os:=trans_start;                  // start of transition mm.

      slewing:=slewing_flag;   // slewing flag.              // !!! replacing Tspares 10-7-99...
      slew_s:=slew_start;      // slewing zone start mm.
      slew_l:=slew_length;     // slewing zone length mm.
      slew:=slew_amount;       // amount of slew mm.

      slew2_kmax:=tanh_kmax;           {:double;}  {spare_int1:integer;}   // stretch factor for mode 2 slews.
                                                   {spare_int2:integer;}   // !!! double used because only 8 bytes available in existing file format (2 integers).
      slew_mode:=slew_type;             {:byte;}   {spare_flag3:boolean;}  // !!! byte used because only 1 byte available in existing file format 1-11-99.

    end;//with

    if hand_i<>turnout_info1.hand
       then begin
              hand_i:=turnout_info1.hand;      // need to swap hand.
              gocalc(0,0);                     // calcs for the peg, datum, etc.
              invert_handing;                  // swap again to preserve his handing.
            end;

  end;//with
end;
//________________________________________________________________________________________

procedure align_current_over_bgnd(index:integer; facing_facing, clicked:boolean);

              // align the control template over clicked background template.
              // (gauge, size, timbering, etc unchanged).
var
  saved_control:Ttemplate_info;
  i:integer;

begin
  if (index<0) or (index>(keeps_list.Count-1)) or (keeps_list.Count<1) then EXIT;

  with Ttemplate(keeps_list.Objects[index]).template_info.keep_dims.box_dims1 do begin    // 205d

    if ABS(proto_info.gauge_pi-g)>minfp  // ??? menu should be disabled   205d mixed-gauge templates
       then begin
              snake_onto_bgnd_peg(index,facing_facing,True);   // 205d mixed-gauge must align on centre-lines.
              EXIT;
            end;
  end;//with

  align_current_over_this(Ttemplate(keeps_list.Objects[index]).template_info.keep_dims,clicked);
  if facing_facing=False then swap_end_for_end;

  if clicked=True
     then begin
            saved_control:=hold_the_control;         // 227a
            unhold_the_control(saved_control);  // this sequence updates all the menu check marks, etc.
            show_and_redraw(False,True);             // might be hidden. (allow rollback).

            if pad_form.align_match_length_menu_entry.Checked=True  //  0.91.b matched overall length.
               then begin
                      i:=alert(3,'     align  and  match  length',
                         '||The control template has been matched to the length of the selected background template and aligned over it.'
                        +'||Do you now want to delete or wipe the background template from under the control template?'
                        +'||If you do so, remember to store the control template in its place after making any further changes.| ',
                         '','','','delete  background  template','leave  background  template  unchanged','wipe  background  template',0);
                      case i of
                          4: begin
                               list_position:=index;
                               delete_keep(False,True);   // delete the background template
                             end;
                          6: wipe_it(index);    // wipe the background template (ignore result).
                      end;//case
                    end;//match
          end;//clicked
end;
//________________________________________________________________________________________

function check_peg_on_main:boolean;

var
  i:integer;

begin
  RESULT:=False;  // default init.

  if ABS(pegy-g/2)>minfp
     then begin
            i:=alert(3,'    current  peg  not  on  main-side  centre',
                       '|Align Control Template over Background Template.'
                      +'||For this function to work as intended, the peg on the control template should be located on the main-side track centre-line.'
                      +'||This is not currently the case. If you continue with the peg in its present position, the result may not be as you intended.',
                       '','','','continue  unchanged','cancel    ','move  peg  to  main-side  centre  and  continue          ',0);
            case i of
                   5: EXIT;
                   6: pad_form.peg_on_rail8_menu_entry.Click;
            end;//case
          end;
  RESULT:=True;  // carry on.
end;
//__________________________________________________________________________________________

function snake_onto_this_peg(notch_info:Tnotch; keep_dims:Tkeep_dims; facing_facing, alerts:boolean):boolean;

var
  i:integer;
  this_pegx, this_pegy, this_g:extended;
  saved_notch:Tnotch;

begin
  RESULT:=False;                          // default init.
  saved_notch:=get_current_notch;         // save his current notch position.

  this_pegx:=keep_dims.box_dims1.transform_info.peg_pos.x;   //  mm  peg position for bgnd template.
  this_pegy:=keep_dims.box_dims1.transform_info.peg_pos.y;   //  mm  peg position for bgnd template.
  this_g:=keep_dims.box_dims1.proto_info.gauge_pi;

  if (ABS(this_pegy-this_g/2)>minfp) and (alerts=True)
     then if alert(3,'    background  peg  not  on  main-road  centre',
                     'Snake  onto  Background  Peg'
                    +'||The fixing peg on the background template is not currently located on the main-road centre-line (the template curving line).'
                    +'||Are you sure that you want to snake the control template onto this peg ?',
                     '','','','','no  -  cancel','yes  -  continue',0)=5 then EXIT;

  if (ABS(pegy-g/2)>minfp) and (alerts=True)
     then begin
            i:=alert(3,'    current  peg  not  on  main-road  centre',
                       'Snake  onto  Background  Peg'
                      +'||The fixing peg on the control template is not located on the main-road centre-line (the template curving line).'
                      +'||Are you sure that you want to snake the control template onto this background template ?',
                       '','','','yes  -  continue  unchanged','no  -  cancel','move  peg  to  main-road  centre  and  continue     ',0);
            case i of
                   5: EXIT;
                   6: pad_form.peg_on_rail8_menu_entry.Click;
            end;//case
          end;

  align_current_over_this(keep_dims,False);

  if facing_facing=True          // both templates to face the same way (ladder)..
     then begin
            if spiral=True then os:=os-(this_pegx-pegx);              // maintain transition alignment.
            if slewing=True then slew_s:=slew_s-(this_pegx-pegx);     // maintain slewing alignment.
          end
     else begin                  // ends swapped to a facing-trailing combination (crossover or toe-to-toe)..
            swap_end_for_end;
            if spiral=True then os:=os+(this_pegx-(turnoutx-pegx));              // maintain transition alignment.
            if slewing=True then slew_s:=slew_s+(this_pegx-(turnoutx-pegx));     // maintain slewing alignment.
          end;

  gocalc(0,0);                           // calc new peg, etc.

  set_current_notch(notch_info);
  shift_onto_notch(False,False);

  if facing_facing=False then rotate_turnout(Pi,True);

  set_current_notch(saved_notch);
  RESULT:=True;
end;
//_______________________________________________________________________________________

procedure snake_onto_bgnd_peg(index:integer; facing_facing, alerts:boolean);

begin
  if (index<0) or (index>(keeps_list.Count-1)) or (keeps_list.Count<1) then EXIT;

  with Ttemplate(keeps_list.Objects[index]).template_info do begin
    if snake_onto_this_peg(notch_info,keep_dims,facing_facing,alerts)=False then EXIT;
  end;//with
end;
//______________________________________________________________________________

function popup_if_clicked(ctrl_down,right_click:boolean):boolean;  // popup and highlight bgnd keep if clicked on within limit g+1 square (arbitrary).
                                                                   // first clear any existing highlighting...
var
  old_index:integer;
  now_bgkeep:Tbgnd_keep;
  menu_caption_str:string;
  fb_str:string;             // 0.95.a

begin
  RESULT:=False; // init;    // 0.91.c for drag-panning

  if (GetKeyState(VK_SCROLL)AND 1)<>0 then EXIT;  // 244b  popup locked off

  copy_draw_to_pad;    // pad would be wiped if low-memory option in force

   // see if mouse is on a bgnd keep and highlight it...

  old_index:=clicked_keep_index;   // save any existing index;

  clicked_keep_index:=hover_mousedown(mouse_x(pad_click_X), mouse_y(pad_click_X,pad_click_Y), g/2+1);

  if clicked_keep_index=-1 then EXIT;    // not on one.

  if clicked_keep_index=old_index    // already clicked, so cancel the popup.
     then begin
            clicked_keep_index:=-1;              // so cancels only on first click.
            pad_form.cancel_popup_entry.Click;
            EXIT;
          end;

  if ctrl_down=True  // 223c  CTRL key down, don't popup menu, do make the control
     then begin
            pad_form.make_control_popup_entry.Click;

            RESULT:=True;
            EXIT;
          end;

  draw_background_templates(pad_form.Canvas,0,clicked_keep_index,True,hover_colour);  //  draw directly on pad and highlight this one.

  if (classic_templot=False) and (right_click=False)   // 0.93.a Quick mode click on template
     then begin
            bgnd_clicked_in_quick_mode:=True;
            pad_form.make_control_popup_entry.Click;
            bgnd_clicked_in_quick_mode:=False;
            RESULT:=True;
            EXIT;
          end;

  now_bgkeep:=Ttemplate(keeps_list.Objects[clicked_keep_index]).template_info.bgnd_keep;   // next background keep.

  with now_bgkeep do begin

    menu_caption_str:=UpperCase(Trim(full_label_string));
    if Length(menu_caption_str)>20 then menu_caption_str:=Copy(menu_caption_str,1,18)+'...';  // 20 arbitrary.

    with Ttemplate(keeps_list.Objects[clicked_keep_index]).template_info.keep_dims.box_dims1 do begin

      pad_form.align_current_popup_entry.Enabled:=(ABS(proto_info.gauge_pi-g)<minfp);  // 205d disabled for mixed-gauge templates

      if rail_type=2   // 0.95.a FB rail
         then fb_str:='FB •  '
         else fb_str:='BH •  ';

    end;//with

    pad_form.top_name_info_popup_entry.Caption:='I  '+fb_str+menu_caption_str;

    list_position:=clicked_keep_index;  // make it current in the box (for Show in Box).

    with pad_form.Canvas do begin

      Font.Assign(pad_form.bgnd_keeps_font_label.Font);
      Font.Color:=clBlack;

      Brush.Style:=bsSolid;

      if paper_colour=clAqua then Brush.Color:=clFuchsia
                             else Brush.Color:=clAqua;

      Font.Height:=text_font_height;

      if requested_label_string='' then showing_label_string:=full_label_string
                                   else showing_label_string:=requested_label_string;

      text_end_X:=text_begin_X + TextWidth(showing_label_string);
      text_end_Y:=text_begin_Y + TextHeight(showing_label_string);

      if pad_form.boxed_over_names_menu_entry.Checked=True
         then begin
                Pen.Color:=Font.Color;
                Pen.Style:=psSolid;     // 215b
                Rectangle(text_begin_X-2, text_begin_Y-2, text_end_X+2, text_end_Y+2);
              end;

      TextOut(text_begin_X, text_begin_Y, showing_label_string); // highlight it.
    end;//with Canvas

    caption_add(full_label_string);         // and in the pad caption.

  end;//with now_bgkeep

  if user_popup_X=(0-1) then popup_X:=4                // 213a...
                        else popup_X:=user_popup_X;

  if user_popup_Y=(0-1) then popup_Y:=Screen.Height div 2     // Windows will adjust to fit
                        else popup_Y:=user_popup_Y;

  with Ttemplate(keeps_list.Objects[clicked_keep_index]) do begin
    if (template_info.keep_dims.box_dims1.mod_text_x=0) and (template_info.keep_dims.box_dims1.mod_text_y=0)
       then pad_form.restore_label_popup_entry.Enabled:=False
       else pad_form.restore_label_popup_entry.Enabled:=True;

    pad_form.select_bg_popup_entry.Checked:=group_selected;
  end;//with

  pad_form.selection_popup.PopUp(popup_X,popup_Y);

  RESULT:=True;     // 0.91.c
end;
//________________________________________________________________________________________

procedure mouse_action_grab(mode,X,Y:integer);

var
  aq:integer;
  save_switch_free:boolean;

begin
  if trace_mouse=True
     then begin
            if mode=1          // no longer used 0.93
               then begin
                      for aq:=0 to aq_max_c do begin
                        if aqyn[aq]=True
                           then begin
                                  ink_colour:=paper_colour;
                                  show_a_line(pad_form.Canvas,aq,1,True);   // need to erase outer edges (ignore return).
                                end;
                      end;//for
                    end;

            if full_draw=False
               then begin
                      railedges(True,False,centre_lines);   // gauge faces only.
                      guide_marks:=True;                    // but show the guide marks for alignment with background.
                      rad_ends:=spiral or slewing;          // show radial ends if transition or slewing.
                      timber_marks:=False;                  // no timbers or joint marks while adjusting and re-drawing.
                      joint_marks:=False;
                    end;
          end;

  if hide_current_flag=True             // no sense in adjusting an invisible template...
     then begin
            hide_current_flag:=False;
            save_hide:=False;

            pad_form.show_control_template_menu_entry.Checked:=True;   // 209c  radio item
            pad_form.show_control_template_popup_entry.Checked:=True;  // 209c  radio item
          end;

  do_linked_notch;   // link notch if wanted.

  if (mouse_modify=1) or (mouse_modify=2)      // both, or F5 size, actions.
     then begin
            save_switch_free:=switch_free;     // don't want to change the switch, otherwise it might not stay pegged.
            switch_free:=False;
            gocalc(2,mode+1);                  // show it on first click.
            switch_free:=save_switch_free;
          end
     else gocalc(2,mode+1);                    // show it on first click (all other actions).
end;
//__________________________________________________________________________________________

procedure mouse_action_release(cancelling_adjusts:boolean);     // 0.79.a

begin
  Screen.Cursor:=crDefault;

  if mouse_click_action<>0 then mouse_action_button_down:=False;   // don't clear it if drag only allowed.

  if mouse_modify<0 then EXIT;                          // it's been cleared to zero by the terminating click.

  mouse_modify:=0;                                      // in case of button_down mode.
  action_form.action_2_image.Hide;
  action_form.image_panel.Color:=clGray;
  allow_idle:= NOT control_room_form.fast_100_menu_entry.Checked;  // no mouse action - allow idle again.

  if mouse_click_action<>0
     then begin
            action_form.action_label.Caption:=mouse_label_string;    // remove any bullet.
            action_form.action_label.Width:=action_form.Canvas.TextWidth(action_form.action_label.Caption)+2; // bug fix for Wine
          end;

  with pad_form do begin

    guide_marks:=guide_marks_menu_entry.Checked;        // guide marks back off if not wanted.
    rad_ends:=rad_end_marks_menu_entry.Checked;         // ditto rad end marks.
    timber_marks:=timber_centres_menu_entry.Checked
               or timber_outlines_menu_entry.Checked
               or timber_numbers_menu_entry.Checked;    // ditto timbering.
    joint_marks:=joint_marks_menu_entry.Checked;        // ditto joint marks.

    railedges(gauge_faces,outer_edges,centre_lines);      // restore his settings.

    redraw(False);

    Cursor:=mouse_action_cursor;
  end;//with

  if  (cancelling_adjusts=True)            // 0.94.a bug fix -- was "list index out of bounds" on next mouse_down if mouse wasn't moved first time.
  and (shove_timber_form.Showing=True)     // update enabled shove buttons..
  and (
          (shove_along_mod=1)
       or (shove_throw_mod=1)
       or (shove_crab_mod=1)
       or (shove_length_mod=1)
       or (shove_width_mod=1)
       or (shove_twist_mod=1)
      )
       then begin
              shove_index:=find_shove(current_shove_str,False);
              shove_buttons(True,0,shove_index);
            end;

  if (shift_mod=1) and (cancelling_adjusts=False)
     then snap_onto_bgnd_pegs(False);                     // F7 shift mouse action   0.79.a  27-05-06               233b ignore result

end;
//______________________________________________________________________________

procedure pad_mouse_down(mouse_button:TMouseButton; shift_state:TShiftState; X,Y:integer);

  // never come here if middle button 0.91.c

var
  dummy1,dummy2,dummy3:extended;
  xc0,yc0,xc9,yc9:extended;
  mode,zoom_mode:integer;
  menu_caption_str:string;
  fb_str:string;   // 0.95.a

  popup_loc_left,popup_loc_right,screen_poploc:TPoint; // 0.91.b

                    ////////////////////////////////////////////////////////////

                    procedure set_opp;

                    var
                      kopp:extended;   // 0.93.a mods

                    begin
                      kopp:=ABS(kform);                 // 0 to 360          // 0.93.a mods
                      if kopp>Pi then kopp:=kopp-Pi*2;  // -180 to +180
                      kopp:=ABS(kopp);                  // 0 to 180

                      if (kopp>(Pi/2)) and (auto_dir=True)  // 90 to 180 turnout facing opposite way ?
                         then begin
                                opposite_way:=True;
                                X:=pad_form.ClientWidth-X;    // reverse sense of mouse action.
                                Y:=pad_form.ClientHeight-Y;
                                if X=0 then X:=1;             // prevent divisions by zero later (screen-edge clicked).
                                if Y=0 then Y:=1;
                              end
                         else opposite_way:=False;
                    end;
                    ////////////////////////////////////////////////////////////

                    procedure extend_if_zero;  // 0.93.a

                    begin
                      if turnoutx=0
                         then begin  // 0.93.a extend it

                                turnoutx:=132*scale;     // arbitrary new length 2 chains.
                                turnout_i:=1;            // length locked at turnoutx.
                                if plain_track=True then xorg:=turnoutx
                                                    else xorg:=0;
                                startx:=0;
                              end;
                    end;
                    ////////////////////////////////////////////////////////////

begin
  allow_left_button_pan:=False;   // 0.91.c  init.

  prev_pad_click_X:=pad_click_X;  // save the previous clicked position.
  prev_pad_click_Y:=pad_click_Y;

  pad_click_X:=X;                 // and this one, used for mouse_action grab, shove timbers, chair labels
  pad_click_Y:=Y;                 // (and bgnd shapes - in bgnd_unit)
                                  // (X,Y may be changed for opposite way).

  if shift_state=[ssAlt,ssLeft]     // left click with ALT key.   0.71.b 2-6-01
     then begin
            jotter_dx_org:=mouse_now_x;       // zero the relative X,Y readout.
            jotter_dy_org:=mouse_now_y;       // zero the relative X,Y readout.


            if Screen.Cursor<>crNone then jotter_form.jotter_rel_xy_readout_panel.Caption:=''                    // wait for redraw to refill it.
                                     else jotter_form.jotter_rel_xy_readout_panel.Caption:=' ( mouse action )';
            if show_ruler_tool=True
               then begin
                      ruler_startx:=mouse_now_x;
                      ruler_starty:=mouse_now_y;
                      redraw_pad(True,False);
                    end;
            EXIT;
          end;

  if (shift_state=[ssAlt,ssRight]) and (show_ruler_tool=True)  // right click with ALT key. mod 0.78.a  18-11-02
     then begin
            ruler_endx:=mouse_now_x;
            ruler_endy:=mouse_now_y;
            redraw_pad(True,False);
            EXIT;
          end;

  if (shift_state=[ssAlt,ssMiddle]) or (shift_state=[ssAlt,ssCtrl,ssLeft])    // middle click (or Ctrl-left) with ALT key.   0.71.b 2-6-01
     then begin
            with jotter_form do begin
              if (jotter_abs_xy_readout_panel.Visible=True) or (jotter_rel_xy_readout_panel.Visible=True)
                 then begin
                        jotter_memo.Lines.Add('');
                        if jotter_abs_xy_readout_panel.Visible=True then jotter_memo.Lines.Add(jotter_abs_xy_readout_panel.Caption);
                        if jotter_rel_xy_readout_panel.Visible=True then jotter_memo.Lines.Add(jotter_rel_xy_readout_panel.Caption);
                      end;
            end;//with
            EXIT;
          end;

  if (shift_state=[ssAlt,ssCtrl,ssRight]) and (mouse_modify>-1)    // right click with ALT key.   0.71.b 2-6-01
     then begin                                                    // mouse action in force.
            with jotter_form do begin
              jotter_memo.Lines.Add('');
              jotter_memo.Lines.Add(action_form.action_label.Caption);
              jotter_memo.Lines.Add(action_form.trail_dim_label.Caption);
            end;//with
            EXIT;
          end;

  if ( (mouse_draw_lines=True) or (draw_template_direct=True) ) and (mouse_button=mbLeft)      // we are simply drawing with the mouse - save co-ords and exit.
     then begin
            draw_mouse_down_X:=X;      // screen coords for line drawing start point.
            draw_mouse_down_Y:=Y;

            temp_mouse_X:=X;           // temp screen coords for mouse move drawing.
            temp_mouse_Y:=Y;

            mouse_down_x:=mouse_x(X);  // and in mm. for shape list
            mouse_down_y:=mouse_y(X,Y);

            mouse_drawing_in_progress:=True;
            EXIT;
          end;

  if (mouse_draw_lines=True) and (mouse_button=mbRight)     // cancel mouse drawing on right button.
     then begin
            show_and_redraw(True,False);  // on idle, no rollback.
            EXIT;
          end;

  if (bgnd_form.Visible=True) and (bgnd_form.clicked_locs_radiobutton.Checked=True)  // 229a radio button
  and (zoom_rectangle=False)
  and (group_fence_rectangle=False)
  and (output_boundary_rectangle=False)  // 0.93.a  // he clicked shape corners.
  and (drawn_shape_rectangle=False)      // 214a
     then begin
            with pad_form.Canvas do begin
              Pen.Style:=psSolid;
              Pen.Mode:=pmCopy;
              Pen.Width:=1;
              Pen.Color:=shapes_colour;

              MoveTo(X-8,Y);     // mark the clicked target cross. (8 arbitrary).
              LineTo(X+8,Y);
              MoveTo(X,Y-8);
              LineTo(X,Y+8);
            end;//with

            bgnd_no_update:=True;    // prevent an update to the shapes form on Activate (he might have already set the option boxes for this one).
            bgnd_form.Show;          // he clicked pad for shape X,Y, so bring form back to the front.
            EXIT;
          end;

  if shift_state=[ssCtrl,ssLeft]  // CTRL+left click
     then begin
            if mouse_modify<>-1     // mouse action in force...
               then cancel_adjusts(False)
               else begin     // out 223c    pad_form.select_centre_menu_entry.Click;   // select pad centre on CTRL key held down.

                           // 223c  make the control if CTRL key down ...

                      if popup_if_clicked(True,False)=False  // go check if he clicked on a bgnd template and make the control

                         then begin                          // not on a bgnd template
                                if pad_form.allow_left_drag_panning_menu_entry.Checked=True
                                   then begin
                                          allow_left_button_pan:=True;  // 0.91.c
                                          Screen.Cursor:=crSizeAll;     // 0.91.c

                                          zoff_now_x:=X;  // 0.91.c   init pad move
                                          zoff_now_y:=Y;
                                          zoom_offsetx_now:=zoom_offsetx;
                                          zoom_offsety_now:=zoom_offsety;
                                        end;
                              end;
                    end;
            EXIT;
          end;

  if (select_centre=True) and (mouse_button=mbLeft) // selecting the pad centre...
     then begin
            select_centre:=False;
            if draw_mode<>2 then pad_form.lock_scaling_menu_entry.Click;

            zoom_offsetx:=mouse_x(X)-screenx/2;
            zoom_offsety:=mouse_y(X,Y)-screeny/2;

            cancel_adjusts(False);           // to reset the pad caption.
            do_rollback:=False;       // no need to put this in rollback register on redraw.
            redraw(True);
            EXIT;
          end;
  select_centre:=False;               // right click cancels select.

  if ( (zoom_rectangle=True) or (group_fence_rectangle=True) or (output_boundary_rectangle=True) or (drawn_shape_rectangle=True) )
  and (mouse_button=mbLeft)  // 0.93.a    // drawing with the mouse - save co-ords and exit.
     then begin
            draw_mouse_down_X:=X;            // screen coords for first point.
            draw_mouse_down_Y:=Y;
            rectangle_in_progress:=True;
            rectangle_exists:=False;         // nothing there yet for erasing.
            EXIT;
          end;

  //---------------------

  if clicks_accepted=True     // over a bgnd template name label?
     then begin
            clicks_accepted:=False;                   // only one click.

            clicked_keep_index:=hover_keep_index;     // save the list indices.

            list_position:=clicked_keep_index;  // make it current in the box.

            if (classic_templot=False) and (mouse_button=mbLeft) and (click_bgnd_select=False)   // 0.93.a Quick mode left click on label
               then begin
                      bgnd_clicked_in_quick_mode:=True;
                      pad_form.make_control_popup_entry.Click;
                      bgnd_clicked_in_quick_mode:=False;
                      EXIT;
                    end;

            if (click_bgnd_select=True) and (mouse_button=mbLeft)  // bgnd keep to or from group selection list.
               then begin
                      click_bgnd_to_selected;
                      EXIT;
                    end;

            if (shift_click=True) or (mouse_button=mbRight)            // right click or either click if shift key was down when highlighted.
               then begin
                      if (clicked_keep_index>-1) and (clicked_keep_index<keeps_list.Count) and (keeps_list.Count>0)
                         then begin

                                 with Ttemplate(keeps_list.Objects[clicked_keep_index]) do begin
                                   if (template_info.keep_dims.box_dims1.mod_text_x=0) and (template_info.keep_dims.box_dims1.mod_text_y=0)
                                      then pad_form.restore_label_popup_entry.Enabled:=False
                                      else pad_form.restore_label_popup_entry.Enabled:=True;

                                   pad_form.select_bg_popup_entry.Checked:=group_selected;
                                 end;//with
                              end;

                      menu_caption_str:=UpperCase(Trim(Ttemplate(keeps_list.Objects[clicked_keep_index]).template_info.bgnd_keep.full_label_string));
                      if Length(menu_caption_str)>20 then menu_caption_str:=Copy(menu_caption_str,1,18)+'...';  // 20 arbitrary.

                      with Ttemplate(keeps_list.Objects[clicked_keep_index]).template_info.keep_dims.box_dims1 do begin

                        pad_form.align_current_popup_entry.Enabled:=(ABS(proto_info.gauge_pi-g)<minfp);  // 205d disabled for mixed-gauge templates

                        if rail_type=2   // 0.95.a FB rail
                           then fb_str:='FB •  '
                           else fb_str:='BH •  ';

                      end;//with


                      pad_form.top_name_info_popup_entry.Caption:='I  '+fb_str+menu_caption_str;  // 0.95.a

                      pad_form.selection_popup.PopUp(popup_X,popup_Y);

                      cancel_adjusts(False);       // update the pad caption. 13-1-00.
                      do_rollback:=False;
                      redraw(False);        // remove highlighting (must be False (immediate redraw) in case we want to re-highlight for label moving).
                    end;
            EXIT;
          end;// clicks_accepted
  //--------------------------

  if mouse_button=mbRight                       //  right-click :
     then begin
            if popup_if_clicked(False,True)=False  // go check if he clicked on a bgnd template and do pop-up.   // 0.93.a
               then begin

                            // multiple monitors... 0.91.b

                      popup_loc_left.X:=pad_form.ClientWidth div 4;
                      popup_loc_left.Y:=pad_form.ClientHeight div 5;

                      popup_loc_right.X:=pad_form.ClientWidth div 2;
                      popup_loc_right.Y:=pad_form.ClientHeight div 5;

                      if X<(pad_form.ClientWidth div 2)
                         then screen_poploc:=pad_form.ClientToScreen(popup_loc_right)
                         else screen_poploc:=pad_form.ClientToScreen(popup_loc_left);

                      pad_form.pad_popup_menu.Popup(screen_poploc.X,screen_poploc.Y);
                    end;

            mouse_action_release(False);   // (if any)
            EXIT;
          end;

  if mouse_modify=-1               // no mouse action selected.
     then begin
            if hide_current_flag=False     // MW 03-08-2024  555a
               then begin
                      if chair_label_clicked(pad_click_X,pad_click_Y)=True then EXIT; // clicked on a chair label
                    end;

            if (shove_timber_form.Showing=True) and (hide_current_flag=False)
               then begin
                      if shove_number_clicked(pad_click_X,pad_click_Y)=True then EXIT; // clicked on a timber number
                    end;

            if (check_diffs_form.Showing=True) and (hide_current_flag=False)   // 0.94.a ...
               then begin
                      if checkrail_label_clicked(pad_click_X,pad_click_Y)=True then EXIT; // clicked on a check-rail label
                    end;

            if (gaps_form.Showing=True) and (hide_current_flag=False)   // 227a
               then begin
                      if symbol_clicked(pad_click_X,pad_click_Y)=True then EXIT; // clicked on a symbol label
                    end;

            if (bgnd_form.Showing=True) // 229a
               then begin
                      if shape_clicked(pad_click_X,pad_click_Y)=True then EXIT; // clicked on a background shape p1 loc
                    end;

            if popup_if_clicked(False,False)=False  // go check if he clicked on a bgnd template and do pop-up.
               then begin
                      if pad_form.allow_left_drag_panning_menu_entry.Checked=True
                         then begin
                                allow_left_button_pan:=True;  // 0.91.c
                                Screen.Cursor:=crSizeAll;     // 0.91.c

                                zoff_now_x:=X;  // 0.91.c   init pad move
                                zoff_now_y:=Y;
                                zoom_offsetx_now:=zoom_offsetx;
                                zoom_offsety_now:=zoom_offsety;
                              end;
                    end;
            EXIT;
          end;

                // MOUSE ACTIONS BEGIN HERE:-

  if ABS(fx)<minfp then EXIT;                 // no proper screen calcs done ??
  if ABS(fy)<minfp then EXIT;

  with pad_form do begin     // (for the Cursor)

  if X=0 then X:=1;          // prevent divisions by zero later (screen-edge clicked).
  if Y=0 then Y:=1;

    // mods Jan 2000 - for use with button-down mouse actions, we now set the Screen cursor instead of the form cursor (form cursor won't change while the button is down (???) ).

  Screen.Cursor:=crDefault;     //  needed in case clicks get out of sync.

  if mouse_modify=0                          // will be changed.
     then begin
            action_form.action_2_image.Show;
            action_form.image_panel.Color:=clMaroon;
            allow_idle:=control_room_form.allow_full_idle_menu_entry.Checked;  // mouse action or always, disallow idle.
          end
     else begin
            action_form.action_2_image.Hide;
            action_form.image_panel.Color:=clGray;
            allow_idle:= NOT control_room_form.fast_100_menu_entry.Checked;  // no mouse action - allow idle again.
          end;

  mode:=7;
  zoom_mode:=9;

  if both_mod=1
     then begin
            extend_if_zero;  // 0.93.a
            set_opp;
            if mouse_modify=0
               then begin
                      if opposite_way=True then Screen.Cursor:=adjust_corners_cursor_invert     //crSizeNWSE
                                           else Screen.Cursor:=adjust_corners_cursor_invert;

                             //  Delphi cursor bug - crSize not working (NT only?).

                      mouse_modify:=1;
                      if draw_mode<>2 then mode:=zoom_mode;    // free-scaling - change mode.
                    end
               else mouse_modify:=0;

            xing_now:=X;
            k3n_now:=k3n;
            curving_now:=Y;

            nomrad_now:=max_rad;    // keep compiler happy.

            if spiral=False
               then nomrad_now:=nomrad
               else begin                        // transition.
                      case adjust_trans_rad of
                                          1: nomrad_now:=nomrad1;
                                          2: nomrad_now:=nomrad2;
                                        else run_error(198);
                      end;//case
                    end;

            if ABS(nomrad_now)>minfp then curvature_now:=1/nomrad_now
                                     else curvature_now:=1/minfp;

            kform_now:=kform;
            arm_angle_now:=arm_angle;
            pegangle_now:=pegangle;
            docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy1);    // save current peg position on pad and curving angle.
          end;

  if sx_mod=1
     then begin
            extend_if_zero;  // 0.93.a
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=adjust_we_cursor_invert;    //crSizeWE;
                      mouse_modify:=2;
                      if draw_mode<>2 then mode:=zoom_mode;      // free-scaling - change mode.
                    end
               else mouse_modify:=0;

            xing_now:=X;
            k3n_now:=k3n;

            kform_now:=kform;
            arm_angle_now:=arm_angle;
            pegangle_now:=pegangle;
            docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy1);    // save current peg position on pad and curving angle.
          end;

  if curving_mod=1
     then begin
            extend_if_zero;  // 0.93.a
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=adjust_ns_cursor_invert;   // custom_cursor.
                      mouse_modify:=3;                          //  toggle the re-draw flag on.
                      //no_mouse_move:=0;                       //  permit mouse moves to do adjusts.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                 //  then toggle re-draw off.
                    end;

            curving_now:=Y;
            nomrad_now:=max_rad;    // keep compiler happy.

            if spiral=False               // no transition.
               then begin
                      nomrad_now:=nomrad;

                      if (f6_swing_fixed=True) and (ABS(nomrad)>minfp)   // 0.91.b      // constant radius only.
                         then begin
                                if nomrad=max_rad
                                   then f6_swing_angle:=ARCTAN(1/7)               // 1:7 RAM default 0.93.a was Pi/18 10 degs default for a straight starting template
                                   else f6_swing_angle:=ABS(turnoutx/nomrad);     // nomrad may be negative.
                              end
                         else f6_swing_angle:=0;                                  // not used.
                    end
               else begin                // transition.
                      case adjust_trans_rad of
                                          1: nomrad_now:=nomrad1;
                                          2: nomrad_now:=nomrad2;
                                        else run_error(198);
                      end;//case
                    end;

            if ABS(nomrad_now)>minfp then curvature_now:=1/nomrad_now
                                     else curvature_now:=1/minfp;

            kform_now:=kform;
            docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy1);    // save current peg position on pad and curving angle.
          end;

  if length_mod=1
     then begin
            set_opp;
            if mouse_modify=0
               then begin
                      extend_if_zero;  // 0.93.a

                      Screen.Cursor:=adjust_we_cursor_invert; // crSizeWE;
                      mouse_modify:=4;                        // toggle the re-draw flag on.
                      if draw_mode<>2 then mode:=zoom_mode;   // free-scaling - change mode.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   // then toggle re-draw off.
                    end;
            length_now:=X;
            endx_now:=turnoutx;

            pegx_now:=pegx;
            udpegx_now:=udpegx;

            os_now:=os;
            slew_s_now:=slew_s;

            kform_now:=kform;
            docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy1);    // save current peg position on pad and curving angle.
            if peg_code=600 then dummy1:=aq25offset(pegx,now_peg600);               // peg on TOLP, save current pegangle for curviform V-crossing. 0.79.a
          end;

  if shift_mod=1
     then begin
            extend_if_zero;  // 0.93.a
            if mouse_modify=0
               then begin
                      Screen.Cursor:=adjust_ne_cursor_invert;//crDrag;
                      mouse_modify:=5;                 //  toggle the re-draw flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;
            shift_now_x:=X;
            shift_now_y:=Y;
            xshift_now:=xshift;
            yshift_now:=yshift;
          end;

  if twist_mod=1
     then begin
            extend_if_zero;  // 0.93.a
            if mouse_modify=0
               then begin
                      Screen.Cursor:=adjust_ns_cursor_invert;//crSizeNS;
                      mouse_modify:=6;                 //  toggle the re-draw flag on.

                      init_rotate(pegx,pegy,False);  // set up transform constants.

                                                 // save clicked position...
                      shift_now_y:=Y;
                      kform_now:=kform;          // and current angle.

                      if X<pad_pegx then twist_dir:=-1      // rotation direction depends which side of peg he clicked.
                                    else twist_dir:=1;

                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;
          end;

  if zoom_mod=1
     then begin
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crHSplit;
                      mouse_modify:=7;                 //  toggle the re-draw flag on.
                      mode:=zoom_mode;              //  free-scaling - change mode.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;

            zoom_now:=X;                     // save clicked position.
            screenx_zoom_now:=screenx;
            do_rollback:=False;              // no need to put this change in rollback register on redraw.
          end;

  if approach_mod=1
     then begin
            extend_if_zero;  // 0.93.a
            set_opp;
            if mouse_modify=0
               then begin
                      startx:=0;      // cancel any blanking (we waited until mouse down in case F3 was in error).

                      Screen.Cursor:=adjust_we_cursor_invert;//crSizeWE;
                      mouse_modify:=8;                            //  toggle the re-draw flag on.
                      if draw_mode<>2 then mode:=zoom_mode;    // free-scaling - change mode.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;
            approach_now:=X;
            xorg_now:=xorg;
            turnoutx_now:=turnoutx;

            pegx_now:=pegx;
            udpegx_now:=udpegx;

            os_now:=os;
            slew_s_now:=slew_s;

            kform_now:=kform;
            docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy1);    // save current peg position on pad and curving angle.
          end;

  if datum_mod=1                             // move Y datum
     then begin
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crVSplit;
                      mouse_modify:=9;                 //  toggle the re-draw flag on.
                      mode:=zoom_mode;              // free-scaling - change mode.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;

            datum_now:=Y;                     // save clicked position.
            y_datum_now:=y_datum;
          end;

  if trans_length_mod=1                  // SHIFT+CTRL-F4 adjust transition length
     then begin
            extend_if_zero;  // 0.93.a
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=adjust_we_cursor_invert;//crHSplit;
                      mouse_modify:=10;                //  toggle the re-draw flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;

            trans_length_now:=X;                     // save clicked position.
            tst_now:=tst;

            kform_now:=kform;
            docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy1);    // save current peg data for peg_curve calcs.

          end;

  if zoffset_mod=1
     then begin
            if mouse_modify=0
               then begin
                      Screen.Cursor:=cross_size_cursor;//crDrag;
                      mouse_modify:=11;                //  toggle the re-draw flag on.
                      mode:=zoom_mode;                 // free-scaling - change mode.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                 //  then toggle re-draw off.
                    end;

            zoff_now_x:=X;
            zoff_now_y:=Y;
            zoom_offsetx_now:=zoom_offsetx;
            zoom_offsety_now:=zoom_offsety;
            do_rollback:=False;              // no need to put this change in rollback register on redraw.
          end;

  if spot_mod=1
     then begin
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crVSplit;
                      mouse_modify:=12;             //  toggle the re-draw flag on.
                      mode:=zoom_mode;              //  free-scaling - change mode.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;

            spot_now:=pad_form.ClientHeight-Y;        // save clicked position. (neg so that up screen zooms in.)
            screenx_spot_now:=screenx;
            zoom_offsetx_now:=zoom_offsetx;
            zoom_offsety_now:=zoom_offsety;

            zoom_now:=X;              // save clicked X position for additional scale zooming if wanted.
            do_rollback:=False;       // no need to put this change in rollback register on redraw.
          end;

  if peg_mod=1
     then begin
            extend_if_zero;  // 0.93.a
            if peg_rail<>0 then set_opp;    //  do opposite way if running peg along rails.
            if mouse_modify=0
               then begin
                      if peg_rail=0 then Screen.Cursor:=crNone     //  free move.
                                    else Screen.Cursor:=adjust_we_cursor_invert;//crSizeWE;  //  along rails.
                      mouse_modify:=13;                        //  toggle the re-draw flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;

            peg_now_x:=X;
            peg_now_y:=Y;
            pegx_now:=pegx;
            pegy_now:=pegy;

            docurving(False,True,pegx,pegy,dummy1,dummy2,now_peg_k,dummy3);    // save current peg curving angle.

          end;

  if orbit_mod=1
     then begin
            extend_if_zero;  // 0.93.a
            if mouse_modify=0
               then begin
                      Screen.Cursor:=adjust_we_cursor_invert;//crSizeWE;
                      mouse_modify:=14;                //  toggle the re-draw flag on.

                      if spiral=False
                         then begin
                                orbit_rad:=clrad1;
                                init_rotate(xt1,yt1,True)   // set up transform constants (rad centre).
                              end
                         else case adjust_trans_rad of
                                                 1: begin
                                                      init_rotate(xt1,yt1,True);
                                                      orbit_rad:=clrad1;
                                                    end;
                                                 2: begin
                                                      init_rotate(xt2,yt2,True);
                                                      orbit_rad:=clrad2;
                                                    end;
                                               else begin
                                                      init_rotate(xt1,yt1,True);
                                                      orbit_rad:=clrad1;
                                                    end;
                              end;//case

                      shift_now_x:=X;            // save clicked position.
                      kform_now:=kform;          // and current angle.

                      if Y>rad_centy then orbit_dir:=hand_i*SGZ(orbit_rad)        // set orbit direction.
                                     else orbit_dir:=0-hand_i*SGZ(orbit_rad);
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;
          end;

  if notch_mod=1
     then begin
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crNone;
                      mouse_modify:=15;                //  toggle the re-draw flag on.
                      if mode=1 then mode:=3;       // otherwise doesn't erase on pad.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;
            notch_now_x:=X;
            notch_now_y:=Y;
            notchx_now:=notchx;
            notchy_now:=notchy;
            do_rollback:=False;      // no need to put this change in rollback register on redraw.
          end;

  if trans_start_mod=1                  // SHIFT+CTRL-F3 adjust transition start
     then begin
            extend_if_zero;  // 0.93.a
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=adjust_we_cursor_invert;//crHSplit;
                      mouse_modify:=16;                //  toggle the re-draw flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;

            trans_start_now:=X;                     // save clicked position.
            os_now:=os;

            kform_now:=kform;
            docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy1);    // save current peg data for peg_curve calcs.

          end;

  if shift_keeps_mod=1
     then begin
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crMultiDrag;
                      mouse_modify:=17;              //  toggle the re-draw flag on.
                      if mode=1 then mode:=3;        //  otherwise doesn't erase on pad.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;
            shift_now_x:=X;
            shift_now_y:=Y;
            xshift_now:=xshift_keeps;
            yshift_now:=yshift_keeps;
          end;

  if twist_keeps_mod=1
     then begin
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crSizeNS;
                      mouse_modify:=18;                //  toggle the re-draw flag on.
                      if mode=1 then mode:=3;       //  otherwise doesn't erase on pad.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;
                                           // save clicked position...
            shift_now_y:=Y;
            kform_now:=kform_keeps;          // and current angle.

            if X>(notchx*fx+ex-gx) then twist_dir:=-1      // rotation direction depends which side of the notch he clicked.
                                   else twist_dir:=1;

          end;

  if ring_mod=1
     then begin
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crNone;
                      mouse_modify:=19;                //  toggle the re-draw flag on.
                      if mode=1 then mode:=3;          //  otherwise doesn't erase on pad.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;
            ring_now_x:=X;
            ring_now_y:=Y;
            xring_now:=rings[0,0];
            yring_now:=rings[0,1];
          end;

  if slide_mod=1
     then begin
            extend_if_zero;  // 0.93.a
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=adjust_we_cursor_invert;//crSizeWE;
                      mouse_modify:=20;                //  toggle the re-draw flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;
            peg_now_x:=X;
            pegx_now:=pegx;
            pegangle_now:=pegangle;

            kform_now:=kform;
            docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy1);    // save current peg data for peg_curve calcs.

          end;

  if slew_start_mod=1          // SHIFT+CTRL-F5 adjust slewing start
     then begin
            extend_if_zero;  // 0.93.a
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=adjust_we_cursor_invert;//crHSplit;
                      mouse_modify:=21;                //  toggle the re-draw flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;

            slew_start_now:=X;                     // save clicked position.
            slew_s_now:=slew_s;

            kform_now:=kform;
            docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy1);    // save current peg data for peg_curve calcs.
          end;

  if slew_length_mod=1                  // SHIFT+CTRL-F6 adjust slewing length
     then begin
            extend_if_zero;  // 0.93.a
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=adjust_we_cursor_invert;//crHSplit;
                      mouse_modify:=22;                //  toggle the re-draw flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;

            slew_length_now:=X;                     // save clicked position.
            slew_l_now:=slew_l;

            kform_now:=kform;
            docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy1);    // save current peg data for peg_curve calcs.

          end;

  if slew_amount_mod=1               // CTRL-F7 adjust amount of slew.
     then begin
            extend_if_zero;  // 0.93.a
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=adjust_ns_cursor_invert;//crVSplit;
                      mouse_modify:=23;                //  toggle the re-draw flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;

            slew_amount_now:=Y;                     // save clicked position.
            slew_now:=slew;

            kform_now:=kform;
            docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy1);    // save current peg data for peg_curve calcs.
          end;

  if out_factor_mod=1
     then begin
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crSizeWE;
                      mouse_modify:=24;             //  toggle the re-draw flag on.
                      mode:=zoom_mode;              //  outlines are on the backdrop.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;              //  then toggle re-draw off.
                    end;

            printsize_now_X:=X;
            out_factor_now:=out_factor;
          end;

  if shove_along_mod=1
     then begin
            extend_if_zero;  // 0.93.a
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crSizeWE;
                      mouse_modify:=25;                //  toggle the re-draw flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;
            shove_now_x:=X;
            shovex_now:=shovex;

            shovetimb_now:=X;
            shovetimbx_now:=shovetimbx;
          end;

  if shove_throw_mod=1
     then begin
            extend_if_zero;  // 0.93.a
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crSizeNS;
                      mouse_modify:=26;                //  toggle the re-draw flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;
            shove_now_y:=Y;
            shoveo_now:=shoveo;
          end;

  if roam_mod=1
     then begin
            extend_if_zero;  // 0.93.a
            set_opp;
            if mouse_modify=0
               then begin
                      startx:=0;    // cancel any blanking (we waited until mouse down in case CTRL-F9 was in error).

                      Screen.Cursor:=adjust_we_cursor_invert;//crSizeWE;
                      mouse_modify:=27;                           //  toggle the re-draw flag on.
                      if draw_mode<>2 then mode:=zoom_mode;    // free-scaling - change mode.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;
            roam_now:=X;
            xorg_now:=xorg;

            pegx_now:=pegx;
            udpegx_now:=udpegx;

          end;

  if slew_factor_mod=1               // adjust mode 2 slew factor.
     then begin
            extend_if_zero;  // 0.93.a
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crHSplit;
                      mouse_modify:=28;                //  toggle the re-draw flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;

            slew_factor_now:=X;                       // save clicked position.
            slew2_kmax_now:=slew2_kmax;

            kform_now:=kform;
            docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy1);    // save current peg data for peg_curve calcs.
          end;

  if bunch_mod=1
     then begin
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crSizeNWSE;
                      mouse_modify:=29;                //  toggle the re-draw flag on.
                      mode:=zoom_mode;              //  free-scaling - change mode.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;

            bunch_now:=X;
            bunching_jump_now:=bunching_jump;

            shear_now:=Y;
            bunching_shear_now:=bunching_shear;

            do_rollback:=False;              // no need to put this change in rollback register on redraw.
          end;

  if porg_mod=1
     then begin
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crMultiDrag;
                      mouse_modify:=30;                //  toggle the re-draw flag on.
                      mode:=zoom_mode;              //  outlines are on the backdrop.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;

            porg_now_x:=X;
            porg_now_y:=Y;
            print_pages_top_origin_now:=print_pages_top_origin;
            print_pages_left_origin_now:=print_pages_left_origin;

            do_rollback:=False;              // no need to put this change in rollback register on redraw.
          end;

  if corner1_mod=1
     then begin
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crDrag;
                      mouse_modify:=31;                //  toggle the re-draw flag on.
                      mode:=zoom_mode;              //  shapes are on the backdrop.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;

            shapes_now_x:=X;
            shapes_now_y:=Y;

            shapes_shiftx_now:=0;
            shapes_shifty_now:=0;

            shifted_already_x:=0;
            shifted_already_y:=0;

            do_rollback:=False;              // no need to put this change in rollback register on redraw.
          end;

  if corner2_mod=1
     then begin
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crDrag;
                      mouse_modify:=32;                //  toggle the re-draw flag on.
                      mode:=zoom_mode;              //  shapes are on the backdrop.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;

            shapes_now_x:=X;
            shapes_now_y:=Y;

            shapes_shiftx_now:=0;
            shapes_shifty_now:=0;

            shifted_already_x:=0;
            shifted_already_y:=0;

            do_rollback:=False;              // no need to put this change in rollback register on redraw.
          end;

  if oneshape_shift_mod=1
     then begin
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crDrag;
                      mouse_modify:=33;                //  toggle the re-draw flag on.
                      mode:=zoom_mode;              //  shapes are on the backdrop.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;
            shapes_now_x:=X;
            shapes_now_y:=Y;

            shapes_shiftx_now:=0;
            shapes_shifty_now:=0;

            shifted_already_x:=0;
            shifted_already_y:=0;

            do_rollback:=False;              // no need to put this change in rollback register on redraw.
          end;

  if clip_size_mod=1
     then begin
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crSizeWE;
                      mouse_modify:=85;             //  toggle the re-draw flag on.
                      mode:=zoom_mode;              //  shapes are on the backdrop.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;              //  then toggle re-draw off.
                    end;

            clip_size_now_X:=X;

            clip_size_now:=clip_size;

            do_rollback:=False;              // no need to put this change in rollback register on redraw.
          end;

  if clip_angle_mod=1
     then begin
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crSizeNS;
                      mouse_modify:=86;             //  toggle the re-draw flag on.
                      mode:=zoom_mode;              //  shapes are on the backdrop.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;              //  then toggle re-draw off.
                    end;

            clip_angle_now_Y:=Y;

            clip_angle_now:=clip_angle;

            do_rollback:=False;              // no need to put this change in rollback register on redraw.
          end;

  if allshapes_shift_mod=1
     then begin
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crMultiDrag;
                      mouse_modify:=34;                //  toggle the re-draw flag on.
                      mode:=zoom_mode;              //  shapes are on the backdrop.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;

            shapes_now_x:=X;
            shapes_now_y:=Y;

            shapes_shiftx_now:=0;
            shapes_shifty_now:=0;

            shifted_already_x:=0;
            shifted_already_y:=0;

            do_rollback:=False;              // no need to put this change in rollback register on redraw.
          end;

  if snake_mod=1
     then begin
            extend_if_zero;  // 0.93.a
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=adjust_we_cursor_invert;//crSizeWE;
                      mouse_modify:=35;                //  toggle the re-draw flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;
            peg_now_x:=X;
            pegx_now:=pegx;
            pegangle_now:=pegangle;

            kform_now:=kform;
            docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy1);    // save current peg data for peg_curve calcs.

            if spiral=True then os_now:=os;                  // need transition and slewing starts.
            if slewing=True then slew_s_now:=slew_s;
          end;


  if allshapes_scale_mod=1
     then begin
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crSizeNS;
                      mouse_modify:=36;             //  toggle the re-draw flag on.
                      mode:=zoom_mode;              //  shapes are on the backdrop.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;

            shapes_now_y:=Y;
            shapes_scaled_already:=1.0;

            do_rollback:=False;              // no need to put this change in rollback register on redraw.
          end;

  if allshapes_rotate_mod=1
     then begin
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crSizeNS;
                      mouse_modify:=37;                //  toggle the re-draw flag on.
                      mode:=zoom_mode;              //  shapes are on the backdrop.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;

            shapes_now_y:=Y;
            shapes_rot_now:=0;
            shapes_rotated_already:=0;

            if X<(rings[0,0]*fx+ex-gx) then twist_dir:=-1   // rotation direction depends which side of the current spacing ring was clicked.
                                       else twist_dir:=1;

            do_rollback:=False;              // no need to put this change in rollback register on redraw.
          end;

  if oneshape_scale_mod=1
     then begin
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crSizeWE;
                      mouse_modify:=38;             //  toggle the re-draw flag on.
                      mode:=zoom_mode;              //  shapes are on the backdrop.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;

            shapes_now_x:=X;
            shapes_scaled_already:=1.0;

            do_rollback:=False;              // no need to put this change in rollback register on redraw.
          end;

  if shove_length_mod=1
     then begin
            extend_if_zero;  // 0.93.a
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crSizeNS;
                      mouse_modify:=39;                //  toggle the re-draw flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;
            shove_now_y:=Y;
            shovel_now:=shovel;
          end;

  if shove_width_mod=1
     then begin
            extend_if_zero;  // 0.93.a
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crSizeWE;
                      mouse_modify:=40;                //  toggle the re-draw flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;
            shove_now_x:=X;
            shovew_now:=shovew;
          end;

  if shove_twist_mod=1
     then begin
            extend_if_zero;  // 0.93.a
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crSizeNS;
                      mouse_modify:=41;                //  toggle the re-draw flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;
            shove_now_y:=Y;
            shovek_now:=shovek;
          end;

  if blanking_mod=1
     then begin
            extend_if_zero;  // 0.93.a
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=adjust_we_cursor_invert;//crSizeWE;
                      mouse_modify:=42;                      //  toggle the re-draw flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;

            blanking_now:=X;
            startx_now:=startx;
          end;

  if pt_blanking_mod=1
     then begin
            extend_if_zero;  // 0.93.a
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=adjust_we_cursor_invert;  //crSizeWE;
                      mouse_modify:=87;                        //  toggle the re-draw flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;

            pt_blanking_now:=X;
            pt_blank_now:=pt_blank;
          end;

  if roll_mod=1
     then begin
            extend_if_zero;  // 0.93.a
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=adjust_we_cursor_invert;//crSizeWE;
                      mouse_modify:=43;                          //  toggle the re-draw flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;
            roll_now:=X;
            tb_roll_percent_now:=tb_roll_percent;
          end;

  if ringdia_mod=1                             // adjust ring dia.
     then begin
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crVSplit;
                      mouse_modify:=44;             //  toggle the re-draw flag on.
                      if mode=1 then mode:=3;       //  otherwise doesn't erase on pad.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;

            ringdia_now_y:=Y;                     // save clicked position.
            ring_dia_now:=ring_dia;
          end;

  if ruler1_mod=1
     then begin
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crDrag;
                      mouse_modify:=45;                //  toggle the re-draw flag on.
                      if mode=1 then mode:=3;          //  otherwise doesn't erase on pad.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;
            ruler1_now_x:=X;
            ruler1_now_y:=Y;
            ruler_startx_now:=ruler_startx;
            ruler_starty_now:=ruler_starty;
          end;

  if ruler2_mod=1
     then begin
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crDrag;
                      mouse_modify:=46;                //  toggle the re-draw flag on.
                      if mode=1 then mode:=3;          //  otherwise doesn't erase on pad.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;
            ruler2_now_x:=X;
            ruler2_now_y:=Y;
            ruler_endx_now:=ruler_endx;
            ruler_endy_now:=ruler_endy;
          end;

  if swell_mod=1
     then begin
            extend_if_zero;  // 0.93.a
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=adjust_ns_cursor_invert;   // custom_cursor.
                      mouse_modify:=47;                         //  toggle the re-draw flag on.
                      init_rotate(0,g/2,False);                 // set up transform constants (for template datum).
                     end
                else begin
                       Screen.Cursor:=crDefault;
                       mouse_modify:=0;                 //  then toggle re-draw off.
                     end;

            curving_now:=Y;            // save clicked position...
            kform_now:=kform;          // and current angle.
            turnoutx_now:=turnoutx;
            nomrad_now:=nomrad;
            if ABS(nomrad_now)>minfp then curvature_now:=1/nomrad_now
                                     else curvature_now:=1/minfp;

            docurving(True,True,       0,g/2,xc0,yc0,dummy1,dummy2);
            docurving(True,True,turnoutx,g/2,xc9,yc9,dummy1,dummy2);
            ends_apart_now:=SQRT(SQR(xc9-xc0)+SQR(yc9-yc0));           // chord length.
          end;

  if shove_crab_mod=1
     then begin
            extend_if_zero;  // 0.93.a
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crSizeWE;
                      mouse_modify:=48;               //  toggle the re-draw flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;
            shove_now_x:=X;
            shovec_now:=shovec;
          end;

  if move_labels_mod=1    // 0.82.d
     then begin
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crMultiDrag;
                      mouse_modify:=49;               //  toggle the re-draw flag on.
                      if mode=1 then mode:=3;         //  otherwise doesn't erase on pad.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;
            labels_now_x:=X;
            labels_now_y:=Y;
            xlabels_now:=xshift_labels;
            ylabels_now:=yshift_labels;
          end;

  // 0.93.a mouse_modify matches repeat_last_index for 50+ (see 99) ...

  if plat_ts_start_mod=1       // 0.93.a
     then begin
            extend_if_zero;  // 0.93.a
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crSizeWE;
                      mouse_modify:=50;                       //  toggle flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                        //  then off.
                    end;

            platform_ts_start_now_X:=X;
            platform_ts_start_mm_now:=platform_ts_start_mm;
          end;


  if plat_ts_length_mod=1       // 0.93.a
     then begin
            extend_if_zero;  // 0.93.a
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crSizeWE;
                      mouse_modify:=51;                       //  toggle flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                        //  then off.
                    end;

            platform_ts_length_now_X:=X;
            platform_ts_length_mm_now:=platform_ts_length_mm;
          end;


  if plat_ts_width1_mod=1       // 0.93.a
     then begin
            extend_if_zero;  // 0.93.a
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crSizeNS;
                      mouse_modify:=52;                       //  toggle flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                        //  then off.
                    end;

            platform_ts_start_width_now_Y:=Y;
            platform_ts_start_width_ins_now:=platform_ts_start_width_ins;
          end;


  if plat_ts_width2_mod=1       // 0.93.a
     then begin
            extend_if_zero;  // 0.93.a
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crSizeNS;
                      mouse_modify:=53;                       //  toggle flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                        //  then off.
                    end;

            platform_ts_end_width_now_Y:=Y;
            platform_ts_end_width_ins_now:=platform_ts_end_width_ins;
          end;


  if plat_ms_start_mod=1       // 0.93.a
     then begin
            extend_if_zero;  // 0.93.a
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crSizeWE;
                      mouse_modify:=54;                       //  toggle flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                        //  then off.
                    end;

            platform_ms_start_now_X:=X;
            platform_ms_start_mm_now:=platform_ms_start_mm;
          end;


  if plat_ms_length_mod=1       // 0.93.a
     then begin
            extend_if_zero;  // 0.93.a
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crSizeWE;
                      mouse_modify:=55;                       //  toggle flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                        //  then off.
                    end;

            platform_ms_length_now_X:=X;
            platform_ms_length_mm_now:=platform_ms_length_mm;
          end;


  if plat_ms_width1_mod=1       // 0.93.a
     then begin
            extend_if_zero;  // 0.93.a
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crSizeNS;
                      mouse_modify:=56;                       //  toggle flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                        //  then off.
                    end;

            platform_ms_start_width_now_Y:=Y;
            platform_ms_start_width_ins_now:=platform_ms_start_width_ins;
          end;


  if plat_ms_width2_mod=1       // 0.93.a
     then begin
            extend_if_zero;  // 0.93.a
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crSizeNS;
                      mouse_modify:=57;                       //  toggle flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                        //  then off.
                    end;

            platform_ms_end_width_now_Y:=Y;
            platform_ms_end_width_ins_now:=platform_ms_end_width_ins;
          end;


  if gaunt_offset_mod=1  // SHIFT+F12
     then begin
            extend_if_zero;  // 0.93.a
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crSizeNS;
                      mouse_modify:=58;               //  toggle the re-draw flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;

            gaunt_now:=Y;
            gaunt_offset_now_mm:=gaunt_offset_in*inscale;

            kform_now:=kform;
            arm_angle_now:=arm_angle;
            pegangle_now:=pegangle;
            docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy1);    // save current peg position on pad and curving angle.
          end;


  if vee_mod=1      // F9
     then begin
            extend_if_zero;  // 0.93.a
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=adjust_we_cursor_invert;    //crSizeWE;
                      mouse_modify:=59;
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;

            veexing_now:=X;
            vee_now:=k3n;

            kform_now:=kform;
            arm_angle_now:=arm_angle;
            pegangle_now:=pegangle;
            docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy1);    // save current peg position on pad and curving angle.
          end;


  if hdk_mod=1    // F10
     then begin
            extend_if_zero;  // 0.93.a
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=adjust_we_cursor_invert;    //crSizeWE;
                      mouse_modify:=60;
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;

            hdxing_now:=X;
            hdkn_now:=hdkn;

            kform_now:=kform;
            arm_angle_now:=arm_angle;
            pegangle_now:=pegangle;
            docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy1);    // save current peg position on pad and curving angle.
          end;


  if rect_left_mod=1
     then begin
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crSizeWE;
                      mouse_modify:=61;
                      mode:=zoom_mode;     //  outlines are on the backdrop.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;
                    end;

            rect_now_X:=X;
            output_rectangle_x_now:=output_rectangle_x1;

            do_rollback:=False;              // no need to put this change in rollback register on redraw.
          end;

  if rect_right_mod=1
     then begin
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crSizeWE;
                      mouse_modify:=62;
                      mode:=zoom_mode;     //  outlines are on the backdrop.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;
                    end;

            rect_now_X:=X;
            output_rectangle_x_now:=output_rectangle_x2;

            do_rollback:=False;              // no need to put this change in rollback register on redraw.
          end;

  if rect_top_mod=1
     then begin
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crSizeNS;
                      mouse_modify:=63;
                      mode:=zoom_mode;     //  outlines are on the backdrop.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;
                    end;

            rect_now_Y:=Y;
            output_rectangle_y_now:=output_rectangle_y2;

            do_rollback:=False;              // no need to put this change in rollback register on redraw.
          end;

  if rect_bottom_mod=1
     then begin
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crSizeNS;
                      mouse_modify:=64;
                      mode:=zoom_mode;     //  outlines are on the backdrop.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;
                    end;

            rect_now_Y:=Y;
            output_rectangle_y_now:=output_rectangle_y1;

            do_rollback:=False;              // no need to put this change in rollback register on redraw.
          end;

  if rect_move_mod=1
     then begin
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crSizeAll;
                      mouse_modify:=65;
                      mode:=zoom_mode;     //  outlines are on the backdrop.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;
                    end;

            rect_now_X:=X;
            rect_now_Y:=Y;
            output_rectangle_x_now:=output_rectangle_x1;
            output_rectangle_y_now:=output_rectangle_y1;

            do_rollback:=False;              // no need to put this change in rollback register on redraw.
          end;

  if check_diffs_len_mod=1     // 0.94.a
     then begin
            extend_if_zero;    // 0.93.a
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=adjust_we_cursor_invert;   // custom_cursor.  crSizeWE;
                      mouse_modify:=66;
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;
                    end;

            mouse_diffs_now_X:=X;
            mouse_diffs_now_Y:=Y;

            mouse_check_len_mm_now:=mouse_diff.len_diff*inscale;

            case current_diff_code of
              501,504: diff_dir:=0-1;  // +/-1 mouse diffing direction
                  else diff_dir:=1;
            end;//case

            min_diff:=(current_diffed_fl_len-current_diffed_len)/inscale+mouse_diff.len_diff;   // minimum negative diff

            if current_diff_code=504 then min_diff:=min_diff+cktsmid_offset/inscale;  // TS1 stop at F.P.
          end;

  if check_diffs_flare_mod=1   // 0.94.a
     then begin
            extend_if_zero;    // 0.93.a
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crSizeWE;
                      mouse_modify:=67;
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;
                    end;

            mouse_diffs_now_X:=X;
            mouse_diffs_now_Y:=Y;

            mouse_check_flare_mm_now:=mouse_diff.flr_diff*inscale;

            case current_diff_code of
              501,504: diff_dir:=1;  // +/-1 mouse diffing direction
                  else diff_dir:=0-1;
            end;//case

            case current_diff_code of    // !!!  k_flare_len is f-s ins,   xing_flare_len is model mm     *2.5 arbitrary, max flare angle 1:2.5
              507,508: begin
                         min_diff:=(current_diffed_end_gap-fw)*2.5/inscale-k_flare_len;  // minimum negative diff, K-crossing
                         max_diff:=current_diffed_len/inscale-k_flare_len;               // maximum positive diff, K-crossing
                       end;

                  else begin                                                                    // V-crossing
                         min_diff:=((current_diffed_end_gap-fw)*2.5-xing_flare_len)/inscale;    // minimum negative diff
                         max_diff:=(current_diffed_len-xing_flare_len)/inscale;                 // maximum positive diff

                         if current_diff_code=504 then max_diff:=max_diff-cktsmid_offset/inscale;  // TS1 stop at F.P.

                       end;
            end;//case
          end;

  if check_diffs_gap_mod=1     // 0.94.a
     then begin
            extend_if_zero;    // 0.93.a
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crSizeNS;
                      mouse_modify:=68;
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;
                    end;

            mouse_diffs_now_X:=X;
            mouse_diffs_now_Y:=Y;

            mouse_check_gap_mm_now:=mouse_diff.gap_diff;

            case current_diff_code of
              501,502,506,507: diff_dir:=1*hand_i;      // +/-1 mouse diffing direction
                          else diff_dir:=0-1*hand_i;
            end;//case

            min_diff:=fw-fw_end;  // bend-out can't go negative.
            max_diff:=g/2-fw_end; // or more than 1/2 track gauge

            if max_diff>current_diffed_fl_len/2.5+fw-fw_end then max_diff:=current_diffed_fl_len/2.5+fw-fw_end;  // 2.5 arbitrary, max flare angle 1:2.5
          end;

  if dv_mod=1     // 0.98.a
     then begin
            extend_if_zero;  // 0.93.a
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=adjust_we_cursor_invert;//crSizeWE;
                      mouse_modify:=69;                      //  toggle the re-draw flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;

            dv_now:=X;
            dv_start_now:=cdvi.dv_start;

            dv_copy_start_now:=dv_start_now;  // 215c
          end;

  if dv_clear_mod=1     // 0.98.a
     then begin
            extend_if_zero;  // 0.93.a
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crSizeNS;
                      mouse_modify:=70;                  //  toggle the re-draw flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;

            dv_clear_now:=Y;
            dv_clearance_now:=cdvi.dv_clearance;
          end;

          // 207a ...

  if plat_ms_skew1_mod=1
     then begin
            extend_if_zero;
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crSizeWE;
                      mouse_modify:=71;                       //  toggle flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                        //  then off.
                    end;

            platform_ms_start_skew_now_X:=X;
            platform_ms_start_skew_mm_now:=platform_ms_start_skew_mm;
          end;

  if plat_ms_skew2_mod=1
     then begin
            extend_if_zero;
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crSizeWE;
                      mouse_modify:=72;                       //  toggle flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                        //  then off.
                    end;

            platform_ms_end_skew_now_X:=X;
            platform_ms_end_skew_mm_now:=platform_ms_end_skew_mm;
          end;


  if plat_ts_skew1_mod=1
     then begin
            extend_if_zero;
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crSizeWE;
                      mouse_modify:=73;                       //  toggle flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                        //  then off.
                    end;

            platform_ts_start_skew_now_X:=X;
            platform_ts_start_skew_mm_now:=platform_ts_start_skew_mm;
          end;

  if plat_ts_skew2_mod=1
     then begin
            extend_if_zero;
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crSizeWE;
                      mouse_modify:=74;                       //  toggle flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                        //  then off.
                    end;

            platform_ts_end_skew_now_X:=X;
            platform_ts_end_skew_mm_now:=platform_ts_end_skew_mm;
          end;

  if turnout_road_mod=1   // 209a  CTRL+F12
     then begin
            extend_if_zero;  // 0.93.a
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=adjust_we_cursor_invert; //crSizeWE;
                      mouse_modify:=75;                       //  toggle flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                        //  then toggle off.
                    end;

            turnout_road_now_X:=X;
            turnout_road_endx_now:=turnout_road_endx;

            kform_now:=kform;
            docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy1);    // save current peg position on pad and curving angle.
            if peg_code=241 then dummy1:=aq25offset(pegx,now_peg600);               // peg on TEXITP, save current pegangle for curviform V-crossing. 0.79.a

          end;

  if adjacent_ts_mod=1    // 213a
     then begin
            extend_if_zero;  // 0.93.a
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crSizeNS;
                      mouse_modify:=76;                  //  toggle the re-draw flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;

            ts_adj_now:=Y;
            trtscent_pi_now:=cpi.trtscent_pi;
          end;

  if adjacent_ms_mod=1    // 213a
     then begin
            extend_if_zero;  // 0.93.a
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crSizeNS;
                      mouse_modify:=77;                  //  toggle the re-draw flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;

            ms_adj_now:=Y;
            trmscent_pi_now:=cpi.trmscent_pi;
          end;


  if edge_ts_start_mod=1       // 215a      trackbed edges ...
     then begin
            extend_if_zero;
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crSizeWE;
                      mouse_modify:=78;                       //  toggle flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                        //  then off.
                    end;

            trackbed_ts_start_now_X:=X;
            trackbed_ts_start_mm_now:=trackbed_ts_start_mm;
          end;


  if edge_ts_length_mod=1       // 215a
     then begin
            extend_if_zero;
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crSizeWE;
                      mouse_modify:=79;                       //  toggle flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                        //  then off.
                    end;

            trackbed_ts_length_now_X:=X;
            trackbed_ts_length_mm_now:=trackbed_ts_length_mm;
          end;

  if edge_ms_start_mod=1       // 215a
     then begin
            extend_if_zero;
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crSizeWE;
                      mouse_modify:=80;                       //  toggle flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                        //  then off.
                    end;

            trackbed_ms_start_now_X:=X;
            trackbed_ms_start_mm_now:=trackbed_ms_start_mm;
          end;


  if edge_ms_length_mod=1       // 215a
     then begin
            extend_if_zero;
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=crSizeWE;
                      mouse_modify:=81;                       //  toggle flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                        //  then off.
                    end;

            trackbed_ms_length_now_X:=X;
            trackbed_ms_length_mm_now:=trackbed_ms_length_mm;
          end;

    if main_road_mod=1   // 217a
     then begin
            extend_if_zero;
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=adjust_we_cursor_invert;
                      mouse_modify:=82;                       //  toggle flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                        //  then toggle off.
                    end;

            main_road_now_X:=X;
            main_road_endx_now:=main_road_endx;

            kform_now:=kform;
            docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy1);    // save current peg position on pad and curving angle.
            if peg_code=241 then dummy1:=aq25offset(pegx,now_peg600);               // peg on TEXITP, save current pegangle for curviform V-crossing. 0.79.a

          end;

    if gaunt_radius_mod=1  // 217b
       then begin
              extend_if_zero;  // 0.93.a
              set_opp;
              if mouse_modify=0
                 then begin
                        Screen.Cursor:=crSizeNS;
                        mouse_modify:=83;               //  toggle the re-draw flag on.
                      end
                 else begin
                        Screen.Cursor:=crDefault;
                        mouse_modify:=0;                   //  then toggle re-draw off.
                      end;

              gaunt_rad_now:=Y;
              gaunt_curvature_now:=gaunt_curvature;

              kform_now:=kform;
              arm_angle_now:=arm_angle;
              pegangle_now:=pegangle;
              docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy1);    // save current peg position on pad and curving angle.
            end;


    if locator_mod=1     // 227a
       then begin
              extend_if_zero;  // 0.93.a
              if locator_rail<>0 then set_opp;    //  do opposite way if running locator along rails.
              if mouse_modify=0
                 then begin
                        if locator_rail=0 then Screen.Cursor:=crNone                    //  free move.
                                          else Screen.Cursor:=adjust_we_cursor_invert;  //  along rails.
                        mouse_modify:=84;                                               //  toggle the re-draw flag on.
                      end
                 else begin
                        Screen.Cursor:=crDefault;
                        mouse_modify:=0;                   //  then toggle re-draw off.
                      end;

              locator_now_x:=X;
              locator_now_y:=Y;
              locatorx_now:=locatorx;
              locatory_now:=locatory;

              docurving(False,True,locatorx,locatory,dummy1,dummy2,locatork,dummy3);    // save current locator curving angle.
            end;



//----
      // 99 ...

  if entry_straight_mod=1    // 0.91.b
     then begin
            extend_if_zero;  // 0.93.a
            set_opp;
            if mouse_modify=0
               then begin
                      Screen.Cursor:=adjust_we_cursor_invert;    //crSizeWE;
                      mouse_modify:=99;                          //  99 0.93.a (was 50) toggle the re-draw flag on.
                    end
               else begin
                      Screen.Cursor:=crDefault;
                      mouse_modify:=0;                   //  then toggle re-draw off.
                    end;

            entry_straight_now:=X;
            fixed_sl_now:=fixed_sl;

            min_sl_offset:=MAX((g/5),(h+scale/4));     // 213a

            min_sl:=0;   //  227a     was ABS(fw*k3n);      // 214a  not used            and CESP not beyond knuckle.

            kform_now:=kform;
            docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy1);    // save current peg position on pad.

          end;
                    //------------
  end;//with form

  if mouse_modify=0
     then begin
            if mouse_action_button_down=False then mouse_action_release(False);   // second click.
          end
     else mouse_action_grab(mode,pad_click_X,pad_click_Y);                 // (need actual, not as swapped for opposite way).

end;
//___________________________________________________________________________________________
(*  OT2024

function ask_impact_matrix(prindex:integer; prstr:string):boolean;    // ask if printer is dot-matrix impact type.
                                                                      // return TRUE if valid answer.
const

 TECHNOLOGY=2;

  impact_str:string='     Impact  Printer ?'
    +'||Are you are using an older-pattern dot-matrix or other impact printer (i.e. one using an ink ribbon like a typewriter and capable of printing carbon copies) ?'
    +'|----------------------------------------------'
    +'|If you click YES Templot will simplify some of the output to reduce wear and tear on the print-head (and also the noise).'
    +' This is strongly recommended when calibrating the printer.'
    +'||All line thicknesses (line widths) will be set to 1 dot, regardless of any other settings you may have made for them.'

    +'||Also click YES if this is a pen-plotter, but the fact that you are seeing this message implies something amiss in the plotter driver, as Windows has failed'
    +' to inform Templot that this printer is a plotter.'

    +'|----------------------------------------------'
    +'|Click NO or just press ENTER if this printer is an ink-jet or laser-printer, or other modern printer capable of printing filled areas, or a virtual printer such as a Fax sender.'

    +'||Also click NO if you are not sure.'

    +'||If you click CANCEL, Templot will the assume that the answer is NO for the purpose of printing templates, but you will be unable to calibrate your printer for maximum accuracy.'

    +'||( Hopefully, the next version of Templot will have devised a means of answering this question for itself.)';

var
  impact:integer;
  i:integer;

begin
  RESULT:=False;                                         // init error returns...
  impact:=-1;

  if GetDeviceCaps(Printer.Handle,TECHNOLOGY)=DT_PLOTTER
     then begin
            impact:=2;                // its a pen plotter.
            RESULT:=True;             // so yes also.
          end
     else begin

            repeat
              i:=alert(4,'      impact  printer  ? ...',
                         'The current printer is  '+prstr
                        +'||Is this printer an old style dot-matix or other impact printer (using an ink ribbon) ?',
                         '','','?  help','yes    -    impact  printer   /   pen - plotter','cancel','no    -    ink - jet   /   laser   /   virtual   printer    ',3);
              case i of
                  3: alert_help(0,impact_str,'');
                  4: begin
                       impact:=1;              // save the yes answer for this printer,
                       RESULT:=True;
                     end;
                  6: begin
                       impact:=0;              // save the no answer for this printer.
                       RESULT:=True;
                     end;
              end;//case
            until i>3;
          end;
  Tprint_cal(printer_list.Objects[prindex]).cal_data.printer_impact:=impact;
end;
//___________________________________________________________________________________________
*)

procedure printer_setup(cal_in_progress, black_white_setup:boolean);

const
  BITSPIXEL=12;
  PLANES=14;

  print_col_help_str:string='||Templot has determined that this printer is capable of printing in colour, but you are currently'
                           +' printing in black and white.'
                           +'||Changing to colour printing here affects only Templot''s internal settings. To actually print in colour'
                           +' you must have the correct ink cartridge installed and make the necessary settings on the printer'
                           +' setup dialog window. Refer to your printer documentation for details of colour printing settings.';
var
  cal:Tcal_data;
  i:integer;
  prindex:integer;
  prstr:string;
  colour_depth_bits:integer;

  //DC:HDC;

begin
  if no_printer_available=True
     then begin
            alert(6,'    no  printer',
                    'Templot is unable to locate a printer driver on this system. To install a printer driver, from the Windows taskbar click Start > Settings > Printers > Add Printer.',
                    '','','','','cancel','',0);
            EXIT;
          end;

  repeat

    showing_dialog:=True;   // 212a Wine bug

    if pad_form.print_setup_dialog.Execute=False
       then begin
              showing_dialog:=False;   // 212a Wine bug
              EXIT;
            end;

    showing_dialog:=False;   // 212a Wine bug

    if get_prindex(prindex)=False      // current selected printer index.
       then begin
              if alert(6,'    no  printer',
                       'Templot is unable to find the selected printer.'
                      +'||Please select another.','','','','','cancel','O K',0)=5 then EXIT;
            end
       else BREAK;
  until 0<>0;

  prstr:=printer_list.Strings[prindex];       // and name.

(* // OT2024

  if Tprint_cal(printer_list.Objects[prindex]).cal_data.printer_impact=-1
     then begin
            if ask_impact_matrix(prindex,prstr)=False then EXIT;
          end;
*)

  try
    with pad_form do begin

      cal:=Tprint_cal(printer_list.Objects[prindex]).cal_data;

      if Printer.Orientation=poPortrait then portrait_menu_entry.Click;
      if Printer.Orientation=poLandscape then landscape_menu_entry.Click;
{
      DC:=GetDC(Printer.Canvas.Handle);
//  FDPI := GetDeviceCaps(DC,LOGPIXELSX); // DPI in pixels per inch
  ReleaseDC(Printer, DC);
}

      // OT2024 colour_depth_bits:=GetDeviceCaps(Printer.Canvas.Handle,BITSPIXEL)*GetDeviceCaps(Printer.Canvas.Handle,PLANES);

      colour_depth_bits:=24;  // OT2024  kludge

      if (colour_depth_bits>1) and (black_white=True)
         then begin
                repeat
                  i:=alert(4,'  '+prstr,
                           'The current printer is  '+prstr
                          +'||This is a colour printer.'
                          +'||Change to full-colour or grey-shade printing ?',
                           '','','more  information','change  to  grey - shade  printing','continue  black && white  printing','change  to  full - colour  printing',3);
                  case i of
                      3: alert_help(0,'   '+prstr+print_col_help_str,'');
                      4: grey_shade_printing_menu_entry.Click;
                      5: if black_white_setup=False
                         then begin
                                if set_black_and_white=True then EXIT;     // for the setup warning, (but not if called from there).
                                                                           // if True he re-entered this routine, so we can exit.
                              end;
                      6: colour_printing_menu_entry.Click;              // reset colour printer.
                  end;//case
                until i<>3;
              end;

      if (cal.printer_calibrated=False) and (cal_in_progress=False)
         then begin
                repeat                      // no cal done and not called from calibration function.
                  i:=alert(2,'    printer  not  calibrated',
                             '||The current printer is  '+prstr
                            +'||Templot has no calibration information for this printer.'
                            +'||Using the printer uncalibrated will probably be satisfactory for overall track design and printing trial templates.'
                            +'||Before printing the final construction templates a printer calibration is recommended to ensure dimensional accuracy.',
                             '','more  information','load  previous  calibration  settings  from  file','calibrate  this  printer  now','','use  printer  uncalibrated',2);
                  case i of
                      2: alert_help(0,cal_prompt_str,'');
                      3: calibration_load(prindex);
                      4: calibrate_printer;                 // do printer calibration.
                      6: EXIT;
                    else run_error(135);
                  end;//case
                until i<>2;
              end;
    end;//with
  finally
    printer_setup_done:=True;            // flag that the printer setup has been shown at least once.
    page_info(True,True,False,0);        // for the page outlines on the pad.
  end;//try
end;
//______________________________________________________________________________________

procedure update_ruler_div;   // 0.93.a

begin
  ruler_div:=grid_spacex;             // make ruler divisions the same as the grid X
  if ruler_div<1 then ruler_div:=1;   // 1mm prevent neg or zero.
end;
//______________________________________________________________________________

procedure set_grid_spacings(calling_form:TForm);

var
  i, n:integer;
  od:Toutdim;        // [0..7] array of extended;

begin
  i:=alert(4,'   grid  line  spacings ...',
             'The grid can be drawn in any of these units. For small scale models it is usually more convenient to work in mm.'
            +'||If PROTO-FEET is selected, the grid units will be in prototype feet at your current model scale.'
            +'||Which do you want to use ?',
             'feet','inches','proto - feet','cm','cancel','mm',0);
  case i of

      1: begin                          // feet
           n:=putdim('',6,'X grid spacing on bottom margin (along page)',grid_spacex/304.8,True,False,True,False);     // neg or 0 not allowed.
           n:=putdim('',6,'Y grid spacing on left margin (across page)',grid_spacey/304.8,True,False,True,False);    // neg or 0 not allowed.
           if n<>1 then EXIT;
           if getdims('grid  line  spacings  in  feet',grid_help_all_str,calling_form,n,od)=True
             then begin
                    if od[0]=def_req then grid_spacex:=304.8              // 1 foot default
                                     else grid_spacex:=ABS(od[0]*304.8);
                    if od[1]=def_req then grid_spacey:=304.8
                                     else grid_spacey:=ABS(od[1]*304.8);
                  end;
           grid_labels_code_i:=1;
         end;

      2: begin                          // inches
           n:=putdim('',2,'X grid spacing on bottom margin (along page)',grid_spacex/25.4,True,False,True,False);     // neg or 0 not allowed.
           n:=putdim('',2,'Y grid spacing on left margin (across page)',grid_spacey/25.4,True,False,True,False);    // neg or 0 not allowed.
           if n<>1 then EXIT;
           if getdims('grid  line  spacings  in  inches',grid_help_all_str,calling_form,n,od)=True
             then begin
                    if od[0]=def_req then grid_spacex:=25.4              // 1 inch default
                                     else grid_spacex:=ABS(od[0]*25.4);
                    if od[1]=def_req then grid_spacey:=25.4
                                     else grid_spacey:=ABS(od[1]*25.4);
                  end;
           grid_labels_code_i:=2;
         end;

      3: begin                          // proto-feet
           n:=putdim('',7,'X grid spacing on bottom margin (along page)',(grid_spacex/scale),True,False,True,False);     // neg or 0 not allowed.
           n:=putdim('',7,'Y grid spacing on left margin (across page)',(grid_spacey/scale),True,False,True,False);    // neg or 0 not allowed.
           if n<>1 then EXIT;
           if getdims('grid  line  spacings  in  prototype  feet',grid_help_all_str,calling_form,n,od)=True
             then begin
                    if od[0]=def_req then grid_spacex:=scale*25           // 25ft (100 mm in 4mm scale) default
                                     else grid_spacex:=ABS(od[0]*scale);  // negative spacing not allowed.
                    if od[1]=def_req then grid_spacey:=scale*25
                                     else grid_spacey:=ABS(od[1]*scale);
                  end;
           grid_labels_code_i:=3;
         end;

      4: begin                          // cm
           n:=putdim('',5,'X grid spacing on bottom margin (along page)',(grid_spacex/10),True,False,True,False);     // neg or 0 not allowed.
           n:=putdim('',5,'Y grid spacing on left margin (across page)',(grid_spacey/10),True,False,True,False);    // neg or 0 not allowed.
           if n<>1 then EXIT;
           if getdims('grid  line  spacings  in  cm',grid_help_all_str,calling_form,n,od)=True
             then begin
                    if od[0]=def_req then grid_spacex:=20               // 2 cm default
                                     else grid_spacex:=ABS(od[0]*10);   // negative spacing not allowed.
                    if od[1]=def_req then grid_spacey:=20
                                     else grid_spacey:=ABS(od[1]*10);
                  end;
           grid_labels_code_i:=4;
         end;

      5: EXIT;

      6: begin                          // mm
           n:=putdim('',1,'X grid spacing on bottom margin (along page)',grid_spacex,True,False,True,False);     // neg or 0 not allowed.
           n:=putdim('',1,'Y grid spacing on left margin (across page)',grid_spacey,True,False,True,False);    // neg or 0 not allowed.
           if n<>1 then EXIT;
           if getdims('grid  line  spacings  in  mm',grid_help_all_str,calling_form,n,od)=True
             then begin
                    if od[0]=def_req then grid_spacex:=20            // 20 mm  default.
                                     else grid_spacex:=ABS(od[0]);   // negative spacing not allowed.
                    if od[1]=def_req then grid_spacey:=20
                                     else grid_spacey:=ABS(od[1]);
                  end;
           grid_labels_code_i:=6;
         end;

    else run_error(71);
  end;//case

  if grid_spacex<0.1 then grid_spacex:=0.1;     // minimum 0.1 mm.
  if grid_spacey<0.1 then grid_spacey:=0.1;

  if ruler_units=0 then update_ruler_div;   // 0.93.a ruler as grid option

  do_rollback:=False;       // no need to put this change in rollback register on redraw.
  redraw(True);
end;
//______________________________________________________________________________________

procedure toggle_radius_lamp;

begin
  if toggle_1=0
     then begin
            toggle_1:=1;
            info_form.min_rad_lamp_panel.Color:=clRed;
          end
     else begin
            toggle_1:=0;
            info_form.min_rad_lamp_panel.Color:=clBlack;
          end;
end;
//____________________________________________________________________________________

procedure toggle_ring_lamp;

begin
  if toggle_2=0
     then begin
            toggle_2:=1;
            if (cl_only=True) or (warn_centrelines=True)
               then info_form.ring_lamp_panel.Color:=clWhite
               else info_form.ring_lamp_panel.Color:=clRed;
          end
     else begin
            toggle_2:=0;
            info_form.ring_lamp_panel.Color:=clBlack;
          end;
end;
//______________________________________________________________________________

procedure do_hide_current;      // set flags to hide control template.

begin
  pad_form.hide_control_template_menu_entry.Checked:=True;   // 209c  radio item
  pad_form.hide_control_template_popup_entry.Checked:=True;  // 209c  radio item

  hide_current_flag:=True;
  save_hide:=True;
  current_is_showing:=False;
  redraw_pad(True,False);
end;
//______________________________________________________________________________

procedure extend_template_from_zero;

begin
  turnoutx:=132*scale;     // arbitrary new length 2 chains.
  turnout_i:=1;            // length locked at turnoutx.
  if plain_track=True then xorg:=turnoutx
                      else xorg:=0;
  startx:=0;
  redraw_pad(False,True);     // immediate re-calc
end;
//______________________________________________________________________________

function check_control_template_is_valid(cancel_str:string):boolean;   // 0.93.a

const
  zero_help_str:string='php/199    `0Zero-Length Control Template`9'

  +'||The control template can be invalidated by having its length set to zero.'
  +'||The usual reason this is done is to prevent a copy of the control template being automatically stored in the storage box, which would otherwise create duplicate stored templates.'
  +'||A zero-length control template is shown on the trackpad as a red  `0<B>Z</B>`8  symbol replacing its fixing peg, and in `0red text`8 on the information bar.'
  +'||When the control template is shown as zero-length, your usual next action will be either to create a new mint template by clicking the <span style="color:#0000ff; font-size:15px;"><B>NEW</B></span> tool-button,'
  +' or to click on an existing background template in order to modify or copy that template.'
  +'||More experienced users can simply extend the length of the control template (`0F4`2 mouse action) and continue working with it.'
  +' Selecting any other mouse action will automatically extend the control template to a scale length of 2 chains (132ft).'
  +'||If desired the control template can be invalidated manually by clicking the Z tool-button or by pressing the `0DELETE`2 key.'
  +' If the current control template is already zero-length when this button is clicked, the most recent manually invalidated template will be restored.';

var
  i:integer;

begin
  RESULT:=(turnoutx>0);

  if (RESULT=False) and (bgnd_clicked_in_quick_mode=False)
     then begin
            repeat
              i:=alert(2,'php/199    zero - length  template',
                         ' |The length of the control template is zero.'
                        +'||A zero-length template can not be stored in the storage box, or saved in a data file, or used as a background template in a track plan, or used to make a crossover, diamond-crossing, branch track, etc.| ',
                         '','','?  more  information','extend  template  to  2  chains  length  and  continue     ','cancel  '+cancel_str,'',3);

              if i=3 then alert_help(0,zero_help_str,'');

            until i<>3;

            if i=4
               then begin
                      extend_template_from_zero;
                      RESULT:=True;
                    end;
          end;
end;
//______________________________________________________________________________

procedure set_y_datum;          // set up default y position.
                                // set y_datum (in mm.)
                                // y_datum is positive from left sheet edge to calc origin.

begin
  case hand_i of

           1: y_datum:=6*scale;              // l.h. turnout y datum default (6ft scale) from left margin.

          -1: if (screeny-page_width/100)>(4*scale)     // more than 4ft scale beyond whole page width visible ?
                 then y_datum:=page_width/100-6*scale   // r.h. turnout y datum default 6ft scale from far edge of sheet.

                 else y_datum:=screeny-10*scale;         // r.h  10ft scale from visible screen height.

         else  run_error(17);
  end;//case
end;
//_______________________________________________________________________________________________________________________________

function show_a_line(on_canvas:TCanvas; aq, pen_width:integer; erasing:boolean):boolean;     //  draw a rail-line on the screen.

  // (RESULT is never used)

var
  now, now_max:integer;
  move_to, line_to, save_line_to:TPoint;

begin
  RESULT:=False;    // default init.

  if aqyn[aq]=False then EXIT;         //  no valid data to draw.

  try
    with on_canvas do begin

      Pen.Color:=ink_colour;
      Pen.Mode:=pmCopy;

      Pen.Width:=pen_width;    // 212a

      Brush.Color:=paper_colour;  // gaps in dotted lines.
      Brush.Style:=bsSolid;

      TextOut(0,0,'');  // needed for dotted lines - Delphi bug?

      Pen.Style:=psSolid;

      if (aq=16) and (adjacent_edges=True) and (draw_ts_platform_rear_edge=False) then Pen.Style:=psDot;    // 0.93.a show dotted on screen if hidden on output.
      if (aq=20) and (adjacent_edges=True) and (draw_ms_platform_rear_edge=False) then Pen.Style:=psDot;

      now_max:=nlmax_array[aq];

      move_to.X:=Round(outoflist(aq,0,0)*sx+ex-gx);  move_to.Y:=Round((outoflist(aq,0,1)+yd)*sy+by-gy);
      for now:=1 to now_max do begin
	  line_to.X:=Round(outoflist(aq,now,0)*sx+ex-gx);  line_to.Y:=Round((outoflist(aq,now,1)+yd)*sy+by-gy);

          save_line_to:=line_to;  // in case check_limits modifies it (e.g paper bunching)

	  if check_limits(move_to, line_to)=True then begin MoveTo(move_to.X, move_to.Y); LineTo(line_to.X, line_to.Y); end;
	  move_to:=save_line_to;
      end;//for
    end;//with
    RESULT:=True;
  except
    EXIT;               // abandon if any calc errors
  end;//try
end;
//_______________________________________________________________________________________________________________________________

function calc_peg_dims(code:integer; var pegx,pegy,pegangle:extended):Tpeg_indicator_string;

           // called from turnout_dims.
var
  temp_x:extended;

    // for dummy vehicle  223a ..

  tr_y,tr_k:extended;  // on turnout road rail
  xtr,ytr:extended;    // on turnout road centre-line

  osk:extended;  // 226a

begin

  RESULT:='-';     // default init.

  case code of
            -2: begin {on a rail joint} pegy:=g/2;  pegangle:=0;  RESULT:='J'; end;
            -1: RESULT:='S'; // do nothing
             0: begin pegx:=0;        pegy:=g/2;  pegangle:=0;  RESULT:='0'; end;
             1: begin pegx:=xorg;     pegy:=g/2;  pegangle:=0;  RESULT:='1'; end;
             2: begin pegx:=toemidx;  pegy:=g/2;  pegangle:=0;  RESULT:='2'; end;
             3: begin pegx:=dpx;      pegy:=g/2;  pegangle:=0;  RESULT:='N'; end;     // 234e  was 3
             4: begin pegx:=fpx;      pegy:=g;    pegangle:=0;  RESULT:='4'; end;

             5: begin pegx:=tcpx;     pegy:=tcpy; pegangle:=k3;         RESULT:='N'; end;
             6: begin pegx:=txpx;     pegy:=aq25offset(pegx,pegangle);  RESULT:='5'; end;
             7: begin pegx:=trpx;     pegy:=trpy; pegangle:=0;          RESULT:='N'; end;

             8: begin pegx:=mcpx;     pegy:=g/2;  pegangle:=0;          RESULT:='N'; end;
             9: begin pegx:=mxpx;     pegy:=g/2;  pegangle:=0;          RESULT:='7'; end;
            10: begin pegx:=mrpx;     pegy:=g/2;  pegangle:=0;          RESULT:='N'; end;

            11: begin pegx:=turnoutx; pegy:=g/2; pegangle:=0; RESULT:='9'; end;

            12: begin pegx:=0;        pegy:=g/2+trtscent;  pegangle:=0; RESULT:='N'; end;  // adjacent turnout-side track centre.
            13: begin pegx:=0;        pegy:=g/2-trmscent;  pegangle:=0; RESULT:='N'; end;  // adjacent main-side track centre.
            14: begin pegx:=torgx;    pegy:=torgy;  pegangle:=k3+Pi/2;  RESULT:='N'; end;  // turnout rad centre.
            15: begin pegx:=os;       pegy:=g/2;  pegangle:=0;          RESULT:='N'; end;  // transition start.
            16: begin pegx:=os+tst;   pegy:=g/2;  pegangle:=0;          RESULT:='N'; end;  // transition end (length).
            17: begin pegx:=mvjpx;    pegy:=g/2;  pegangle:=0;          RESULT:='8'; end;  // vee joint (point rail end).
            18: begin pegx:=tvjpx;    pegy:=aq25offset(pegx,pegangle);  RESULT:='6'; end;  // vee joint (splice rail end).

            19: begin pegx:=(turnoutx+startx)/2; pegy:=g/2;  pegangle:=0;  RESULT:='N'; end;  // mid-length  5-3-01.     startx blanking included 216a

            20: begin
                  case peg_rail of               // pegx set by trail_peg, slide or snake mouse actions...
                           0: do_nothing;        // free move, pegy set by trail_peg also.
                           1: begin
                                pegy:=0;                       // straight stock rail       aq=0.
                                pegangle:=0;
                              end;
                           2: pegy:=aq2offset(pegx,pegangle);  // curved turnout rail       aq=2.
                           3: begin
                                pegy:=g;                       // straight turnout rail     aq=1.
                                pegangle:=0;
                              end;

                           4: if plain_track=True
                                 then begin
                                        pegy:=g;       // TS running rail plain track.
                                        pegangle:=0;
                                      end
                                 else pegy:=aq3offset(pegx,pegangle);  // curved stock rail         aq=3.

                           5: begin
                                pegy:=g/2-trmscent;            // adjacent main-side centre-line.
                                pegangle:=0;
                              end;
                           6: begin
                                pegy:=g/2+trtscent;            // adjacent turnout-side centre-line.
                                pegangle:=0;
                              end;
                           7: begin
                                pegy:=(g+trtscent)/2;          // double-track turnout-side centre-line.
                                pegangle:=0;
                              end;
                           8: begin
                                pegy:=g/2;                     // main road centre-line     aq=24.
                                pegangle:=0;
                              end;
                           9: pegy:=aq25offset(pegx,pegangle); // turnout road centre-line  aq=25.
                         else begin
                                pegy:=g/2;
                                pegangle:=0;
                              end;
                  end;//case
                  if mouse_modify>-1 then RESULT:='M'         // mouse action moving the peg.
                                     else RESULT:='F';
                end;

            22: begin pegx:=toemidx;  pegy:=g/2;  pegangle:=k1;  RESULT:='22'; end;    // toggle CTRL-2 options.
            33: begin pegx:=dpx;      pegy:=g/2;  pegangle:=k3;  RESULT:='NN'; end;    // toggle options.  234e wqs 33
            44: begin pegx:=fpx;      pegy:=g;    pegangle:=k3;  RESULT:='44'; end;    // toggle CTRL-4 options.

           100: begin  // peg on end of planing...

                  if (half_diamond=True) and (fixed_diamond=True)   // fixed diamond.
                     then begin
                            pegx:=toemidx;    // no planing - put it on centre instead (menu is disabled).
                            pegy:=g/2;
                            pegangle:=k1;
                          end
                     else begin
                            pegx:=(plox+stox)/2;           // on nominal radial end mark (not strictly radial).

                            if (ABS(pegx-sworgx)<ABS(swrad-g/2-minfp)) and (gaunt=False)
                               then begin
                                      pegy:=sworgy-SQRT(SQR(swrad-g/2)-SQR(pegx-sworgx));
                                      pegangle:=ARCSIN((pegx-sworgx)/(swrad-g/2));
                                    end
                               else begin          // gaunt or ??? ...
                                      pegx:=0;
                                      pegy:=g/2+h;
                                      pegangle:=0;
                                    end;
                          end;
                  RESULT:='N';
                end;


           104:  begin                      // peg on switch heel...
                  if half_diamond=True
                     then begin               // fixed-diamond or switch-diamond.
                            pegx:=toemidx;    // no heel - put it on centre instead (menu is disabled).
                            pegy:=g/2;
                            pegangle:=k1;
                          end
                     else begin
                            if gaunt=True     // 0.93.a ex 081 ...
                               then begin
                                      pegx:=xorg;
                                      pegy:=g/2+h;
                                      pegangle:=0;
                                    end
                               else begin
                                      pegx:=torgx+(tradius-g/2)*SIN(k2);  //^^^  // switch curve end / turnout curve start.
                                      pegy:=torgy-(tradius-g/2)*COS(k2);  //^^^
                                      pegangle:=k2;
                                    end;
                          end;
                  RESULT:='N';
                end;

           108: begin   // CESP
                  if (tradius_is_straight=True) or (xing_type_i<>0)  // no CESP, return TCP instead
                     then begin
                            pegx:=tcpx;
                            pegy:=tcpy;
                          end
                     else begin                                 // peg on CESP (crossing entry straight point=end of turnout curve)...
                            pegx:=torgx+(tradius-g/2)*SIN(k3);  // turnout curve end.
                            pegy:=torgy-(tradius-g/2)*COS(k3);
                          end;

                  pegangle:=k3;
                  RESULT:='N';
                end;

           200: begin pegx:=egpx;        pegy:=g/2;        pegangle:=0;  RESULT:='N'; end;      // EGTP
           201: begin pegx:=egeo_orgx;   pegy:=egeo_orgy;  pegangle:=0;  RESULT:='N'; end;      // EGORG
           220: begin pegx:=igpx;        pegy:=g/2;        pegangle:=0;  RESULT:='N'; end;      // IGTP
           221: begin pegx:=igeo_orgx;   pegy:=igeo_orgy;  pegangle:=0;  RESULT:='N'; end;      // IGORG

           222: begin pegx:=toemidx;  pegy:=g/2;  pegangle:=k1/2;  RESULT:='SD'; end;    // split deflection  213a

           240: begin pegx:=xorg+min_turnout_road_endx;  pegy:=aq25offset(pegx,pegangle);  RESULT:='N'; end;    // TMINP   213b
           241: begin pegx:=xorg+turnout_road_endx;      pegy:=aq25offset(pegx,pegangle);  RESULT:='E'; end;    // TEXITP  213b

           260: begin pegx:=xorg+min_main_road_endx;  pegy:=g/2;  pegangle:=0;  RESULT:='N'; end;    // MMINP   217a
           261: begin pegx:=xorg+main_road_endx;      pegy:=g/2;  pegangle:=0;  RESULT:='E'; end;    // MEXITP  217a

           270: begin                       // 217b  mid-point of turnout curve   first get switch heel...
                  if half_diamond=True
                     then temp_x:=toemidx    // no heel - put it on centre instead (menu is disabled).
                     else if gaunt=True
                          then temp_x:=xorg
                          else temp_x:=torgx+(tradius-g/2)*SIN(k2);  // switch curve end / turnout curve start.

                  case xing_calc_i of             // turnout curve end...
                       -1: pegx:=(fpx+temp_x)/2;                          // generic FP
                        0: pegx:=(torgx+(tradius-g/2)*SIN(k3)+temp_x)/2;  // regular CESP
                        1: pegx:=(fpx+temp_x)/2;                          // curviform FP   was TVJP
                      else pegx:=0;   // ???
                  end;//case

                  pegy:=aq25offset(pegx,pegangle);  RESULT:='N';
                end;

           275: begin                             // 218d  mid-point of return curve     used in radius calcs
                  pegx:=retrorgx-retr*SIN(tvjpk/2);
                  pegy:=retrorgy+retr*COS(tvjpk/2);
                  pegangle:=tvjpk/2;

                  RESULT:='N';
                end;


           279: begin pegx:=wingcx_minus-scale/6; pegy:=g; pegangle:=0; RESULT:='N'; end;  // 218a  start of knuckle bend minus 2" arbitrary

           280: begin pegx:=atx;           pegy:=g;    pegangle:=0;  RESULT:='N'; end;     // 218a  peg on "A" timber, gauge face

           285: begin pegx:=bnox;          pegy:=g+j;  pegangle:=0;  RESULT:='N'; end;     // 218a  peg on end of vee splice

           300: begin pegx:=slew_s;        pegy:=g/2;  pegangle:=0;  RESULT:='N'; end;     // peg on slewing start.
           301: begin pegx:=slew_s+slew_l; pegy:=g/2;  pegangle:=0;  RESULT:='N'; end;     // peg on slewing end.

           400: begin pegx:=os-ts1;        pegy:=g/2;  pegangle:=0;  RESULT:='N'; end;     // peg on trans calcs origin.

           600: begin pegx:=turnoutx;  pegy:=aq25offset(pegx,pegangle);  RESULT:='N'; end;  // TOLP peg on turnout road opposite end of main road (for parallel crossings). // 0.79.a
           610: begin
                      if plain_track=True
                         then pegx:=xorg-pt_blank    // 234e
                         else pegx:=startx;

                                       pegy:=g/2;      pegangle:=0;      RESULT:='3'; end;  // blanking length  // 0.82.a    234e   was N

           615: begin pegx:=ipx;       pegy:=g/2;      pegangle:=0;      RESULT:='N'; end;  // IP Intermediate 208a

           620: begin                            // 226a    OSMP
                  try
                    if (tradius_is_straight=True)  // ??? return TCP instead
                       then begin
                              pegx:=tcpx;
                              pegy:=tcpy;
                              pegangle:=k3;
                            end
                       else begin                                  // peg on OSMP (outside-slip switch mirroring position)...
                              osk:=ARCTAN(1/osk_match)/2;
                              pegx:=torgx+(tradius-g/2)*SIN(osk);
                              pegy:=torgy-(tradius-g/2)*COS(osk);
                              pegangle:=osk;
                            end;
                    RESULT:='N';
                  except               // trig errors?
                    pegx:=0;
                    pegy:=g/2;
                    pegangle:=0;
                    RESULT:='0';
                  end;//try

                end;

           625: begin                            // 226a     OSXP
                  try
                    if (tradius_is_straight=True)  // ??? return MCP instead
                       then pegx:=mcpx
                       else begin                                  // peg on OSXP (outside-slip switch intersection position)...
                              osk:=ARCTAN(1/osk_match)/2;
                              pegx:=torgx+(torgy-g/2)*TAN(osk);
                            end;

                    pegy:=g/2;
                    pegangle:=0;
                    RESULT:='N';
                  except               // trig errors?
                    pegx:=0;
                    pegy:=g/2;
                    pegangle:=0;
                    RESULT:='0';
                  end;//try
                end;

           630: begin pegx:=thdpx; pegy:=aq25offset(pegx,pegangle);  RESULT:='N'; end;  // 226c half-diamond exit

           700: begin                     // 223a dummy vehicle pin
                  pegx:=cdvi.dv_start*inscale;
                  pegy:=g/2;

                  if grid_form.turnout_road_dummy_vehicle_radio_button.Checked=True
                     then begin
                            tr_y:=aq3offset(pegx,tr_k);  // get start position on stock rail

                            xtr:=pegx+g/2*SIN(tr_k);     // start position on centre line
                            ytr:=tr_y-g/2*COS(tr_k);

                            pegx:=xtr;   // reset for turnout road
                            pegy:=ytr;
                          end;

                  pegangle:=0;
                  RESULT:='N';
                end;

           801: begin pegx:=toex;  pegy:=0;  pegangle:=0;  RESULT:='N'; end;      // KP1   227a

           802: begin pegx:=toex;  pegy:=0;  pegangle:=k1;  RESULT:='N'; end;     // KP2   227a

           803: begin pegx:=setx;  pegy:=g;  pegangle:=0;  RESULT:='N'; end;    // KP3   227a

           804: begin pegx:=setx;  pegy:=g;  pegangle:=k1;  RESULT:='N'; end;   // KP3   227a


           999: begin                    // user-defined peg location and angle.
                  if udpeg_valid=True
                     then begin
                            pegx:=udpegx;
                            pegy:=udpegy;
                            pegangle:=udpegangle;
                          end
                     else begin
                            pegx:=0;
                            pegy:=g/2;
                            pegangle:=0;
                          end;
                  RESULT:='U';
                end;
           else begin pegx:=0;        pegy:=g/2;  pegangle:=0;  RESULT:='F'; end; // ??? peg-code wrong.

           // !!! this following is in all versions prior to 0.71.a and will cause a crash
           // if a later file is loaded with an upgrade peg_code.
           //else run_error(248);  // peg_code wrong?

  end;//case

end;
//_____________________________________________________________________________________________

procedure turnout_dims;       // calc and display all the turnout dimensions on the pad.
                              // called only from gocalc.

var
  temp_k, dummy:extended;
  geox,geoy,geok:extended;
  tvjy,tvjk:extended;

    // 217b ..

  curving_rad,mod_pegangle,shrink_factor,tanx:extended;

begin
  reset_defaults;                   //  set up any defaults and distortions.

  if calc_curving=False             //  calc curving radii and transition constants, etc
     then abandon_calcs:=True;

  if abandon_calcs=False            //  curving calcs error-free ?
     then begin
            init_resize;                //  set up starting switch size for re-sizing upwards.
            done_calcs:=calcturnout;    //  calc all turnout dims.
          end;

  if abandon_calcs=False            //  turnout calcs error-free ?
     then begin
                          // first get geometrical rads...

            if (spiral=False) and (slewing=False) and (plain_track=False)
               then begin

                      tvjy:=aq25offset(tvjpx,tvjk);   // peg calcs for TVJP (Ctrl-6).

                      docurving(False,False,tvjpx,tvjy,geox,geoy,geok,dummy);
                      if calc_geo_radius(nomrad,geox,geoy-g/2,geok+tvjk, egeo_rad,egeo_k,egeo_swing,egpx)=False
                         then begin
                                egeo_rad:=max_rad;  // don't leave invalid data.
                                egeo_k:=0;
                                egeo_swing:=0;
                                egpx:=0;
                              end;

                      docurving(False,False,tcpx,tcpy,geox,geoy,geok,dummy);
                      if calc_geo_radius(nomrad,geox,geoy-g/2,geok+k3, igeo_rad,igeo_k,igeo_swing,igpx)=False
                         then begin
                                igeo_rad:=max_rad;  // don't leave invalid data.
                                igeo_k:=0;
                                igeo_swing:=0;
                                igpx:=0;
                              end;
                    end
               else begin                 // no geo rads if transition or slewing...
                      egeo_rad:=max_rad;
                      egeo_k:=0;
                      egeo_swing:=0;
                      egpx:=0;

                      igeo_rad:=max_rad;  // don't leave invalid data.
                      igeo_k:=0;
                      igeo_swing:=0;
                      igpx:=0;
                    end;
                               // radial centres...
            egeo_orgx:=egpx;
            egeo_orgy:=egeo_rad+g/2;    // adjust rad centres for the datum on the rail.

            igeo_orgx:=igpx;
            igeo_orgy:=igeo_rad+g/2;

                             // now the peg position calcs...

            pad_form.peg_indicator_panel.Caption:=calc_peg_dims(peg_code,pegx,pegy,pegangle);

            normalize_angle(pegangle);

            docurving(True,True,pegx,pegy,pegx_on_pad,pegy_on_pad,temp_k,curving_rad);  // curve and transform peg to get position (for info) and curving angle.

              // 217b  gradient correction for radial shrink/enlarge ...

            //try

            if ABS(pegangle)<minfp
               then mod_pegangle:=pegangle
               else begin
                      try
                        shrink_factor:=(curving_rad-(pegy-g/2))/curving_rad;
                        tanx:=1/TAN(pegangle);
                        tanx:=tanx*shrink_factor;
                        mod_pegangle:=ARCTAN(1/tanx);
                      except
                        mod_pegangle:=pegangle;
                      end;//try
                    end;

            arm_angle:=mod_pegangle+temp_k+kform;     // arm angle (actual on pad including curving and transforms).

            normalize_angle(arm_angle);

            docurving(True,True,0,0,datumx_on_pad,datumy_on_pad,temp_k,dummy);    // position of rail-end datum on pad (for info).

            //try

            compile_template_data;  // finally compile text of template info, and show it.

                    // and the pad size data...

            with info_form do begin
              pad_width_label.Caption:='pad  width   =  '+round_str(screenx,2)+' mm  ( '+round_str(screenx/25.4,2)+' " )';
              pad_height_label.Caption:='pad  height  =  '+round_str(screeny,2)+' mm  ( '+round_str(screeny/25.4,2)+' " )';

              notchx_label.Caption:='notch  centre  X  =  '+round_str(notchx,2)+' mm';
              notchy_label.Caption:='notch  centre  Y  =  '+round_str(notchy,2)+' mm';
              notchk_label.Caption:='notch  angle  =  '+round_str(notch_angle*180/Pi,2)+' degs'+k_ram_str(notch_angle);

              offsetx_label.Caption:='offset  X  =  '+round_str(zoom_offsetx,2)+' mm  ( '+round_str(zoom_offsetx/25.4,2)+' " )';
              offsety_label.Caption:='offset  Y  =  '+round_str(zoom_offsety,2)+' mm  ( '+round_str(zoom_offsety/25.4,2)+' " )';
            end;//with

          end;
end;
//______________________________________________________________________________________

procedure screen_calcs;   //  do the screen zoom calcs. enter with screenx.

begin
  if screenx<minfp then run_error(2); //  calcs not done - division by zero, or negative scale.

  xmax:=pad_form.ClientWidth-1;         //  max x value inside borders (screen is 0..1023).
  ymax:=pad_form.ClientHeight-1;        //  ditto y.

  if xmax<20 then xmax:=20;      // 216c sensible safety limits (div 0).             was 10
  if ymax<10 then ymax:=10;

  ex:=17;   // left screen margin position = top of sheet. ( 1/60 = 17 pixels for 1024 screen)

  fx:=(xmax-ex)/screenx;         // x scaling factor for screen in pixels per mm.
  sx:=fx/100;                    // x scaling factor for screen in pixels per 1/100 mm. (rail-data is in 1/100 mm.)

  fy:=0-fx;
  sy:=0-sx;                      // y is same but negative - see notes above.

  try
    ffx:=1/fx;     // x mm per screen pixel.
    ffy:=1/fy;     // y ditto.
  except
    ffx:=1;        // ?? scale zero?
    ffy:=1;
  end;//try

  by:=ymax+pad_form.Font.Height-4;  // leave room at bottom for labels (font height is negative).

  screeny:=by/ABS(fy);           //  screen y dim in mm.

  gx:=zoom_offsetx*fx;           //  pad offset pixels on x
  gy:=zoom_offsety*fy;           //  ditto y. (negative).

  try
    bunch_jump_i:=Round(bunching_jump*fx);    // for paper bunching.
    bunch_shear_i:=Round(bunching_shear*fy);    // for paper bunching.
  except
    bunch_jump_i:=Round(50*scale);            // default 50ft scale.
    bunch_shear_i:=0;
  end;//try
end;
//________________________________________________________________________________________

procedure gocalc(calcs_code, mode:integer);    //  a new turnout wanted - let's do some calcs.

      // calcs_code 0 = does the turnout calcs only, no list generation or drawing.
      //            1 = also generates a list but no drawing (for storage box, etc, - set the scaling factors first).
      //            2 = also does a redraw.

      // mode  0 = normal fixed off-screen redraw.

      //  modes for mouse actions...

           // on-screen modes..

      //       1 = zoom fixed. 2 or fewer background templates
      //       2 = ditto, first click.

      //       3 = zoom fixed. 3 or more background keeps.
      //       4 = ditto, first click.

      //       5 = zoom or position changing.
      //       6 = ditto, first click.

           // off-screen modes..

      //       7 = zoom fixed.
      //       8 = ditto, first click.

      //       9 = zoom or position changing.
      //      10 = ditto, first click.


var
  hourglass:boolean;
  current_canvas:TCanvas;
  in_mode:integer;
  mps:TPoint;

begin

  data_changed:=False;                   // don't come here again via endless loop until a flagchange.

  if gocalc_lock=True then EXIT;    // no calcs while locked.

  if (mode<0) or (mode>10) then run_error(19);

  case mode of
      1..6: current_canvas:=pad_form.Canvas;
       else current_canvas:=offdraw_bmp.Canvas;
  end;//case

  if (draw_mode<>0) and (mouse_modify<0) and (Screen.Cursor=crDefault)
     then begin
            hourglass:=True;
          end
     else hourglass:=False;

  try

    if in_progress=False
       then begin
              in_progress:=True;                //  prevent any re-entry here.

              abandon_calcs:=False;             //  init fresh start or re-calc.

              calcs_done_and_valid:=False;      //  flag calcs not yet valid (for add keep).

              turnout_dims;                     //  do all the turnout calcs for the pad.

              if abandon_calcs=False            //  turnout calcs error-free ?
                 then begin
                        if calcs_code=0 then EXIT;  // only the turnout calcs were wanted.

                        if calcs_code=2    // no screen calcs if calcs are for the keeps box - just calc the lists using the keeps box scaling factors.
                           then begin
                                  if draw_mode<2 then screenx:=ABS(turnoutx+scale*4);   //  add some right margin space (4ft scale).
                                                                                        //  (can't use xy_max because list not yet filled.)
                                  if screenx<screenx_min then screenx:=screenx_min;     // minimum for screen width (max zoom in).
                                  if screenx>screenx_max then screenx:=screenx_max;     // maximum zoom out.

                                  screen_calcs;    // then calc the screen factors from screenx.

                                  if draw_mode=0 then set_y_datum; // set default y_datum.

                                        // save the screen factors, might be need in the keeps box (e.g. highlight background).

                                  save_sx:=sx;
                                  save_sy:=sy;
                                  save_ex:=ex;
                                  save_by:=by;
                                  save_gx:=gx;
                                  save_gy:=gy;

                                  if (mode=1) or (mode=2)
                                     then begin
                                            pad_marks_current(pad_form.Canvas,False);    // erase any existing marks and timbers (ignore return).
                                            draw_rail_endmarks(pad_form.Canvas,False);   // and the rail ends.
                                          end;

                                  do_background(mode);    // then draw the grid and add the scale bar.
                                end;

                            // calcs_code 1 or 2 ..

                        done_rails:=new_calc_draw(current_canvas,calcs_code,mode);   // then calc rail lines and marks, erase previous lines and draw new.
                      end;

              if (done_calcs=True) and (done_rails=True) and (abandon_calcs=False)   //  all calcs ok ?
                 then begin
                        calcs_done_and_valid:=True;     // ok for output to printer or keeps box.

                        if (calcs_code=2) and ( (mode=0) or (mode>6) )   // no screen change if calcs are for the keeps box.
                           then copy_draw_to_pad;  // copy the draw bitmap to the pad.              do_background(0);   //  draw the complete background.

                        EXIT;            // all done.
                      end
                 else begin
                        calcs_done_and_valid:=False;    //  can't print or keep this.
                        done_calcs:=False;
                        done_rails:=False;
                        abandon_calcs:=True;
                        if Application.Terminated=False then Application.ProcessMessages;     // clear any pending repaints.
                        redraw(True);                    // onIdle handles error options and recovery.
                      end;

            end;//if not in progress
finally
  if hourglass=True
     then begin
            Screen.Cursor:=crDefault;
            case mouse_modify of
                          -1: pad_form.Cursor:=crDefault;
                           0: pad_form.Cursor:=mouse_action_cursor;
            end;//case

            if GetCursorPos(mps)=False then begin mps.X:=0; mps.Y:=0; end;
            SetCursorPos(mps.X, mps.Y);    // need a mouse move event to set the pad cursor after a screen cursor change.

          end;

  in_progress:=False;                                 // allow re-entry
end;//try


end;
//______________________________________________________________________________

procedure railedges(gf,oe,cl:boolean);    // switch rail edges on/off as requested.

var i:integer;

begin
  for i:=0 to aq_max_c do aqyn[i]:=False;      // switch them all off,
                                               // and then some back on...

  if (cl_only=False) and (rail_section<>0)     // rails wanted?
     then begin

            if pad_form.stock_rails_menu_entry.Checked=True  // generator switches...
               then begin
                      if main_road_stock_rail_flag=True      // rail switches per template.
                         then begin
                                aqyn[0]:=gf;    // straight stock rail.
                                aqyn[8]:=oe;
                              end;

                      if turnout_road_stock_rail_flag=True
                         then begin
                                aqyn[3]:=gf;    // curved stock rail.
                                aqyn[11]:=oe;
                              end;
                    end;

            if pad_form.crossing_rails_menu_entry.Checked=True
               then begin
                      if main_road_crossing_rail_flag=True
                         then begin
                                aqyn[1]:=gf;    // straight turnout/wing rail.
                                aqyn[9]:=oe;
                              end;

                      if turnout_road_crossing_rail_flag=True
                         then begin
                                aqyn[2]:=gf;    // curved turnout/wing rail.
                                aqyn[10]:=oe;
                              end;

                      if crossing_vee_flag=True
                         then begin
                                aqyn[4]:=gf;    // straight vee rail.
                                aqyn[12]:=oe;

                                aqyn[5]:=gf;    // curved vee rail.
                                aqyn[13]:=oe;
                              end;
                    end;

            if pad_form.check_rails_menu_entry.Checked=True
               then begin
                      if main_road_check_rail_flag=True
                         then begin
                                aqyn[6]:=gf;    // straight check rail.
                                aqyn[14]:=oe;

                                if (half_diamond=True) and (fixed_diamond=True)
                                   then begin
                                          aqyn[28]:=gf;    // h-d DS check rail. (DS check is in main road.)
                                          aqyn[29]:=oe;
                                        end;
                              end;

                      if turnout_road_check_rail_flag=True
                         then begin
                                aqyn[7]:=gf;    // curved check rail.
                                aqyn[15]:=oe;

                                if (half_diamond=True) and (fixed_diamond=True)
                                   then begin
                                          aqyn[26]:=gf;    // h-d MS check rail. (MS check is in diamond road.)
                                          aqyn[27]:=oe;
                                        end;
                              end;
                    end;
          end;

  if gen_platforms=True     // generator switch
     then begin

            if adjacent_edges=False    // 0.93.a   False=adjacent tracks,  True=trackbed edges and platform edges.
               then begin
                      if (cl_only=False) and (rail_section<>0)
                         then begin
                                               // any adjacent tracks...
                                aqyn[16]:=gf;
                                aqyn[17]:=oe;

                                aqyn[18]:=gf;
                                aqyn[19]:=oe;

                                aqyn[20]:=gf;
                                aqyn[21]:=oe;

                                aqyn[22]:=gf;
                                aqyn[23]:=oe;

                              end;
                    end

               else begin     // 0.93.a platforms and trackbed edges...

                      aqyn[16]:=draw_ts_platform;
                      aqyn[17]:=draw_ts_platform;

                      aqyn[18]:=draw_ts_trackbed_edge;
                      aqyn[19]:=draw_ts_trackbed_edge;

                      aqyn[20]:=draw_ms_platform;
                      aqyn[21]:=draw_ms_platform;

                      aqyn[22]:=draw_ms_trackbed_edge;
                      aqyn[23]:=draw_ms_trackbed_edge;

                    end;
          end;//generator switch

  if track_centre_lines_flag=True
     then begin
            aqyn[24]:=cl;          // track centre-lines.

            if plain_track=False then aqyn[25]:=cl;    // turnout side centre-line.
          end;
end;
//________________________________________________________________________________________

procedure do_railedges;      // called from rail_options_unit and platform_unit and trackbed_unit and control_room

begin
  railedges(gauge_faces,outer_edges,centre_lines);
end;
//_________________________________________________________________________________________

procedure swap_hand;

var
  dummy1,dummy2:extended;
  pc,pf:Tpex;

begin
  arm_angle_now:=arm_angle;                                          // save current peg arm angle on pad.
  docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,dummy1,dummy2);  // and current peg position.

  hand_i:=0-hand_i;     // swap hand.
                        // then calc new peg position on pad...

  docurving(False,True,pegx,pegy,pc.x,pc.y,dummy1,dummy2);  // first curve it.

  dotransform(kform,xform,yform,pc,pf);      // then rotate it (without shifts).

  xshift:=now_peg_x - pf.x;      // maintain peg position constant (now_peg includes the now_shift).
  yshift:=(0-now_peg_y) - pf.y;  // must swap hand of now_peg_y.

  gocalc(0,0);                   // recalc to update arm_angle.

  rotate_turnout(0-arm_angle-arm_angle_now,True);    // re-align to previous angle (opposite hand) and re-draw.
end;
//__________________________________________________________________________________________

procedure select_switch;

var
  old_n, new_n:integer;
  sw_info:Tswitch_info;
  dummy:extended;

begin
  if half_diamond=True
     then begin
            if alert(3,'    switch  settings   -   half - diamond',
                       'The control template is a half-diamond.'
                      +'||Normally any changes which you make to the switch settings will have no effect until you change to a turnout template'
                      +' by clicking the|`0TEMPLATE > CONVERT HALF-DIAMOND TO TURNOUT`1 menu item.'
                      +'|||But you can if you wish use the switch settings for the timber-spacing and rail joint marks on a movable K-crossing.'
                      +' This might apply if you are using a half-diamond template to improvise a split deflection switch, for example to create a symmetrical Y-turnout.'
                      +' For this to work, you need to click the|`0real > timbering > half-diamond timbering > movable K-crossing: timber-spacing as turnout switch`z menu option.'
                      +' Remember to de-select this option when you return to creating normal half-diamond templates for diamond-crossings and slips.'
                      +' Bear in mind that this option applies only to the timber spacings and rail joints, the switch geometry is ignored for a K-crossing.',
                       '','','','','cancel','continue  -  set  switch ...',0)=5 then EXIT;
          end;

  if plain_track=True
     then begin
            if alert(3,'    plain  track',
                       'The control template is plain track.'
                      +'||Any changes which you make to the switch settings will have no effect until you change to a turnout template'
                      +' by clicking the|TEMPLATE > INSERT TURNOUT IN PLAIN TRACK|menu item.',
                       '','','','','cancel','continue  -  set  switch ...',0)=5 then EXIT;
          end;


  kform_now:=kform;
  docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy);    // save current peg data for peg_curve calcs.

  old_n:=get_switch_list_index(csi.group_code,csi.size_code);  // search for current list index.
  if old_n<0 then old_n:=0;                                    //??? not in list  ???.

  new_n:=get_switch(old_n);    // show selector form and get new index.

  sw_info:=Tswitch(switch_select_form.switch_selector_listbox.Items.Objects[new_n]).list_switch_info;

  if set_csi_from_switch_info(sw_info)=False
     then begin                           // ??? data invalid.
            if set_csi_data(2,2)=False    // set REA B default if copied data invalid.
               then run_error(82);        // ?????? no B switch in list?
          end;

  csi.joggled_stock_rail:=switch_select_form.joggled_checkbox.Checked;  // he might have changed it from the default.

  gocalc(0,0);     // calc new pegx.

  peg_curve;            // adjust shifts and rotates for current peg position.
  redraw_pad(True,True);
end;
//__________________________________________________________________________________________

function check_radius(warn:boolean; var r:extended):boolean;    // return true if curving radius was within limits.
                                                                // otherwise force it there and return false.
begin
  RESULT:=True;         // init return.

  if Abs(r)>max_rad_limit then begin
                                if r>=0 then r:=max_rad_limit
                                        else r:=0-max_rad_limit;
                                RESULT:=False;
                                if warn=True then alert(6,'    invalid  radius','||The maximum valid radius is '+round_str(max_rad_limit,0)+' mm.'
                                                         +'||( That is a radius of one hundred kilometres'
                                                         +' - more than sixty miles - which is effectively DEAD STRAIGHT in this context.)',
                                                          '','','','','','O K  -  continue',0);
                            EXIT;
                          end;

  if Abs(r)<(g*2) then begin
                         if r>=0 then r:=g*2
                                 else r:=0-g*2;
                         RESULT:=False;
                         if warn=True then alert(6,'    invalid  radius','||The minimum valid radius for the track centre-line is double the current track gauge.'
                                                  +'||i.e. current minimum valid radius = '+round_str(g*2,2)+' mm.',
                                                   '','','','','','O K  -  continue',0);
                         EXIT;
                       end;
end;
//_____________________________________________________________________________________

procedure xingoffset(aq:integer; gauge_mod:extended);      // return ys and ks for turnout curve/crossing

    // used for timbering, peg, rail-joints, debug, etc.

    // !!! switch joggle (if any) is ignored.
    // !!! enter with aq=2 or aq=3.
    // !!! enter with xe=end of turnout curve.

    // N.B. aq 2 or 3 only are valid here.

begin
    ys:=0;      // default init.
    ks:=0;
    if (aq<>2) and (aq<>3) then EXIT;    // !!!

    if (xs<xe) or (xing_calc_i=1)
       then ys:=radcurve(aq,{0,0,}xs{,1})     // turnout rad part or curviform V-crossing runs on through.
       else begin
            if (xs>retx {csrendx}) and (retpar_i=1) and (xs>fpx)
               then begin
                    retrmod:=gauge_mod-g/2;               // retr rad is to centre-line.
                    ys:=returncurve(xs,retrmod);          // return curve part
                    end
               else begin
                      if xing_calc_i=-1        // generic crossing  - bug fix 27-5-01.
                         then begin
                                case aq of
                                  2: ys:=g+(xs-fpx)/k3n;                // calc along straight crossing at this x.
                                  3: ys:=g+g*COS(k3)+(xs-csrfpx)/k3n;
                                end;//case
                              end
                         else begin      // regular crossing
                                xb:=xe;                            // end of turnout curve = start of
                                ys:=turnoutst_main(aq,xb,xe,xs);   // straight crossing part.
                              end;
                      ks:=k3;               // gradient angle constant.
                    end;
            end;
end;
//______________________________________________________________________________________

function aq2offset(xtb:extended; var k:extended):extended;   //  general utility - return offset at any xtb
                                                             //  along curved turnout rail (gauge-face, aq=2). Enter with xtb.
                                                             //  also returns gradient angle in k.

    // The only rail edges which use these routines are
    // wing rails and check rails.
    // also used for timbering, peg, rail-joints, debug, chairs, etc.
    // !!! switch joggle (if any) is ignored.

var
  xxs,yys,kks,xxb,xxe:extended;
  segment_index:integer;

begin
                // save these globals for caller...
  xxs:=xs;
  yys:=ys;
  kks:=ks;
  xxb:=xb;
  xxe:=xe;

  xs:=xtb;

  if half_diamond=True  // mods 22-09-04 ...
     then begin
            xb:=seg_termx[3,2];   // segment_index=3, h-d all on turnout curve.
            xe:=seg_termx[4,2];
            xingoffset(2,0);      // get ys,ks for aq=2.
          end
     else begin
            if xtb<toex          // 227a  switch front, approach track
               then begin
                      k:=0;
                      RESULT:=0;
                      EXIT;
                    end;


            segment_index:=1;   // start search for current rail segment at toex.

            repeat
              xb:=seg_termx[segment_index,2];                    // start x for this segment.
              xe:=seg_termx[segment_index+1,2];                  // end x for this segment
              if (xs<xe) or (segment_index=3) then BREAK;
              INC(segment_index);
            until 0<>0;

            case segment_index of
                   //0: toe(2);
                     1: case switch_type of
                            -1: straight_planing(2,False);    // semi-curved switch.
                             0: swcurve(2,False);             // for straight switch or curved switch, the planing is part of the switch curve.
                             1: double_curved_planing(2);     // double-curved switch planing.
                           else run_error(68);
                        end;//case
                     2: swcurve(2,False);
                     3: xingoffset(2,0);      // aq=2.
                   else run_error(82);
            end;//case
          end;

  RESULT:=ys;
  k:=ks;
                // restore these for caller...
  xs:=xxs;
  ys:=yys;
  ks:=kks;
  xb:=xxb;
  xe:=xxe;
end;
//_______________________________________________________________________________________________________________________________

function aq3offset(xtb:extended; var k:extended):extended;   //  general utility - return offset at any xtb
                                                             //  along curved stock rail (gauge-face, aq=3). Enter with xtb.
                                                             //  also returns gradient angle in k.

    // The only rail edges which use these routines are
    // wing rails and check rails.
    // also used for timbering, peg, rail-joints, debug, etc.

    // !!! switch joggle (if any) is ignored.
    // !!! switch joggle (if any) is ignored.

var
  xxs,yys,kks,xxb,xxe:extended;
  segment_index:integer;

begin
                 // save these globals for caller...
  xxs:=xs;
  yys:=ys;
  kks:=ks;
  xxb:=xb;
  xxe:=xe;

  xs:=xtb;

  if half_diamond=True  // mods 22-09-04...
     then begin
            xb:=seg_termx[3,3];   // segment_index=3, h-d all on turnout curve.
            xe:=seg_termx[4,3];
            xingoffset(3,g);  // get ys,ks for aq=3.
          end
     else begin

            segment_index:=0;     // turnout - start search for current rail segment.

            repeat
              xb:=seg_termx[segment_index,3];               // start x for this segment.
              xe:=seg_termx[segment_index+1,3];             // end x for this segment.
              if (xs<xe) or (segment_index=3) then BREAK;
              INC(segment_index);
            until 0<>0;

            case segment_index of
                     0: toe(3);
                     1: case switch_type of
                             -1: straight_planing(3,False);      // semi-curved switch.
                              0: swcurve(3,False);               // for straight switch or curved switch, the planing is part of the switch curve.
                              1: double_curved_planing(3);       // double-curved switch planing.
                           else run_error(68);
                        end;//case
                     2: swcurve(3,False);
                     3: xingoffset(3,g);     // aq=3
                   else run_error(83);
            end;//case
          end;

  RESULT:=ys;
  k:=ks;

                // restore these for caller...
  xs:=xxs;
  ys:=yys;
  ks:=kks;
  xb:=xxb;
  xe:=xxe;
end;
//_______________________________________________________________________________________________________________________________

procedure get_cpi;         // copy current prototype info from current list.

                          // scale not changed - only done via gauge_dims.
begin
  with cpi do begin
    g:=             gauge_pi;        // mm.
    fw:=            fw_pi;           // mm flangeway.
    fw_end:=        fwe_pi;          // mm flangeway end (flangeway+flare).
    xing_flare_len:=xing_fl_pi;      // mm length of flares (not h-d).
    railtop:=       railtop_pi;      // mm width of rail head.
    railbottom:=    railbottom_pi;   // mm width of rail foot for flatbottom rail.
    trtscent:=      trtscent_pi;     // mm track centres, turnout side.
    trmscent:=      trmscent_pi;     // mm ditto, main side.
    retcent:=       retcent_pi;      // mm ditto, return curve.
    min_radius:=    min_radius_pi;   // mm minimum radius for check (30" approx).

    rail_height:=rail_height_pi;   // full-size inches - height of rail.

    rail_inclination:=rail_inclination_pi;    // radians (1:20).

                 // wing and check rails...

    wing_reach1:=    wing_ms_reach1_pi;    // full-size inches - size 1 wing rail reach length (forward from "A").
    wing_reach2:=    wing_ms_reach2_pi;    // full-size inches - size 2 wing rail reach length (forward from "A").

    // 0.94.a no longer used, mouse diffs instead.  wing_ts_reach_mod:= wing_ts_reach_mod_pi; // full-size inches - TS wing rail reach length modifier.


    ck_working1:=    ck_ms_working1_pi;    // full-size inches - size 1 check rail working length (back from "A").
    ck_working2:=    ck_ms_working2_pi;    // full-size inches - size 2 check rail working length (back from "A").
    ck_working3:=    ck_ms_working3_pi;    // full-size inches - size 3 check rail working length (back from "A").

    // 0.94.a no longer used, mouse diffs instead.  ck_ts_working_mod:= ck_ts_working_mod_pi; // full-size inches - TS check rail working length modifier.

    ck_ext1:=        ck_ms_ext1_pi;        // full-size inches - size 1 check rail extension length (forward from "A").
    ck_ext2:=        ck_ms_ext2_pi;        // full-size inches - size 2 check rail extension length (forward from "A").

    // 0.94.a no longer used, mouse diffs instead.  ck_ts_ext_mod:=     ck_ts_ext_mod_pi;     // full-size inches - TS check rail extension length modifier.

    tbwide:=tbwide_pi;       // inches full-size width of turnout timbers.
    slwide:=slwide_pi;       // inches full-size width of plain sleepers.
    jt_slwide:=jt_slwide_pi; // joint sleepers 212a

    tb_end_offset:=tb_end_offset_pi;  // 232a inches full-size

    ftimbspmax:=ftimbspmax_pi;   // inches full-size max timber-spacing for closure space.

    tb:=tb_pi;      // plain sleepers length.

    if Abs(tb-108*inscale)<minfp
     then begin
            nine_foot:=True;
            eight_foot_six:=False;
            pad_form.sleepers_9ft_menu_entry.Checked:=True;    // radio item.
          end
     else begin
            nine_foot:=False;                        // non 9ft-standard length.
            if Abs(tb-102*inscale)<minfp
               then begin
                      pad_form.sleepers_8ft6in_menu_entry.Checked:=True;        // radio item.
                      eight_foot_six:=True;
                    end
               else begin
                      pad_form.sleepers_otherlength_menu_entry.Checked:=True;   // radio item.
                      eight_foot_six:=False;
                    end;
          end;
                                  // mods 11-5-01 v:0.71.a...

    ms_ends:=mainside_ends_pi;    //  True=main side ends in line, False=ends centralized.

    randend:=random_end_pi;       //  amount of timber-end randomising.
    randangle:=random_angle_pi;   //  amount of timber_angle randomising.

    if (randend<>0) or (randangle<>0) then random_flag:=True
                                      else random_flag:=False;

  end;//with
end;
//___________________________________________________________________________________________

function gauge_dims(from_list,mod_rads,mod_gauge_data:boolean):extended;    // set up gauge and scale from selected item in list.

            // return ratio between old and new scales.

var
  old_cg:Tproto_info;
  mod_scale_ratio,mod_gauge_ratio:extended;
  i,n,sz:integer;

  m:integer;  // 238a

begin

  RESULT:=1.0;     // default init.

  old_cg:=cpi;

  if from_list=True         // copy to current from the gauge list
     then begin
            with gauge[gauge_i] do begin
              with cpi do begin

                name_str_pi:=name_str_glist;

                scale_pi:=scale_glist;            // mm/ft.
                gauge_pi:=gauge_glist;            // mm.
                fw_pi:=fw_glist;                  // mm flangeway.
                fwe_pi:=fwe_glist;                // mm flangeway end (flangeway+flare).

                trtscent_pi:=trtscent_glist;      // mm track centres, turnout side.
                trmscent_pi:=trmscent_glist;      // mm ditto, main side.
                min_radius_pi:=min_radius_glist;  // mm minimum radius for check.

                    // and use defaults for these when changing gauge via the list...

                    // new check and wing dimensioning : v:0.71.a 25-5-01...

                ck_ms_working1_pi:=def_req;  // full-size 78 inches - size 1 MS check rail working length (back from "A").   228a was 78"   revert 228b
                ck_ms_working2_pi:=def_req;  // full-size 108 inches - size 2 MS check rail working length (back from "A").
                ck_ms_working3_pi:=def_req;  // full-size 138 inches - size 3 MS check rail working length (back from "A").

                // 0.94.a no longer used, mouse diffs instead.  ck_ts_working_mod_pi:=def_req;   // full-size 0 inches - TS check rail working length modifier.

                ck_ms_ext1_pi:=def_req;   // full-size 48 inches - size 1 MS check rail extension length (forward from "A").
                ck_ms_ext2_pi:=def_req;   // full-size 78 inches - size 2 MS check rail extension length (forward from "A").

                // 0.94.a no longer used, mouse diffs instead.  ck_ts_ext_mod_pi:=def_req;   // full-size 0 inches - TS check rail extension length modifier.

                wing_ms_reach1_pi:=def_req;    // short wing rails 48" default from centre of "A" timber.
                wing_ms_reach2_pi:=def_req;    // long wing rails 78" default from centre of "A" timber.

                // 0.94.a no longer used, mouse diffs instead.  wing_ts_reach_mod_pi:=def_req; // full-size 0 inches - TS wing rail reach length modifier.

                xing_fl_pi:=def_req;     // default 3'0" flare on wing/check rails (not h-d).
                railtop_pi:=def_req;     // default rail head width, scale 2.75".
                railbottom_pi:=def_req;  // default flatbottom base, scale 5.5" (FB-109, BS-110A, BS-113A). 11-5-01.
                rail_height_pi:=def_req; // default railheight.

                rail_seat_height_pi:=def_req;   // 235a

                rail_inclination_pi:=def_req;    // radians (1:20).

                retcent_pi:=def_req;     // default return curve track centres 11'2"

                tbwide_pi:=def_req;      // 12" wide turnout timbers.
                slwide_pi:=def_req;      // 10" wide plain sleepers.

                jt_slwide_pi:=def_req_single;  // 10" wide joint sleepers.  212a

                tb_end_offset_pi:=def_req;    // 232a inches full-size

                xtimbsp_pi:=def_req;     // !!! disused in 0.75.a 14-10-01. 30" timber spacing at crossing.
                ftimbspmax_pi:=def_req;  // 30" ditto for closure space.

                if (nine_foot=False)
                   and (eight_foot_six=False)

                   and (gauge_i<>t_TT3_i)
                   and (gauge_i<>t_TTI_i)
                   and (gauge_i<>t_TTF_i)

                   and (gauge_i<>t_TMS_i)
                   and (gauge_i<>t_TMF_i)

                   and (gauge_i<>t_00XF_i)    // 239a
                   and (gauge_i<>t_00SF_i)
                   and (gauge_i<>t_00MF_i)
                   and (gauge_i<>t_003D_i)    // 555a
                   and (gauge_i<>t_00IF_i)    // 234a
                   and (gauge_i<>t_00BF_i)
                   and (gauge_i<>t_00H0_i)
                   and (gauge_i<>t_00DGF_i)
                   and (gauge_i<>t_00DGI_i)
                   and (gauge_i<>t_00BRMSB_i)

                                               // changing from an odd size to one which is not TT or 00...
                   then begin
                          i:=alert(4,'    non-standard  timber  length ?',
                                     'The existing nominal timber length is '+round_str(tb/inscale,2)+' inches, which is non-standard (not 9ft or 8ft 6in, i.e. 108 or 102 inches).'
                                    +'||Do you want to use this timber length for the new '+Trim(name_str_glist)+' gauge/scale setting?',
                                     '','','yes  -  use  this  length','no  -  change  to  9ft  nominal  timber length','','no  -  change  to  8ft 6ins  nominal  timber length',0);
                          case i of
                                 4: begin eight_foot_six:=False; nine_foot:=True; end;
                                 6: begin eight_foot_six:=True; nine_foot:=False; end;
                          end;//case
                        end;

                tb_pi:=def_req;          // default timbering.
                old_tb_pi:=tb/inscale;   // full-size inches - used internally for gauge changes (no meaning in file).

                              // mods 11-5-01 v:0.71.a...

                random_end_pi:=def_req;             // no timber-end randomising.
                random_angle_pi:=def_req;           // no timber_angle randomising.

                mainside_ends_pi:=pad_form.timbers_in_line_menu_entry.Checked;   //  timber ends no change.


                s1j_on_wing_pi:=True; // S1J joint chairs at wing rail front joint

                s1j_on_j1_pi:=False;  // normal S1 chair on timber J1

                slab_bracket_pi:=False;   // 239a  normal cast A chair

                ccl_ccr_variable_pi:=False;  // 243a  CCL CCR check end chairs fixed size.

                nose_clamp_pi:=True;    // 241a

                xing_filler_pi:=True;   // 241a  3D xing gap spacer

                //ms_parallel_wing_pi:=False;  // 239a

                temp_dd_parallel_wing_pi:=True;  // 244a to replace EF

                temp_wrong_k_xing_pi:=False;   // 244a temporary for customizing

                keys_on_outer_jaws_pi:=True;

                rail_seats_pi:=True;

                ms_snibs_pi:=True;   // 244a    MS snibs  2-D DXF kerf
                ts_snibs_pi:=True;   // 244a    TS snibs  2-D DXF kerf

              end;//with cpi

            end;//with
          end;

  if ABS(cpi.scale_pi-scale)>minfp then print_timber_numbers_font.Assign(set_font('Arial',Round(cpi.scale_pi+3),[],clBlack));    // (scale+3 arbitrary) default for this changes with scale (he might change it later).

  scale:=cpi.scale_pi;             // mm per ft.
  if scale=0 then run_error(24);   // no data for this gauge_i (is this startup? otherwise should have been caught by the form).
  inscale:=scale/12.0;             // mm per inch.

  reset_defaults;  //  to use the new settings.

  gauge_str:=cpi.name_str_pi;          //  current gauge name.

  if from_list=True                //  change of gauge, so...
     then begin

            if (initdone_flag=True)   // can't test old_cg if not.
               then begin
                      if (old_cg.scale_pi>minfp) and (old_cg.gauge_pi>minfp) and (mod_rads=True)  // not startup (old scale=0)
                         then begin
                                                 // re-scale existing radii...

                                mod_scale_ratio:=scale/old_cg.scale_pi;
                                mod_gauge_ratio:=g/old_cg.gauge_pi;

                                mod_gauge_ratio:=mod_scale_ratio;  //!!!! experiments with separate ratios cancelled 21-1-01. Results not encouraging.

                                RESULT:=mod_gauge_ratio;      // return ratio for pegging calcs onto notch on gauge change.

                                if grid_labels_code_i=3       // proto-feet on pad grid.  0.79.a  23-05-06
                                   then begin
                                          grid_spacex:=grid_spacex*mod_gauge_ratio;
                                          grid_spacey:=grid_spacey*mod_gauge_ratio;
                                        end;

                                        // scale rads unless straight...

                                if ABS(nomrad)<max_rad_test then nomrad:=nomrad*mod_gauge_ratio;
                                if ABS(nomrad1)<max_rad_test then nomrad1:=nomrad1*mod_gauge_ratio;
                                if ABS(nomrad2)<max_rad_test then nomrad2:=nomrad2*mod_gauge_ratio;

                                check_radius(False,nomrad);  // don't need function return value.
                                check_radius(False,nomrad1); // don't need function return value.
                                check_radius(False,nomrad2); // don't need function return value.

                                os:=os*mod_gauge_ratio;
                                tst:=tst*mod_gauge_ratio;
                                slew_s:=slew_s*mod_gauge_ratio;
                                slew_l:=slew_l*mod_gauge_ratio;
                                slew:=slew*mod_gauge_ratio;

                                xorg:=xorg*mod_gauge_ratio;
                                turnoutx:=turnoutx*mod_gauge_ratio;   // (max checked in reset_defaults for new incx).

                                turnout_road_endx:=turnout_road_endx*mod_gauge_ratio;         //  223a    209a
                                min_turnout_road_endx:=min_turnout_road_endx*mod_gauge_ratio; //  223a    209a

                                main_road_endx:=main_road_endx*mod_gauge_ratio;               //  223a    217a
                                min_main_road_endx:=min_main_road_endx*mod_gauge_ratio;       //  223a    217a

                                if fixed_sl<>def_req then fixed_sl:=fixed_sl*mod_gauge_ratio;   // 223a

                                pegx:=pegx*mod_gauge_ratio;
                                if (peg_code=-1) or (peg_code=-2) then pegy:=pegy*mod_gauge_ratio;

                                startx:=startx*mod_gauge_ratio;       // blanking.

                                screenx_max:=100000*scale;       // 100000ft scale max zoom-out.             215a  was 10000ft  (increase for large tiled maps)

                                if mod_gauge_data=True     // we are not not minting, so overwrite list data for these...

                                                     //!!! scale ratio, not gauge, otherwise converting to say 00/H0 would produce much underscale centres.
                                   then begin
                                                       // don't change the names, gauge, scale, flangeways, etc...
                                                       // 0.82.a don't change min_rad for warning.

                                          with cpi do begin

                                            if old_cg.xing_fl_pi     <>def_req then      xing_fl_pi:=old_cg.xing_fl_pi*mod_scale_ratio;                     // mm length of flares.

                                            if old_cg.railtop_pi     <>def_req then      railtop_pi:=old_cg.railtop_pi*mod_scale_ratio;     // mm width of rail top (and bottom if bullhead).
                                            if old_cg.railbottom_pi  <>def_req then   railbottom_pi:=old_cg.railbottom_pi*mod_scale_ratio;  // mm width of railfoot (FB).

                                            if old_cg.trtscent_pi    <>def_req then     trtscent_pi:=old_cg.trtscent_pi*mod_scale_ratio;    // mm track centres, turnout side.
                                            if old_cg.trmscent_pi    <>def_req then     trmscent_pi:=old_cg.trmscent_pi*mod_scale_ratio;    // mm ditto, main side.
                                            if old_cg.retcent_pi     <>def_req then      retcent_pi:=old_cg.retcent_pi*mod_scale_ratio;     // mm ditto, return curve.

                                                       // then these are in full-size inches, or angles, so no need to use the scale ratio...

                                            if old_cg.rail_height_pi       <>def_req then      rail_height_pi:=old_cg.rail_height_pi;  // full-size inches - rail height (for 3D in DXF).

                                            if old_cg.rail_inclination_pi  <>def_req then rail_inclination_pi:=old_cg.rail_inclination_pi;   // radians.


                                            if old_cg.ck_ms_working1_pi     <>def_req then     ck_ms_working1_pi:=old_cg.ck_ms_working1_pi;    // full-size inches - size 1 MS check rail working length (back from "A").
                                            if old_cg.ck_ms_working2_pi     <>def_req then     ck_ms_working2_pi:=old_cg.ck_ms_working2_pi;    // full-size inches - size 2 MS check rail working length (back from "A").
                                            if old_cg.ck_ms_working3_pi     <>def_req then     ck_ms_working3_pi:=old_cg.ck_ms_working3_pi;    // full-size inches - size 3 MS check rail working length (back from "A").

                                            // 0.94.a no longer used, mouse diffs instead.  if old_cg.ck_ts_working_mod_pi  <>def_req then  ck_ts_working_mod_pi:=old_cg.ck_ts_working_mod_pi; // full-size inches - TS check rail working length modifier.

                                            if old_cg.ck_ms_ext1_pi         <>def_req then         ck_ms_ext1_pi:=old_cg.ck_ms_ext1_pi;        // full-size inches - size 1 MS check rail extension length (forward from "A").
                                            if old_cg.ck_ms_ext2_pi         <>def_req then         ck_ms_ext2_pi:=old_cg.ck_ms_ext2_pi;        // full-size inches - size 2 MS check rail extension length (forward from "A").

                                            // 0.94.a no longer used, mouse diffs instead.  if old_cg.ck_ts_ext_mod_pi      <>def_req then      ck_ts_ext_mod_pi:=old_cg.ck_ts_ext_mod_pi;     // full-size inches - TS check rail extension length modifier.

                                            if old_cg.wing_ms_reach1_pi     <>def_req then     wing_ms_reach1_pi:=old_cg.wing_ms_reach1_pi;    // full-size inches - size 1 MS wing rail reach length (forward from "A").
                                            if old_cg.wing_ms_reach2_pi     <>def_req then     wing_ms_reach2_pi:=old_cg.wing_ms_reach2_pi;    // full-size inches - size 2 MS wing rail reach length (forward from "A").

                                            // 0.94.a no longer used, mouse diffs instead.  if old_cg.wing_ts_reach_mod_pi  <>def_req then  wing_ts_reach_mod_pi:=old_cg.wing_ts_reach_mod_pi; // full-size inches - TS wing rail reach length modifier.


                                            if old_cg.tbwide_pi      <>def_req then       tbwide_pi:=old_cg.tbwide_pi;      // inches full-size width of turnout timbers.
                                            if old_cg.slwide_pi      <>def_req then       slwide_pi:=old_cg.slwide_pi;      // inches full-size width of plain sleepers.

                                            if old_cg.jt_slwide_pi   <>def_req_single then jt_slwide_pi:=old_cg.jt_slwide_pi;   // inches full-size width of joint sleepers. 212a

                                            if old_cg.tb_end_offset_pi<>def_req then       tb_end_offset_pi:=old_cg.tb_end_offset_pi;      // 232a inches full-size

                                            if old_cg.xtimbsp_pi     <>def_req then      xtimbsp_pi:=old_cg.xtimbsp_pi;     //  !!! disused in 0.75.a 14-10-01.inches full-size timber-spacing at crossing.
                                            if old_cg.ftimbspmax_pi  <>def_req then   ftimbspmax_pi:=old_cg.ftimbspmax_pi;  // inches full-size max timber-spacing for closure space.

                                            if old_cg.random_end_pi  <>def_req then   random_end_pi:=old_cg.random_end_pi;    // inches full-size amount of timber-end randomising.

                                            if old_cg.random_angle_pi<>def_req then random_angle_pi:=old_cg.random_angle_pi;   // amount of timber_angle randomising.

                                               // timber length not modified - done via old_tb_pi in reset_defaults if necessary.

                                          end;//with

                                          get_cpi;      // use these modified values.

                                          for n:=0 to Length(current_shoved_timbers)-1 do begin  // rescale (!!! scale ratio, not gauge) the timber shove dims ...
                                            with current_shoved_timbers[n].shove_data do begin     // shove data records.
                                              sv_x:=sv_x*mod_scale_ratio;        // xtb modifier.
                                              sv_o:=sv_o*mod_scale_ratio;        // offset modifier (near end).
                                              sv_l:=sv_l*mod_scale_ratio;        // length modifier (far end).
                                              sv_w:=sv_w*mod_scale_ratio;        // width modifier (per side).
                                            end;//with
                                          end;//for

                                               // 227a ..

                                          for n:=0 to Length(current_symbols)-1 do begin
                                            with current_symbols[n].symbol_data do begin
                                              symb_x:=symb_x*mod_scale_ratio;
                                            end;//with
                                          end;//for


                                          with ccd do begin   // 0.94.a check-rail end-gap modifiers (mm)

                                               // (length modifiers are in full-size inches and don't need changing )

                                            end_diff_mw.gap_diff:=end_diff_mw.gap_diff*mod_scale_ratio;
                                            end_diff_me.gap_diff:=end_diff_me.gap_diff*mod_scale_ratio;
                                            end_diff_mr.gap_diff:=end_diff_mr.gap_diff*mod_scale_ratio;

                                            end_diff_tw.gap_diff:=end_diff_tw.gap_diff*mod_scale_ratio;
                                            end_diff_te.gap_diff:=end_diff_te.gap_diff*mod_scale_ratio;
                                            end_diff_tr.gap_diff:=end_diff_tr.gap_diff*mod_scale_ratio;

                                            end_diff_mk.gap_diff:=end_diff_mk.gap_diff*mod_scale_ratio;
                                            end_diff_dk.gap_diff:=end_diff_dk.gap_diff*mod_scale_ratio;

                                          end;//with

                                        end;// if mod data.
                              end;// if mod rads
                    end;// if initdone

            incx:=def_req;              // x increment.

            ring_dia:=def_req;

            peg_arm_length:=def_req;

            if (g<>def_req) and (g<47.9*inscale) and (csi.group_code=2)     // n.g. now selected with current REA switch
               then begin
                      sz:=csi.size_code;
                      if sz>1 then sz:=sz-1;       // use next shorter size,
                      if set_csi_data(1,sz)=False  // change to straight switch and set new switch data from listbox entries.
                         then run_error(77);
                    end;
          end;

end;
//________________________________________________________________________________________

procedure rescale_notch(ratio:extended);

begin
  notchx:=notchx*ratio;
  notchy:=notchy*ratio;
end;
//________________________________________________________________________________________

procedure quick_gauge_click(i:integer);   // quick change of gauge and scale.

var
  mod_ratio:extended;
  not_mint:boolean;

begin
  not_mint:=NOT gauge_form.mint_new_radio_button.Checked;

  if not_mint=True then new_notch(get_peg_for_notch,False); // so can put the new template back on it.

  gauge_i:=i;                                 //  set new gauge index,
  mod_ratio:=gauge_dims(True,True,not_mint);  //  and get new dimensions.

  set_y_datum;                 //  need to change the pad datum.
  redraw(False);               //  force immediate redraw.

  if not_mint=True
     then begin
            rescale_notch(mod_ratio);
            shift_onto_notch(False,False);
          end
     else begin
            if gauge_form.retain_length_checkbox.Checked=True   // 208d test added, was (0)
               then mint_new_current(1,True)
               else mint_new_current(0,True);
          end;

  redraw(True);
end;
//________________________________________________________________________________________

function get_spiral_length(k_rads:extended):extended;      // 0.71.a  13-4-01.

               // return length of transition curve template for given k_rads angular swing.
               // !! length is from template datum, not transition origin.

begin
  RESULT:=turnoutx;     // default init.

  if (spiral=False) and (slewing=False)      // shouldn't be here by rights!
     then begin
            RESULT:=k_rads*nomrad;
            EXIT;
          end;

  length_in_degs:=k_rads;      // init for calc.

  wait_cancel_clicked:=False;
  wait_form.cancel_button.Show;
  wait_form.waiting_label.Caption:='calculating ...';

  wait_form.waiting_label.Width:=wait_form.Canvas.TextWidth(wait_form.waiting_label.Caption);  // 205b bug fix for Wine

  pad_form.trans_calc_timer.Tag:=15;       // do the calcs while the wait message shows modal.
  pad_form.trans_calc_timer.Enabled:=True; // one-shot only to start the process.

  do_show_modal(wait_form);  // 212a   ShowModal

  if Application.Terminated=False then Application.ProcessMessages;

  // returns here when the wait form closes...

  case pad_form.trans_calc_timer.Tag of

    -1: EXIT;  // he cancelled  - leave template as it is.

     0: begin
         alert(5,'    calculation  failed',
                 'Sorry, the angular swing calculations have failed to produce a result.'
                +'||The most likely reason is that you entered a swing angle in the wrong direction.'
                +'||For negative curving or a negative slew (in the opposite direction to the hand of the template), the swing angle is also negative.'
                +'||Alternatively, your specified swing angle may be invalid for the current transition curve and/or slewing settings.',
                 '','','','','','continue',0);
         EXIT;
       end;

     1: RESULT:=length_in_mm;
  end;//case
end;
//________________________________________________________________________________________

function do_degs_length_adjust(length_in_degs:extended; var length_in_mm:extended):integer;

     // this code moves the template overall length to and fro in reducing steps
     // until the total angular swing matches length_in_degs.

     // Return 1 if valid result, 0 if calc error, or -1 if he clicks cancel.

var
  dir,len_step:extended;
  saved_turnoutx, saved_xorg:extended;
  swing_start,swing_dir:extended;
  diff_swing_start:extended;

begin
  RESULT:=0;                   // default init..
  saved_turnoutx:=turnoutx;
  saved_xorg:=xorg;
  length_in_mm:=turnoutx;
  turnout_i:=1;      // length locked at new turnoutx.

  try
    do_rollback:=False;
    gocalc(0,0);
    swing_start:=total_swing;

    if ABS(length_in_degs-total_swing)<minfp   // already there.
       then begin
              RESULT:=1;
              EXIT;
            end;

              // trial start to set direction...

    turnoutx:=turnoutx+scale;   // step along 1ft scale (arbitrary).
    if turnoutx<0 then EXIT;                        // calc failed.
    if turnoutx>turnoutx_max then EXIT;             // calc failed.
    if xorg>turnoutx then xorg:=turnoutx;

    do_rollback:=False;
    gocalc(0,0);                   // fresh calc sets new total swing.

    diff_swing_start:=total_swing-swing_start;

    if ABS(diff_swing_start)<minfp then EXIT;  // calc failed - swing not changing with length changes.

    swing_dir:=1;                              // swing increases with increasing length.
    if diff_swing_start<0 then swing_dir:=-1;  // or swing reduces with increasing length.

              // reset and start again...

    turnoutx:=saved_turnoutx;
    xorg:=saved_xorg;

    dir:=1;                // start by going forward.
    len_step:=10*scale;    // 10ft scale arbitrary  initial step-size.
    if len_step>(saved_turnoutx/4) then len_step:=saved_turnoutx/4;   // 1/4 arbitrary.

    if ((length_in_degs<swing_start) and (swing_dir=1))   // or start by going backwards.
       then begin
              dir:=-1;
              len_step:=0-len_step;
            end;

    repeat

      do_rollback:=False;
      turnoutx:=turnoutx+len_step*swing_dir;           // step along.
      if turnoutx<0 then BREAK;                        // calc failed.
      if turnoutx>turnoutx_max then BREAK;             // calc failed.
      if xorg>turnoutx then xorg:=turnoutx;

      gocalc(0,0);                   // fresh calc sets new total swing.

      if ABS(total_swing-length_in_degs)<0.00001   // arbitrary tolerance.
         then begin
                length_in_mm:=turnoutx;
                RESULT:=1;
                EXIT;
              end;

      if ((dir=1) and (total_swing>length_in_degs))   // gone too far..
      or ((dir=-1) and (total_swing<length_in_degs))
         then begin
                len_step:=0-len_step/10;     // change direction and use smaller steps.
                dir:=0-dir;
              end;
                        // try again..

      if Application.Terminated=False then Application.ProcessMessages;  // allow him to click.

      if wait_cancel_clicked=True   // he cancelled.
         then begin
                RESULT:=-1;
                EXIT;
              end;

    until ( ((dir*swing_dir)=1) and (turnoutx>turnoutx_max) )
       or ( ((dir*swing_dir)=-1) and (turnoutx<0) );           // will take forever, but we must have a limit of some sort.

    if RESULT=0         // reset and start again with oppopsite sign of required swing...
                        // (this is his most likely error).
       then begin
              length_in_degs:=0-length_in_degs;  // required swing

              turnoutx:=saved_turnoutx;
              xorg:=saved_xorg;

              dir:=1;                // start by going forward.
              len_step:=10*scale;    // 10ft scale arbitrary  initial step-size.
              if len_step>(saved_turnoutx/4) then len_step:=saved_turnoutx/4;   // 1/4 arbitrary.

              if ((length_in_degs<swing_start) and (swing_dir=1))   // or start by going backwards.
                 then begin
                        dir:=-1;
                        len_step:=0-len_step;
                      end;

              repeat

                do_rollback:=False;
                turnoutx:=turnoutx+len_step*swing_dir;          // step along in direction to get increasing swing.
                if turnoutx<0 then EXIT;                        // calc entirely failed.
                if turnoutx>turnoutx_max then EXIT;             // calc entirely failed.
                if xorg>turnoutx then xorg:=turnoutx;

                gocalc(0,0);                   // fresh calc sets new total swing.

                if ABS(total_swing-length_in_degs)<0.00001   // arbitrary tolerance.
                   then begin
                          length_in_mm:=turnoutx;
                          RESULT:=1;
                          EXIT;
                        end;

                if ((dir=1) and (total_swing>length_in_degs))   // gone too far..
                or ((dir=-1) and (total_swing<length_in_degs))
                   then begin
                          len_step:=0-len_step/10;     // change direction and use smaller steps.
                          dir:=0-dir;
                        end;
                                  // try again..

                if Application.Terminated=False then Application.ProcessMessages;  // allow him to click.

                if wait_cancel_clicked=True   // he cancelled.
                   then begin
                          RESULT:=-1;
                          EXIT;
                        end;

              until ( ((dir*swing_dir)=1) and (turnoutx>turnoutx_max) )
                 or ( ((dir*swing_dir)=-1) and (turnoutx<0) );           // will take forever, but we must have a limit of some sort.

            end;

  finally
    turnoutx:=saved_turnoutx;     // restore original control template.
    xorg:=saved_xorg;
  end;//try
end;
//_______________________________________________________________________________________

procedure lengths_click(degs:boolean);

          // get xorg, startx and turnoutx lengths ...
const
  pt_blanking_str:string='For a plain track template, the blanking length is measured from the CTRL-1 marker back towards the template datum at CTRL-0.';

  startx_help_str:string='For a turnout template, the blanking length is measured from the template datum (the fixing peg RESET position, CTRL-0), towards the crossing and includes the approach track length, if any.'

  +'||For a plain track template, the blanking length is measured in the opposite direction from the CTRL-1 marker back towards the template datum at CTRL-0.'

  +'||Blanking part of the template is useful when superimposing two templates. For example you may want to omit the first two sleepers between the rail-joint and toe (blade tips)'
  +' when creating a tandem turnout, or fitting two turnouts close together. Likewise by blanking up to the crossing you can use it as a separate component in a complex formation.'

  +'||The pre-set dimension is zero, i.e. no blanking.'

  +'||Note that setting a blanking length does not change the underlying overall length of the turnout, and any blanking is automatically cancelled by some Templot functions to avoid confusion.'
  +'||(e.g. the CONVERT TO PLAIN TRACK function, the SWAP CONTROL END-FOR-END and MAKE DOUBLE TRACK tools, and the ADJUST PLAIN TRACK LENGTH (F4), ADJUST APPROACH LENGTH (F3) and ROAM ALONG LENGTH (CTRL-F9) mouse actions.)'

  +'||Blanking is not available when the V-crossing has been isolated.'

  +'||Handy Hints :'
  +'|It is easier to set the blanked length by mouse action (ACTION > MOUSE ACTIONS: GEOMETRY > ADJUST BLANKING LENGTH menu item) or by moving the peg to the desired position (with mouse action CTRL-F8)'
  +' and then selecting the DO > BLANK UP TO PEG menu item. The blanked length can be increased or reduced this way.'

  +'||To blank out the part of a turnout beyond the peg (i.e towards the crossing end), select the DO > SNAP TO PEG menu item, or simply reduce the overall length (mouse action F4).'
  +'||To use the switch as a separate component (as a set of catch points, for example) select the DO > SNAP TO CATCH POINTS menu item.'

  +'||To cancel blanking, set a blanking length of zero, or select the DO > CANCEL BLANKING menu item.'

  +'||It is possible to set a negative blanking length. This permits a timber to be shoved out beyond the CTRL-0 datum position, if necessary, but otherwise it has no effect.'
  +' Note that if you subsequently cancel blanking, such a shoved timber will become inaccessible.'

  +'||( Caution: If you blank to beyond the nose of the vee at the crossing, select the PRINT > PRINTED DRAWING OPTIONS > RAIL INFILL STYLE > NONE menu item before printing the template.'
  +' Otherwise a risk exists with some printers that the rail colour infill function might fail, possibly wasting coloured ink across a large area of the paper.)';

  xorg_help_str:string='||The approach length is the distance from the CTRL-0 datum position to the CTRL-1 rail-joint position at the toe of the turnout.'
  +'||The approach length is part of the overall turnout length. If you change the approach length, you will probably want to make a similar amount of change to the overall length.'
  +'||N.B. For a half-diamond template, there is no approach length, and any dimension entered will be ignored.';

  turnoutx_help_str:string='||If you have made a change to the approach length you will probably want to make a similar amount of change to the overall length to maintain the full length of the turnout.'
  +'||To add a length of exit track to the turnout, increase the overall length accordingly.';

  turnout_road_help_str:string='Turnout-Road Exit Length'
  +'||This dimension applies only when the turnout-road exit length setting is `0GEOMETRY > TURNOUT-ROAD EXIT LENGTH > ADJUSTABLE`1. For other settings this dimension is ignored.'
  +'||It is usually easier to change this setting by mouse action `0CTRL+F12`2.';  // 209a

  main_road_help_str:string='Main-Road Exit Length'
  +'||This dimension applies only when the main-road exit length setting is `0GEOMETRY > MAIN-ROAD EXIT LENGTH > ADJUSTABLE`1. For other settings this dimension is ignored.';

  deg_explain_str:string='||The actual length which this represents will be determined by the current track centre-line radius.'
  +'||To enter the length directly in mm, cancel this and click the GEOMETRY > TEMPLATE LENGTHS (IN MM)... menu item instead.'
  +'||If desired the angle can be entered as a RAM unit value by prefixing the figure with the input conversion letter "n". For example an angular swing of 1:6 can be entered as "n6".'
  +' Be aware that unit angles are an inverse measure, a larger unit angle will produce a shorter length.'
  +' For more information about about input conversion factors click the ? HELP button.';

  mm_explain_str:string='||For curved templates this length can also be entered as an angular swing in degrees or unit angles. To do so cancel this and click the GEOMETRY > SWING ANGLES (IN DEGREES)... menu item instead.';

  rails_explain_str:string='||For plain track templates the length can also be entered as a number of scale rail lengths. To do so cancel this and click the REAL > PLAIN TRACK OPTIONS > TEMPLATE LENGTH (IN RAILS)... menu item instead.';


var
  n, i:integer;
  od:Toutdim;
  dummy:extended;
  deg_factor:extended;
  code:integer;
  deg_str:string;
  approach_help_str,overall_help_str,blank_help_str,pt_help_str:string;

  old_xorg:extended;

begin
  if degs=True
     then begin
            deg_str:='angular  swing';

            approach_help_str:='     Approach  Length'
                           +'||Enter a dimension in degrees of angular swing which represents the required length of plain approach track for the current turnout template.'
                           +deg_explain_str+xorg_help_str;

            overall_help_str:='     Overall  Length'
                           +'||Enter a dimension in degrees of angular swing which represents the required overall length of the current turnout template including any approach track.'
                           +deg_explain_str+turnoutx_help_str;

            blank_help_str:='     Blanking  Length'
                           +'||Enter a dimension in degrees of angular swing which represents the length of your turnout which is required to be blanked out and not appear in the drawing.'
                           +deg_explain_str+startx_help_str;

            pt_help_str:='     Plain Track  Overall  Length'
                           +'||Enter a dimension in degrees of angular swing which represents the required overall length of the current plain track template.'
                           +deg_explain_str+rails_explain_str;
          end;


  if ( (ABS(nomrad)<max_rad_test) and (spiral=False) and (slewing=False) ) or (degs=False)  // fixed curve degs, or mm...
     then begin
            if (degs=True) and (ABS(nomrad)>minfp)    // fixed curve degs...
               then begin
                      code:=3;
                      deg_factor:=180/(Pi*ABS(nomrad));
                    end
               else begin
                      code:=1;
                      deg_factor:=1.0;
                      deg_str:='mm';

                      approach_help_str:='     Approach  Length'
                                     +'||Enter a dimension in mm which represents the required length of plain approach track for the current turnout template, measured along the track centre-line.'
                                     +mm_explain_str+xorg_help_str;


                      overall_help_str:='     Overall  Length'
                                     +'||Enter a dimension in mm which represents the required overall length of the current turnout template including any approach track.'
                                     +mm_explain_str+turnoutx_help_str;

                      blank_help_str:='     Blanking  Length'
                                     +'||Enter a dimension in mm which represents the length of your turnout which is required to be blanked out and not appear in the drawing.'
                                     +mm_explain_str+startx_help_str;

                      pt_help_str:='     Plain Track  Overall  Length'
                                     +'||Enter a dimension in mm which represents the required overall length of the current plain track template, measured along the track centre-line.'
                                     +mm_explain_str+rails_explain_str;
                    end;

            old_xorg:=xorg;

            kform_now:=kform;
            docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy);    // save current peg data for peg_curve calcs.

            if plain_track=False
               then repeat
                         putdim(approach_help_str,code,'approach  track  length',xorg*deg_factor,True,False,False,False);    // start offset, no neg, preset ok, 0 mm is valid, don't terminate on zero.
                         putdim(overall_help_str,code,'overall  length  of  turnout  including  approach',turnoutx*deg_factor,True,True,True,False);   // no neg, no preset, no zero, don't terminate on zero.
                         putdim(blank_help_str,code,'blanking  length',startx*deg_factor,False,False,False,False);   // neg ok, preset ok, zero ok, don't terminate on zero.
                         putdim(turnout_road_help_str,code,'length  to  turnout-road  exit  including  approach',(xorg+turnout_road_endx)*deg_factor,True,True,True,False);   // 209a no neg, no preset, no zero, don't terminate on zero.
                      n:=putdim(main_road_help_str,code,'length  to  main-road  exit  including  approach',(xorg+main_road_endx)*deg_factor,True,True,True,False);            // 217a no neg, no preset, no zero, don't terminate on zero.

                      if n<>4 then EXIT;
                      if getdims('turnout  lengths  in  '+deg_str,'',pad_form,n,od)=True
                         then begin
                                if od[0]=def_req then od[0]:=0;     // zero presets..
                                if od[2]=def_req then od[2]:=0;

                                if (od[0]<0) or (od[1]<0) or (od[0]>od[1]) or (od[2]>od[1])
                                or ((od[0]/deg_factor)>turnoutx_max) or ((od[1]/deg_factor)>turnoutx_max) or ((od[2]/deg_factor)>turnoutx_max) or ((od[3]/deg_factor)>turnoutx_max)  or ((od[4]/deg_factor)>turnoutx_max)
                                   then begin
                                          i:=alert(6,'     one  or  more  invalid  lengths',
                                                     'One or more of the entered dimensions is invalid:'
                                                    +'||The approach length cannot be more than the overall length.'
                                                    +'||The blanking length cannot be more than the overall length.'
                                                    +'||None of the lengths can be more than '+round_str(turnoutx_max,2)+' mm.'
                                                    +'||Only the blanking length can be negative.',
                                                     '','','','','cancel','O K  -  retry',0);
                                          if i=5 then EXIT;
                                          CONTINUE;
                                        end;
                                if half_diamond=False then xorg:=ABS(od[0])/deg_factor
                                                      else xorg:=0;
                                turnoutx:=ABS(od[1])/deg_factor;
                                startx:=od[2]/deg_factor;

                                turnout_road_endx:=(od[3]-xorg)/deg_factor; // 209a  stored dim is from CTRL-1

                                main_road_endx:=(od[4]-xorg)/deg_factor;    // 217a  stored dim is from CTRL-1

                                BREAK;
                              end
                         else EXIT;
                    until 0<>0
               else begin
                         putdim(pt_help_str,code,'overall  length  of  plain  track',xorg*deg_factor,True,True,True,False);   // no neg, no preset, no zero, don't terminate on zero.
                      n:=putdim(pt_blanking_str,1,'plain  track  blanking  length',pt_blank,True,True,False,False);               // no neg, no preset, zero ok, don't terminate on zero.

                      if n<>1 then EXIT;

                      if getdims('plain  track  length  in  '+deg_str,'',pad_form,n,od)=True
                         then begin
                                xorg:=ABS(od[0])/deg_factor;
                                turnoutx:=xorg;

                                if turnoutx>turnoutx_max
                                   then begin
                                          turnoutx:=turnoutx_max;
                                          xorg:=turnoutx;
                                        end;

                                pt_blank:=od[1];
                              end;
                    end;
          end
     else begin     // transition or slewing in degrees...

            old_xorg:=xorg;

            kform_now:=kform;
            docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy);    // save current peg data for peg_curve calcs.

            if plain_track=False
               then begin
                      n:=putdim(overall_help_str,3,'overall  length  of  turnout  including  approach',total_swing*180/Pi,False,True,False,False);   // neg ok (s-curve), no preset, zero ok (s-curve), don't terminate on zero.
                      if n<>0 then EXIT;
                    if getdims('turnout  length  in  '+deg_str,'',pad_form,n,od)=True
                         then begin
                                turnoutx:=get_spiral_length(od[0]*Pi/180);
                                if turnoutx<0 then turnoutx:=0;
                                if turnoutx>turnoutx_max then turnoutx:=turnoutx_max;
                                if xorg>turnoutx then xorg:=turnoutx;
                              end
                         else EXIT;
                    end
               else begin
                      n:=putdim(pt_help_str,3,'overall  length  of  plain  track',total_swing*180/Pi,False,True,False,False);   // neg ok (s-curve), no preset, zero ok (s-curve), don't terminate on zero.
                      if n<>0 then EXIT;
                      if getdims('plain  track  length  in  '+deg_str,'',pad_form,n,od)=True
                         then begin
                                xorg:=get_spiral_length(od[0]*Pi/180);
                                if xorg<0 then xorg:=0;
                                turnoutx:=xorg;
                                if turnoutx>turnoutx_max
                                   then begin
                                          turnoutx:=turnoutx_max;
                                          xorg:=turnoutx;
                                        end;
                              end
                         else EXIT;
                    end;
          end;


  if peg_code<>0
     then begin
            if spiral=True then os:=os+xorg-old_xorg;           //  os transition start changes with xorg ditto.
            if slewing=True then slew_s:=slew_s+xorg-old_xorg;  //  slewing ditto
          end;

  pegx:=pegx-old_xorg+xorg;         // update the peg if free...
  udpegx:=udpegx-old_xorg+xorg;

  turnout_i:=1;      // overall length locked at new turnoutx.

  gocalc(0,0);       //  to calc new pegx.
  peg_curve;         //  adjust shifts and rotates for current peg position.

  do_rollback:=True;
  show_and_redraw(True,True);
end;
//_________________________________________________________________________________________

function check_black_white:boolean;    // return True if he cancels (called from change printer colours).

var
  i,colour_depth_bits:integer;

begin
  RESULT:=False;    // default init.
try
  // OT2024  colour_depth_bits:=GetDeviceCaps(Printer.Canvas.Handle,BITSPIXEL)*GetDeviceCaps(Printer.Canvas.Handle,PLANES);
  colour_depth_bits:=24;  // OT2024  kludge

  if colour_depth_bits=1
     then begin
            i:=alert(2,'    monochrome  printer',
                       'Your current printer is not a colour printer, or has not been set to print in colour.'
                      +'||Changes which you make to the printing colours will have no effect until you change to a colour printer.',
                       '','','ignore  this  message','printer  setup ...','cancel','O K',0);
            case i of
                4: printer_setup(False,False);
                5: RESULT:=True;
            end;//case

            EXIT;
          end;

  if black_white=True
     then begin
            i:=alert(3,'    black & white  printing',
                       'You are currently set to print in black and white only.'
                      +'||Changes which you make to the printing colours will have no effect until you change to full-colour or grey-shade printing.',
                       '','','change  to  grey - shade  printing','change  to  full - colour  printing','cancel','O K',0);
            case i of
                3: pad_form.grey_shade_printing_menu_entry.Click;
                4: pad_form.colour_printing_menu_entry.Click;
                5: RESULT:=True;
            end;//case
            EXIT;
          end;

  if grey_shade=True
     then begin
            i:=alert(3,'    grey - shade  printing',
                       'You are currently set to print in grey shades.'
                      +'||Changes which you make to the printing colours will be converted to shades of grey until you change to full-colour printing.',
                       '','','','change  to  full - colour  printing','cancel','O K',0);
            case i of
                4: pad_form.colour_printing_menu_entry.Click;
                5: RESULT:=True;
            end;//case
          end;
finally
  if RESULT=False then print_form.reset_button.Click;   // reset intensity so that the new print colour can be seen properly.
end;//try
end;
//_____________________________________________________________________________________________

function clrad_at_x(x:extended):extended;     // return the main-road track centre-line radius at this xs.

var
  dummy1,dummy2,dummy3:extended;

begin
  RESULT:=max_rad;      // default init for straight track.

  if spiral=True    // currently a transition, so get rad at peg position..
     then begin
            if x>os
               then begin                         // peg is not in r1.
                      if x>=(os+tst)                     // 214a bug fix was x>(
                         then RESULT:=nomrad2     // peg is in r2.
                         else begin                      // peg is in the transition zone.
                                if transcalcs(False,False,trans_k,(x-os+ts1),dummy1,dummy2,dummy3,RESULT)=False
                                   then RESULT:=nomrad1;  // no change if trans error.
                              end;
                    end
               else RESULT:=nomrad1;             // peg is in r1.
          end
     else RESULT:=nomrad;    // fixed curve.

  if ABS(RESULT)>max_rad then RESULT:=max_rad*SGZ(RESULT);  // in case we just picked up a straight.  212a SGZ added

  if ABS(RESULT)<(g*2) then RESULT:=g*2*SGZ(RESULT);        // min rad (arbitrary).

end;
//_________________________________________________________________________________________

procedure fix_radius(rad:extended; click:boolean);     // set up fixed-radius curving.

var
  dummy:extended;

begin
  kform_now:=kform;
  docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy);    // save current peg data for peg_curve calcs.

  nomrad:=rad;

  spiral:=False;

  if (peg_code=200) or (peg_code=220) then gocalc(0,0);    // to keep peg on geometrical tangent points.

  peg_curve;            // fixed curve turnout on peg.

  gocalc(0,0);

  if click=True then redraw_pad(True,True);
end;
//_______________________________________________________________________________________

procedure transition_clicked(trans_code:integer);

var
  temp, temp_ktrans, existing_rad:extended;

begin
  kform_now:=kform;
  docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,temp);    // save current peg data for peg_curve calcs.

  case trans_code of

 //      0: do nothing, use previous radii and lengths, or startup defaults.

         1: begin    // ease from new straight to existing radius...

              if (spiral=False) and (ABS(nomrad)>(max_rad/2)) then nomrad:=660*scale;  // change existing straight to 10 chains.
              if (spiral=True) and (ABS(nomrad2)>(max_rad/2)) then nomrad2:=660*scale; // change existing straight to 10 chains.

              nomrad1:=max_rad;                             // first transition radius (straight).
              if spiral=False then nomrad2:=nomrad{+ycurv}; // second transition radius (as current fixed curve centre-line),
                                                            // (but don't change if currently a transition).

              tst:=132*scale;                                  // arbitrary transition length 2 chains (132ft).
              if tst>(turnoutx*0.95) then tst:=turnoutx*0.95;  // arbitrary max 95% template length.

              os:=0;                // length of first radius (straight) (zero)
            end;

         2: begin    // ease to new straight from existing radius...

              if (spiral=False) and (ABS(nomrad)>(max_rad/2)) then nomrad:=660*scale;  // change existing straight to 10 chains.
              if (spiral=True) and (ABS(nomrad1)>(max_rad/2)) then nomrad1:=660*scale; // change existing straight to 10 chains.

              nomrad2:=max_rad;                             // second transition radius (straight).
              if spiral=False then nomrad1:=nomrad{+ycurv}; // first transition radius (as current fixed curve centre-line),
                                                            // (but don't change if currently a transition).

              tst:=132*scale;                                 // arbitrary transition length 2 chains (132ft).
              if tst>(turnoutx*0.95) then tst:=turnoutx*0.95; // arbitrary max 95% template length.

              os:=turnoutx-tst;     // length of first radius to give zero length of second(straight).
            end;

         3: begin    // C-curve transition shrinking from existing radius...

              existing_rad:=clrad_at_x(pegx);     // set rads from existing radius at peg.

              if peg_code=0    // CTRL-0, use existing 1st radius.
                 then begin
                        nomrad1:=existing_rad;

                        if ABS(nomrad1)>(max_rad/2)                // arbitrary (is straight).
                           then nomrad2:=(660*scale)*SGZ(nomrad1)  // so transition down to 10 chains.
                           else nomrad2:=nomrad1/2;                // or down to half of 1st radius.
                      end
                 else begin
                        nomrad2:=existing_rad;

                        if ABS(nomrad2)>(max_rad/2)                // arbitrary (is straight).
                           then nomrad1:=(660*scale)*SGZ(nomrad2)  // so transition down to 10 chains.
                           else nomrad1:=nomrad2/2;                // or down to half of 2nd radius.
                      end;

              tst:=turnoutx*0.6;     // arbitrary transition length 60% of template.
              os:=turnoutx*0.2;      // arbitrary length of first radius 20% of template.
            end;

         4: begin    // C-curve transition expanding from existing radius...

              existing_rad:=clrad_at_x(pegx);     // set rads from existing radius at peg.

              if peg_code=0    // CTRL-0, use existing 1st radius.
                 then begin
                        nomrad1:=existing_rad;

                        if ABS(nomrad1)>(max_rad/2)                // arbitrary (is straight).
                           then nomrad2:=(660*scale)*SGZ(nomrad1)  // so transition down to 10 chains.
                           else nomrad2:=nomrad1*2;                // or up to double 1st radius.
                      end
                 else begin
                        nomrad2:=existing_rad;

                        if ABS(nomrad2)>(max_rad/2)                // arbitrary (is straight).
                           then nomrad1:=(660*scale)*SGZ(nomrad2)  // so transition down to 10 chains.
                           else nomrad1:=nomrad2*2;                // or up to double 2nd radius.
                      end;

              tst:=turnoutx*0.6;     // arbitrary transition length 60% of template.
              os:=turnoutx*0.2;      // arbitrary length of first radius 20% of template.
            end;

         5: begin    // S-curve reverse transition at existing radius...

              existing_rad:=clrad_at_x(pegx);     // set rads from existing radius at peg.

              if peg_code=0    // CTRL-0, use existing 1st radius.
                 then begin
                        nomrad1:=existing_rad;

                        if ABS(nomrad1)>(max_rad/2)  // arbitrary (is straight).
                           then nomrad1:=660*scale;  // so use 10 chains.

                        nomrad2:=0-nomrad1;          // to opposite direction same size as 1st radius.
                      end
                 else begin
                        nomrad2:=existing_rad;

                        if ABS(nomrad2)>(max_rad/2)  // arbitrary (is straight).
                           then nomrad2:=660*scale;  // so use 10 chains.

                        nomrad1:=0-nomrad2;          // to opposite direction same size as 2nd radius.
                      end;

              tst:=turnoutx*0.6;     // arbitrary transition length 60% of template.
              os:=turnoutx*0.2;      // arbitrary length of first radius 20% of template.
            end;

  end;//case

  if ABS(nomrad1)<(g*2) then nomrad1:=g*2*SGZ(nomrad1);     // min rad (arbitrary).
  if ABS(nomrad2)<(g*2) then nomrad2:=g*2*SGZ(nomrad2);     // min rad (arbitrary).

  temp:=nomrad1-nomrad2;

  while ABS(temp)<minfp do begin   // no good if rads equal.
    nomrad1:=nomrad1*1.05;         // increase r1 by 5%
    nomrad2:=nomrad2/1.05;         // reduce r2 by 5%
    temp:=nomrad1-nomrad2;
  end;//while

  temp_ktrans:=nomrad1*nomrad2*tst/temp;     // new spiral constant

  if ABS(temp_ktrans)>max_spiral_constant
     then begin
            tst:=max_spiral_constant*SGZ(temp_ktrans)*temp/nomrad1/nomrad2;  //  no good, limit zone length.
            if tst<minfp then tst:=0;
          end;

  with pad_form do begin

    adjust_trans_length_menu_entry.Enabled:=True;  // mouse actions...
    adjust_trans_start_menu_entry.Enabled:=True;

    zone_normal_menu_entry.Checked:=True;          // radio item - normal length adjust action.
    transition_template_menu_entry.Checked:=True;  // radio item.
  end;

  spiral:=True;

  peg_curve;            // transition curve onto peg.
  redraw_pad(True,True);
end;
//______________________________________________________________________________

function do_auto_trans_swing_adjust(old_rad2_orgx, old_rad2_orgy:extended):integer;

     // this code swings the transition curve on rad1 to and fro in reducing steps
     // until the new position of the 2nd rad centre co-incides with the old.
     // The 1st rad centres must be co-incident.
     // The transition zone length must also have been adjusted so that the separation distance
     // between the two centres (ignoring slewing) is the same as before.

     // The rad centres are calculated ON THE PAD, i.e. including slewing.

     // Return 1 if valid result, 0 if calc error, or -1 if he clicks cancel.

var
  xdiff2,ydiff2:extended;
  prev_diff2_sq,next_diff2_sq:extended;

  dir,k_step,start_kform:extended;

begin
  try
    RESULT:=0;   // default init

    do_rollback:=False;
    gocalc(0,0);     // get new calcs.

    xdiff2:=rad2_orgx-old_rad2_orgx;
    ydiff2:=rad2_orgy-old_rad2_orgy;

    prev_diff2_sq:=SQR(xdiff2)+SQR(ydiff2);  // square of distance apart of new and old r2 centres.

    k_step:=Pi/18;     // initial stepping at 10 degrees.

    init_rotate(xt1,yt1,True);

    start_kform:=kform;

    dir:=1;                // start by going forward (arbitrary direction).

    repeat

      do_rollback:=False;
      kform:=kform+k_step;
      gocalc(0,0);              // fresh calc sets new rad2 radial centre.

      xdiff2:=rad2_orgx-old_rad2_orgx;       // how far is the rad2 centre off the original position?
      ydiff2:=rad2_orgy-old_rad2_orgy;

      next_diff2_sq:=SQR(xdiff2)+SQR(ydiff2);

      if next_diff2_sq<0.0001
         then begin
                RESULT:=1;  // r2 centre now within a radius of .01mm of the original centre (dim is squared).
                EXIT;
              end;

      if next_diff2_sq>prev_diff2_sq     // gone too far (difference more than last time)?
         then begin
                k_step:=0-k_step/10;     // change direction and use smaller steps.
                dir:=0-dir;
              end;
                        // try again..

      prev_diff2_sq:=next_diff2_sq;

      if Application.Terminated=False then Application.ProcessMessages;  // allow him to click.

      if wait_cancel_clicked=True   // exit calcs but leave kform where we are.
         then begin
                RESULT:=-1;
                EXIT;
              end;

    until ( (dir=1) and (kform>(start_kform+2*Pi)) )
       or ( (dir=-1) and (kform<(start_kform-2*Pi)) )   // will take forever, but we must have a limit of some sort.
       or ( ABS(k_step)<minfp );

  finally
    normalize_kform;
  end;//try
end;
//_______________________________________________________________________________________

function make_transition_from_current(control_loc,bgnd_loc,trans_hand:integer):boolean;

            // 17-9-15 control_loc,bgnd_loc;  // 0=match at peg, 15=at 1st radius, 16=at 2nd radius.  212a

            // 20-4-01  trans_hand: 1= r1 is LH, -1= r1 is RH .

var
  temp,dummy,dummy1,dummy2:extended;
  saved_control:Ttemplate_info;
  saved_notch:Tnotch;

  got_transition:boolean;

  error_allow:extended;


                          ///////////////////////////////////////////////////////////////////

                          procedure error_restore;

                          begin

                            if keeps_list.Count>0
                               then begin
                                      list_position:=keeps_list.Count-1;  // last one in box we just stored.
                                      delete_keep(False, False);          // delete it.
                                    end;

                            unhold_the_control(saved_control);  // 227a

                          end;
                          ////////////////////////////////////////////////////////////////////


begin
  RESULT:=False;  // default init.

  do_rollback:=False;
  store_and_background(False,False);  // keep it (does a recalc) and copy to background.
  if keep_added=False then EXIT;      // he cancelled.

                   // -------- ready to go...
  try
    saved_control:=hold_the_control;  // 227a

    if spiral=True      // 19-09-2015  212a this is a kludge bug-fix...
       then begin
              if ABS(nomrad1)>max_rad_test    // straight?
                 then begin
                        kform_now:=kform;
                        docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy);    // save current peg data for peg_curve calcs.

                        nomrad1:=(max_rad_test-1)*SGZ(nomrad1);  // kludge - ensure treated as curved, not straight.
                                                                 // until I can find bug when it's straight 19-09-2015

                        peg_curve;      //  keep it on peg..
                        gocalc(0,0);
                      end;

              if ABS(nomrad2)>max_rad_test    // straight?
                 then begin
                        kform_now:=kform;
                        docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy);    // save current peg data for peg_curve calcs.

                        nomrad2:=(max_rad_test-1)*SGZ(nomrad2);  // kludge - ensure treated as curved, not straight.
                                                                 // until I can find bug when it's straight 19-09-2015

                        peg_curve;      //  keep it on peg..
                        gocalc(0,0);
                      end;

            end
       else begin                            // fixed curve
              if ABS(nomrad)>max_rad_test    // straight?
                 then begin
                        kform_now:=kform;
                        docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy);    // save current peg data for peg_curve calcs.

                        nomrad:=(max_rad_test-1)*SGZ(nomrad);  // kludge - ensure treated as curved, not straight.
                                                               // until I can find bug when it's straight 19-09-2015

                        peg_curve;      //  keep it on peg..
                        gocalc(0,0);
                      end;
            end;

    //----------------

    if peg_rail<>8   // bug fix 212a  (needed before putting notch under)
       then begin
              peg_rail:=8;    // peg must be on main-road centre-line.
              peg_code:=20;   // flag gocalc to use peg_rail.
              gocalc(0,0);
            end;

    if (spiral=True) and ((control_loc=15) or (control_loc=16))  // move peg to trans start or length  212a
       then begin
              peg_code:=control_loc;
              gocalc(0,0);
            end;

    unlink_group;                                           // we want to use the notch, cancel any linking..
    pad_form.notch_unlinked_from_current_menu_entry.Click;  // cancel any moving the notch in mouse actions.

    saved_notch:=get_current_notch;         // save his current notch position.
    new_notch(get_peg_for_notch,False);     // first put the notch under the control template, for later pegging.

      // first convert it to fixed curve (so can get rad centre at instantaneous radius if peg in a transition zone or template straight).

    if spiral=True
       then begin
              fix_radius(clrad_at_x(pegx),False);         // set nomrad from centre-line radius at peg.
              gocalc(0,0);
            end;

    with make_trans_data do begin

      old_rad1_orgx:=rad1_orgx;       // 1st rad and centres on pad..
      old_rad1_orgy:=rad1_orgy;
      rad_1st:=nomrad;

      peg1x:=pegx_on_pad;                     // and 1st peg position on pad..
      peg1y:=pegy_on_pad*hand_i+y_datum;


        // now get the background template, and repeat...

      list_position:=clicked_keep_index;              // make it current in the keeps box.
      copy_keep_to_current(False,False,True,False);   // copy to pad.
      gocalc(0,0);

      //------------------

      if spiral=True      // 19-09-2015  212a this is a kludge bug-fix...
         then begin
                if ABS(nomrad1)>max_rad_test    // straight?
                   then begin
                          kform_now:=kform;
                          docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy);    // save current peg data for peg_curve calcs.

                          nomrad1:=(max_rad_test-1)*SGZ(nomrad1);  // kludge - ensure treated as curved, not straight.
                                                                   // until I can find bug when it's straight 19-09-2015

                          peg_curve;      //  keep it on peg..
                          gocalc(0,0);
                        end;

                if ABS(nomrad2)>max_rad_test    // straight?
                   then begin
                          kform_now:=kform;
                          docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy);    // save current peg data for peg_curve calcs.

                          nomrad2:=(max_rad_test-1)*SGZ(nomrad2);  // kludge - ensure treated as curved, not straight.
                                                                   // until I can find bug when it's straight 19-09-2015

                          peg_curve;      //  keep it on peg..
                          gocalc(0,0);
                        end;

              end
         else begin                            // fixed curve
                if ABS(nomrad)>max_rad_test    // straight?
                   then begin
                          kform_now:=kform;
                          docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy);    // save current peg data for peg_curve calcs.

                          nomrad:=(max_rad_test-1)*SGZ(nomrad);  // kludge - ensure treated as curved, not straight.
                                                                 // until I can find bug when it's straight 19-09-2015

                          peg_curve;      //  keep it on peg..
                          gocalc(0,0);
                        end;
              end;

      //--------------------

      if spiral=True
         then begin
                if (bgnd_loc=15) or (bgnd_loc=16) // move peg to trans start or length  212a
                   then begin
                          peg_code:=bgnd_loc;
                          gocalc(0,0);
                        end;

                fix_radius(clrad_at_x(pegx),False);         // set nomrad from centre-line radius at peg.
                gocalc(0,0);
              end;

      old_rad2_orgx:=rad1_orgx;       // 2nd rad and centres on pad..
      old_rad2_orgy:=rad1_orgy;
      rad_2nd:=nomrad;

      peg2x:=pegx_on_pad;                     // and 2nd peg position on pad..
      peg2y:=pegy_on_pad*hand_i+y_datum;

      cen_apart:=SQRT(SQR(old_rad2_orgx-old_rad1_orgx)+SQR(old_rad2_orgy-old_rad1_orgy)); // distance between rad centres.

      if ABS(cen_apart)<0.5   // too close to concentric, 0.5mm arbitrary      // 227a  bug fix
         then begin
                alert(6,'    invalid  transition',
                        'The selected templates are concentric (i.e. both on the same radial centre, or very close).'
                       +'||A transition curve is not possible in this case.',
                        '','','','','cancel','',0);
                error_restore;
                EXIT;
              end;
                    // now change to plain track, no slewing, transition curve...

      plain_track:=True;
      set_plain_track(True,True);

      with pad_form do begin

        adjust_trans_length_menu_entry.Enabled:=True;  // mouse actions...
        adjust_trans_start_menu_entry.Enabled:=True;

        zone_normal_menu_entry.Checked:=True;          // radio item - normal length adjust action.
        transition_template_menu_entry.Checked:=True;  // radio item.

        disable_slewing_menu_entry.Checked:=True;     // radio item.
        slew_nudge_menu_entry.Checked:=False;

        adjust_slew_start_menu_entry.Enabled:=False;
        adjust_slew_length_menu_entry.Enabled:=False;
        adjust_slew_amount_menu_entry.Enabled:=False;
        adjust_slew2_factor_menu_entry.Enabled:=False;

        slewing_panel.Hide;
      end;//with

      with info_form do begin
        slew_warn_panel.Hide;           // cover min rad box.
        min_rad_box.Show;               // restore rad change button...
        limit_rad_box.Show;
      end;//with

      if (ABS(rad_1st-rad_2nd)<minfp)      // 214a mod ...
         then begin
                alert(6,'    invalid  transition',
                        'The selected templates both have the same radius curving in the same direction.'
                       +'||A transition curve can only be created where there is a change in radius or direction.',
                        '','','','','','continue',0);
                error_restore;
                EXIT;
              end;

      got_transition:=False;     // init.
                                 // init for a new control template...
      spiral:=True;
      slewing:=False;
      hand_i:=trans_hand;     // set hand of his choice (rad1 is +ve).

      turnoutx:=SQRT( SQR(peg2x-peg1x)+SQR(peg2y-peg1y) )*1.5;  // arbitrary template length is 1.5 * distance between the pegs.
      xorg:=turnoutx;

      tst:=0;            // arbitrary start zero transition length.
      os:=66*scale;      // arbitrary start 1 chain (scale) initial length.

      nomrad1:=ABS(rad_1st);      // set +ve for first rad.
      nomrad2:=0-ABS(rad_2nd);    // set S curve for starters.

      if ( ABS(nomrad1)>(g*2) ) and ( ABS(nomrad2)>(g*2) )  // min rads (arbitrary).
      and ( cen_apart>(ABS(nomrad1)+ABS(nomrad2)-minfp) )   // ok to try S-curve
         then begin
                wait_cancel_clicked:=False;
                wait_form.cancel_button.Show;
                wait_form.waiting_label.Caption:='calculating ...';

                wait_form.waiting_label.Width:=wait_form.Canvas.TextWidth(wait_form.waiting_label.Caption);  // 205b bug fix for Wine

                pad_form.trans_calc_timer.Tag:=0;        // do the calcs while the wait message shows modal.
                pad_form.trans_calc_timer.Enabled:=True; // one-shot only to start the process.

                do_show_modal(wait_form);  // 212a   ShowModal

                if Application.Terminated=False then Application.ProcessMessages;

                // returns here when the wait form closes...

                case pad_form.trans_calc_timer.Tag of

                  -1: EXIT;  // he cancelled  - leave transition as is.
                // 0: failed - try C-curve instead, below.
                   1: got_transition:=True;
                end;//case
              end;

      if got_transition=False      // now try C-curve instead...
         then begin
                nomrad2:=ABS(nomrad2);     // set C curve.

                temp:=nomrad1-nomrad2;

                if  (ABS(temp)>minfp)
                and (cen_apart<(ABS(ABS(nomrad1)-ABS(nomrad2))+minfp))  // ok to try C-curve
                    then begin
                           wait_cancel_clicked:=False;
                           wait_form.cancel_button.Show;
                           wait_form.waiting_label.Caption:='calculating ...';

                           wait_form.waiting_label.Width:=wait_form.Canvas.TextWidth(wait_form.waiting_label.Caption);  // 205b bug fix for Wine

                           pad_form.trans_calc_timer.Tag:=0;        // do the calcs while the wait message shows modal.
                           pad_form.trans_calc_timer.Enabled:=True; // one-shot only to start the process.

                           do_show_modal(wait_form);  // 212a   ShowModal

                           if Application.Terminated=False then Application.ProcessMessages;

                             // returns here when the wait form closes...

                           case pad_form.trans_calc_timer.Tag of

                             -1: EXIT;  // he cancelled - leave transition as is.
                              0: begin
                                   alert(5,'    calculation  failed',
                                           'Sorry, the transition calculations have failed to produce a result.'
                                          +'||The most likely reason is that the required transition zone length would exceed '+round_str(screenx_max,2)+' mm,'
                                          +' or that the transition curve would be more gentle than the current MAX SPIRAL limit permits.'
                                          +'||Try increasing the difference between the two radii.',
                                           '','','','','','continue',0);
                                   error_restore;
                                   EXIT;
                                 end;
                              1: got_transition:=True;
                            end;//case
                         end
                    else begin
                           if alert(6,'php/201    impossible  transition',
                                      'Sorry, it is not possible to create a transition curve alignment between these two templates.'
                                      +'||The most likely reason is that their centre-lines intersect.'


                                      +'||&nbsp; <IMG SRC="'+exe_str+'internal\hlp\centres_must_not_cross.png">'

                                      +'||Adjust one or both templates until the centre-lines come close together but do not intersect (cross).'
                                      +'||For further explanation and diagrams click `0more information`1 below.',
                                      '','','','more  information','cancel','',4)=4
                              then begin
                                     if help(0,make_trans_help_str,'about  transition  curves')=1 then pad_form.transition_help_menu_entry.Click;
                                   end;
                           error_restore;
                           EXIT;
                         end;
              end;


      turnoutx:=os+tst+66*scale;      // got the transition, set overall length to 1 chain (scale) of final radius (arbitrary).
      if turnoutx>turnoutx_max then turnoutx:=turnoutx_max;
      xorg:=turnoutx;

      startx:=0;         // cancel any blanking.

      retain_on_make;     // 213a  retain for inserted turnout later

      do_rollback:=False;
      gocalc(0,0);
                             // got the template, now align it...

      pad_form.reset_peg_menu_entry.Click;  // peg on CTRL-0.

      do_rollback:=False;
      gocalc(0,0);

      shift_onto_notch(False,False);   // we set the notch earlier on the previous control template.

      do_rollback:=False;
      gocalc(0,0);
                                 // now check the rad1 centres are co-incident...

      error_allow:=0.000001;   // r1 centre not within a radius of .001mm of the original centre (dim is squared).

      if (SQR(old_rad1_orgx-rad1_orgx)+SQR(old_rad1_orgy-rad1_orgy))>error_allow
         then begin
                       // try rotating once

                rotate_turnout(Pi,False);       // rotate template 180 degrees anti-clockwise around peg.
                do_rollback:=False;
                gocalc(0,0);                    // fresh calc sets new radial centres.

                   // check r1 is now on centre...

                if (SQR(old_rad1_orgx-rad1_orgx)+SQR(old_rad1_orgy-rad1_orgy))>error_allow  // r1 centre is still not within a radius of .001mm of the original centre (dim is squared).
                   then begin
                          alert(5,'    transition  calculation  failed',
                                  'Sorry, the transition calculations have failed to produce a result.',
                                            '','','','','','continue',0);
                          error_restore;
                          EXIT;
                        end;
              end;

    end;//with make_trans_data

                         // finally swing it on rad1 until rad2 centres co-incide..

    wait_cancel_clicked:=False;
    wait_form.cancel_button.Show;
    wait_form.waiting_label.Caption:='calculating ...';

    wait_form.waiting_label.Width:=wait_form.Canvas.TextWidth(wait_form.waiting_label.Caption);  // 205b bug fix for Wine

    pad_form.trans_calc_timer.Tag:=9;        // do the calcs while the wait message shows modal.
    pad_form.trans_calc_timer.Enabled:=True; // one-shot only to start the process.

    do_show_modal(wait_form);  // 212a   ShowModal

    if Application.Terminated=False then Application.ProcessMessages;

           // returns here when the wait form closes...

    case pad_form.trans_calc_timer.Tag of

           -1: EXIT;  // he cancelled - leave transition as is.

            0: begin
                 alert(5,'    calculation  failed',
                         'Sorry, the transition calculations have failed to produce a result.',
                         '','','','','','continue',0);
                 error_restore;
                 EXIT;
               end;

          // 1: ok
          end;//case

    clear_current_name;     // finally we have a new control template.
    RESULT:=True;

  finally
    set_current_notch(saved_notch);  // restore his notch.

    wait_form.Close;
    clicked_keep_index:=-1;          // so can popup again.
    do_rollback:=True;

    show_and_redraw(True,True);                       // in case copy caused a current hide.
  end;//try
end;
//___________________________________________________________________________________________

function make_transition_from_current_calcs:integer;

         // return  1= ok,   0= error,   -1= he cancelled.

begin
  RESULT:=0;  // default init.

  do_rollback:=False;
  gocalc(0,0);              // first stab at the new transition.

  RESULT:=do_auto_trans_length_adjust(make_trans_data.cen_apart)  // adjust transition length to match required rad centre-to-centre distance
end;
//__________________________________________________________________________________________

procedure make_transition_click(trans_hand:integer);

var
  i:integer;
  save_index:integer;

  control_loc:integer;  // 0=at peg, 15=at 1st radius, 16=at 2nd radius.  212a
  bgnd_loc:integer;

  dummy:extended;

begin
  if (clicked_keep_index<0) or (clicked_keep_index>(keeps_list.Count-1)) or (keeps_list.Count<1) then EXIT;

  if check_control_template_is_valid('transition')=False then EXIT;  // zero length

  if Ttemplate(keeps_list.Objects[clicked_keep_index]).template_info.keep_dims.box_dims1.align_info.slewing_flag=True
     then begin
            alert(6,'php/201    make  transition  link',
                    'The selected background template contains a slew.'
                   +'||It is not possible to make a transition curve from a slewed template.'
                   +'||If a slew is needed, it should be applied after creating the transition curve.',
                    '','','','','cancel  transition','',0);
            EXIT;
          end;

  if slewing=True
     then begin
            alert(6,'php/201    make  transition  link',
                    'The control template contains a slew.'
                   +'||It is not possible to make a transition curve from a slewed template.'
                   +'||If a slew is needed, it should be applied after creating the transition curve.'
                   +'||To cancel the slewing, click the GEOMETRY > SLEW (NUDGE) > CANCEL SLEW menu item.',
                    '','','','','cancel  transition','',0);
            EXIT;
          end;

  control_loc:=0;   // init for fixed curve..  212a
  bgnd_loc:=0;

  if Ttemplate(keeps_list.Objects[clicked_keep_index]).template_info.keep_dims.box_dims1.align_info.trans_flag=True
     then begin
            repeat
              i:=alert(4,'php/201    make  transition  link',
                         'The selected background template contains a transition curve.'
                        +'||Where on the background template do you want the new transition link to be aligned?',
                         'more  information','background  template :    on  the  1st  radius','background  template :    on  the  2nd  radius','background  template :   at  the  current  peg  location','cancel  making  new  transition','',1);
              case i of

                1: if alert_help(0,make_trans_help_str,'about  transition  curves')=1 then pad_form.transition_help_menu_entry.Click;

                2: bgnd_loc:=15;     // peg code for trans start

                3: bgnd_loc:=16;     // peg code for trans length

                4: begin
                     bgnd_loc:=0;

                     with Ttemplate(keeps_list.Objects[clicked_keep_index]).template_info.keep_dims.box_dims1 do begin

                       if  (transform_info.peg_pos.x>align_info.trans_start)
                       AND (transform_info.peg_pos.x<(align_info.trans_start+align_info.trans_length))

                       then begin
                               if alert(3,'php/201    make  transition  link',
                                          'tree.gif||The peg on the selected background template is currently located within the transition zone.'
                                         +'||You will need to shorten the original template to this location'
                                         +' in order to make a correct boundary with the new control template. Use the `0DO > SNAP TO PEG`1 menu item'
                                         +' (possibly preceded by `0TEMPLATE > SWAP END-FOR-END`1 menu item).',
                                          '','','','','cancel  making  new  transition','continue',0)=5 then EXIT;
                            end;
                     end;//with
                   end;

                else EXIT;
              end;//case

            until i<>1;
          end;

  if spiral=True   // 212a
     then begin
            repeat
              i:=alert(4,'php/201    make  transition  link',
                         'The control template contains a transition curve.'
                        +'||Where on the control template do you want the new transition link to be aligned?',
                         'more  information','control  template :    on  the  1st  radius','control  template :    on  the  2nd  radius','control  template :    at  the  current  peg  location','cancel  making  new  transition','',1);
              case i of

                1: if alert_help(0,make_trans_help_str,'about  transition  curves')=1 then pad_form.transition_help_menu_entry.Click;

                2: control_loc:=15;   // peg code for trans start

                3: control_loc:=16;   // peg code for trans length

                4: begin
                     control_loc:=0;

                     if (pegx>os) AND (pegx<(os+tst))
                        then begin
                               if alert(3,'php/201    make  transition  link',
                                          'tree.gif||The peg is currently located within the transition zone on the control template.'
                                         +'||You will need to shorten the original template to this location'
                                         +' in order to make a correct boundary with the new control template. Use the `0DO > SNAP TO PEG`1 menu item'
                                         +' (possibly preceded by `0TEMPLATE > SWAP END-FOR-END`1 menu item).',
                                          '','','','','cancel  making  new  transition','continue',0)=5 then EXIT;
                             end;
                   end;

                else EXIT;
              end;//case

            until i<>1;
          end;

          //-----------

  save_index:=clicked_keep_index;

  if make_transition_from_current(control_loc,bgnd_loc,trans_hand)=True    // first try one hand.
     then begin
            show_and_redraw(False,False);                 // force a draw.
            pad_form.fit_current_only_menu_entry.Click;   // see all of it.

            repeat

              with alert_box do begin          // 205d
                left_panbutton.Visible:=True;
                right_panbutton.Visible:=True;
                down_panbutton.Visible:=True;
                up_panbutton.Visible:=True;
              end;//with

              alert_option2a_click_code:=1;    // zoom in   205d
              alert_option2b_click_code:=2;    // zoom out

              i:=alert(4,'php/202    make  transition  link',
                         'Is this the required transition curve?',
                         '_1anotick_delete the original templates','_2a_+  zoom in_2b_-  zoom out','more  information','no  -  try  opposite  hand  instead','cancel  transition','yes  -  continue',3);
              case i of
                     3: if alert_help(0,make_trans_help_str,'about  transition  curves')=1 then pad_form.transition_help_menu_entry.Click;

                     4: begin
                          list_position:=keeps_list.Count-1;               // make one just added current in the keeps box.
                          copy_keep_to_current(False,False,False,False);   // copy it back to pad.
                          delete_keep(False,False);                        // remove it.

                          gocalc(0,0);
                          trans_hand:=0-trans_hand;
                          clicked_keep_index:=save_index;
                          if make_transition_from_current(control_loc,bgnd_loc,trans_hand)=False then EXIT;

                          redraw_pad(False,False);                      // force a draw.
                          pad_form.fit_current_only_menu_entry.Click;   // see all of it.
                          i:=3;                                         // ask again
                        end;

                     5: begin
                          list_position:=keeps_list.Count-1;               // make one just added current in the keeps box.
                          copy_keep_to_current(False,False,False,False);   // copy it back to pad.
                          delete_keep(False,False);                        // remove it.
                          redraw_pad(True,False);
                        end;

                     6: begin
                          if alert_box.option1a_checkbox.Checked=True    // 205d  delete underlying templates
                             then begin
                                    list_position:=keeps_list.Count-1;     // make one just added current in the keeps box.
                                    delete_keep(False,False);              // remove it.

                                    list_position:=save_index;     // and the clicked one
                                    delete_keep(False,False);      // remove it

                                  end;

                              // 212a kludge. restore easement straights...

                          if ABS(nomrad1)>(max_rad_test-2)
                             then begin
                                    kform_now:=kform;
                                    docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy);    // save current peg data for peg_curve calcs.

                                    nomrad1:=(max_rad)*SGZ(nomrad1);  // kludge - ensure now treated as straight again.

                                    peg_curve;      //  keep it on peg..
                                    gocalc(0,0);
                                  end;

                          if ABS(nomrad2)>(max_rad_test-2)
                             then begin
                                    kform_now:=kform;
                                    docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy);    // save current peg data for peg_curve calcs.

                                    nomrad2:=(max_rad)*SGZ(nomrad2);  // kludge - ensure now treated as straight again.

                                    peg_curve;      //  keep it on peg..
                                    gocalc(0,0);
                                  end;

                          redraw_pad(True,True);
                        end;

              end;//case
            until i>3;

          end;
end;
//________________________________________________________________________________________

function do_auto_trans_length_adjust(apart_len_wanted:extended):integer;

      // adjust transition zone length until the distance between the rad centres is apart_len_wanted.
      // return 1 if achieved, 0 if calc error, -1 if he cancels.

      // !!! N.B. apart_len_wanted and transition calcs are based on transition datum, not template datum, and ignore any slewing.

var
  dummy1,dummy2,dummy3,dummy4,dummy5:extended;

  now_apart:extended;
  new_zone_len,zone_step:extended;
  dir:integer;

  temp, temp_ktrans:extended;

begin
  Screen.Cursor:=crHourglass;
  RESULT:=0;  // default init.
  try
    new_zone_len:=0;        // start with a transition length of zero.
    zone_step:=scale;       // and step size 1ft (scale) (arbitrary).

       // first find which way the centres-apart distance is moving...
       // (now_apart ignores any slewing).

    if calc_transition(nomrad1,nomrad2,new_zone_len,dummy1,dummy2,dummy3,dummy4,now_apart,dummy5)=False
       then begin
              EXIT;
            end;

    if ABS(now_apart-apart_len_wanted)<sep_limit    // already matches, do nothing.
       then begin
              tst:=new_zone_len;     // new transition zone length = 0.
              RESULT:=1;
              EXIT;
            end;

    if now_apart>apart_len_wanted then dir:=-1
                                  else dir:=1;
    repeat
      if calc_transition(nomrad1,nomrad2,new_zone_len,dummy1,dummy2,dummy3,dummy4,now_apart,dummy5)=False
         then begin
                EXIT;
              end;

      if ABS(now_apart-apart_len_wanted)<sep_limit     // matching centre-to-centre dim?
         then begin
                tst:=new_zone_len;     // set new transition zone length to give wanted rad centre-to-centre distance.
                RESULT:=1;
                EXIT;
              end;

      if ( (dir=1)  and (now_apart>apart_len_wanted) )
      or ( (dir=-1) and (now_apart<apart_len_wanted) )
         then begin
                new_zone_len:=new_zone_len-zone_step*2;     // 226c back track on previous 2 steps   was 1 step
                if new_zone_len<0 then new_zone_len:=0;     // can't be negative.
                zone_step:=zone_step/10;                    // and reduce step size.
              end;

      new_zone_len:=new_zone_len+zone_step;                 // step forward again

      if Application.Terminated=False then Application.ProcessMessages;  // allow him to click.

      if wait_cancel_clicked=True
         then begin
                RESULT:=-1;       // he clicked cancel.
                EXIT;
              end;

    until new_zone_len>screenx_max;   // we need a limit of some sort.

  finally
    temp:=nomrad1-nomrad2;      // 29-7-01 check any transition returned is not too gentle.
    if ABS(temp)<minfp
       then RESULT:=0
       else begin
              temp_ktrans:=nomrad1*nomrad2*tst/temp;     // new spiral constant
              if ABS(temp_ktrans)>max_spiral_constant
                 then begin
                        tst:=0;       // no good.
                        RESULT:=0;
                      end;
            end;

    Screen.Cursor:=crDefault;
  end;//try
end;
//________________________________________________________________________________________

function do_auto_trans_start_adjust(old_rad1_orgx, old_rad1_orgy, old_rad2_orgx, old_rad2_orgy:extended):integer;

     // this code moves the transition start (initial length os) to and fro in reducing steps
     // until the new position of the 2nd rad centre co-incides with the old.
     // The 1st rad centres must be co-incident, and the peg must be in the initial length,
     // so that re-pegging after each calc keeps the 1st rad centres co-incident.
     // The transition zone length must also have been adjusted so that the separation distance
     // between the two centres (ignoring slewing) is the same as before.

     // The rad centres are calculated ON THE PAD, i.e. including slewing.

     // Return 1 if valid result, 0 if calc error, or -1 if he clicks cancel.


var
  xdiff2,ydiff2:extended;
  prev_diff2_sq,next_diff2_sq:extended;

  xdiff1,ydiff1:extended;
  diff1_sq:extended;

  dir,os_step,dummy:extended;

begin

  RESULT:=0;   // default init

  do_rollback:=False;
  gocalc(0,0);     // get new peg calcs.

  xdiff2:=rad2_orgx-old_rad2_orgx;
  ydiff2:=rad2_orgy-old_rad2_orgy;

  prev_diff2_sq:=SQR(xdiff2)+SQR(ydiff2);  // square of distance apart of new and old r2 centres.


  kform_now:=kform;
  docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy);    // save current peg data for peg_curve calcs.


  dir:=1;                // start by going forward (arbitrary direction).
  os_step:=10*scale;     // 10ft scale arbitrary  initial step-size.

  repeat

    do_rollback:=False;
    os:=os+os_step;   // step along...
    peg_curve;        // calls calc_curving and maintains peg position for new os.
    gocalc(0,0);      // fresh calc sets new radial centres.

         // check r1 is still on centre...

    xdiff1:=rad1_orgx-old_rad1_orgx;       // how far is the rad1 centre off the original position?
    ydiff1:=rad1_orgy-old_rad1_orgy;

    diff1_sq:=SQR(xdiff1)+SQR(ydiff1);

    if diff1_sq>0.000001                  // r1 centre not within a radius of .001mm of the original centre (dim is squared).
       then begin                         // so try rotating once (shift onto notch may have failed)..
              rotate_turnout(Pi,False);   // rotate template 180 degrees anti-clockwise around peg.
              do_rollback:=False;
              gocalc(0,0);                // fresh calc sets new radial centres.

                   // check r1 is now on centre...

              xdiff1:=rad1_orgx-old_rad1_orgx;       // how far is the rad1 centre now off the original position?
              ydiff1:=rad1_orgy-old_rad1_orgy;

              diff1_sq:=SQR(xdiff1)+SQR(ydiff1);

              if diff1_sq>0.000001 then EXIT;  // r1 centre is still not within a radius of .001mm of the original centre (dim is squared).
            end;

    xdiff2:=rad2_orgx-old_rad2_orgx;       // how far is the rad2 centre off the original position?
    ydiff2:=rad2_orgy-old_rad2_orgy;

    next_diff2_sq:=SQR(xdiff2)+SQR(ydiff2);

    if next_diff2_sq<0.0001
       then begin
              RESULT:=1;  // r2 centre now within a radius of .01mm of the original centre (dim is squared).
              EXIT;
            end;

    if next_diff2_sq>prev_diff2_sq       // gone too far (difference more than last time)?
       then begin
              os_step:=0-os_step/10;     // change direction and use smaller steps.
              dir:=0-dir;
            end;
                      // try again..

    prev_diff2_sq:=next_diff2_sq;

    if Application.Terminated=False then Application.ProcessMessages;  // allow him to click.

    if wait_cancel_clicked=True   // exit calcs but leave os where we are.
       then begin
              RESULT:=-1;
              EXIT;
            end;

  until ( (dir=1) and (os>screenx_max) )
     or ( (dir=-1) and (os<(0-screenx_max)) );   // will take forever, but we must have a limit of some sort.
end;
//______________________________________________________________________________

procedure unlock_both;

begin
  pad_form.unlock_both_popup_entry.Checked:=True;  // radio item
  switch_free:=True;
  xing_free:=True;
end;
//_________________________________________________________________________________________

function set_black_and_white:boolean;

        // return True if he clicks printer setup... , otherwise False.

var
  i,colour_depth_bits:integer;

begin
  RESULT:=False;    // init.

  // OT2024  colour_depth_bits:=GetDeviceCaps(Printer.Canvas.Handle,BITSPIXEL)*GetDeviceCaps(Printer.Canvas.Handle,PLANES);
  colour_depth_bits:=24;  // OT2024  kludge

  if colour_depth_bits>1
     then begin
            i:=alert(3,'    black & white  on  colour  printer',
                       'You have selected the black & white only option for your template printing. Your current printer is a colour printer, you may get better black & white results with grey-shades printing.'
                      +'||To print in black & white on some models of colour printer, you may also need to change a switch or setting on the printer itself, or in the printer setup dialog.'
                      +'||Single-cartridge printers may also need to have the ink cartridge changed.'
                      +'||Templot cannot make these changes for you.'
                      +'||If the printed templates are unsatisfactory, please refer to the documentation for your printer.',
                       '','','','printer  setup ...','cancel','O K  -  black && white  printing',0);
            case i of
                4: begin
                     printer_setup(False,True);
                     RESULT:=True;
                   end;

                5: EXIT;
            end;//case
          end;

  black_white:=True;
  pad_form.printed_grid_dotted_menu_entry.Click;

  grey_shade:=False;

  print_form.reset_button.Click;


  pad_form.black_and_white_printing_menu_entry.Checked:=True;   //  radio item
end;
//_________________________________________________________________________________________

procedure pt_convert(show_it:boolean);   // convert turnout to plain track on same alignment.

begin
  plain_track:=True;
  set_plain_track(True,True);       // cancels any blanking.

  xorg:=turnoutx;                   // use existing length.

  isolated_crossing:=False;     // 217a

  delete_all_symbols;     // 227a..

  with pad_form do begin
    if peg_code=0
       then reset_peg_menu_entry.Click
       else begin
              peg_code:=20;                             // not on reset, so keep peg wherever it is.
              set_peg_menu_entry.Checked:=True;
              peg_indicator_panel.Caption:='F';         // show him its free.
            end;

    if show_it=True then show_and_redraw(True,True);         // in case hidden.
  end;//with
end;
//___________________________________________________________________________________________

procedure invert_curving;

var
  clrad,clrad1,clrad2,dummy:extended;

begin
  cancel_adjusts(False);  // 0.93.a may be from toolbutton

  if spiral=False
     then begin
            kform_now:=kform;
            docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy);    // save current peg data for peg_curve calcs.
            clrad:=nomrad;      // change sign of centre-line radius..
            clrad:=0-clrad;
            nomrad:=clrad;      // set the new radius.
          end
     else begin                   // transition template...
            kform_now:=kform;
            docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy);    // save current peg data for peg_curve calcs.

            clrad1:=nomrad1;      // change sign of centre-line 1st radius.
            clrad1:=0-clrad1;

            clrad2:=nomrad2;      // change sign of centre-line 2nd radius.
            clrad2:=0-clrad2;

            nomrad1:=clrad1;      // set the new radii.
            nomrad2:=clrad2;
          end;

  if slewing=True then slew:=0-slew;   // need to swap the hand of any slewing also.
  peg_curve;                           // do curving calcs for the current peg position.
end;
//_____________________________________________________________________________________

procedure invert_handing;

begin
 invert_curving;
 gocalc(0,0);                              // for peg calcs.
 swap_hand;
end;
//________________________________________________________________________________________

procedure error_b6_lh_reset;

begin
  plain_track:=False;                               //  False = turnouts,   True = plain track
  set_plain_track(False,True);                      //  False = turnout. Set up menu options.

  xorg:=0;                                          // origin to rail-joint (turnouts)    *

  half_diamond:=False;                              // 0.77.a 19-8-02  normal switch calcs.
  hd_switch_timbering:=False;

  hand_i:=1;                                        //  default left-hand turnout.

  spiral:=False;       // no transition.
  slewing:=False;      // no slew
  reset_trans;         // set transition defaults.

  incx:=def_req;       // increment for x mm.

  if set_csi_data(2,2)=False  // set REA size B (group 2, size 2).
     then run_error(82);

  k3n:=6;              // 1:6 crossing             *
  xing_k_i:=7;         // 1:6 crossing list index  *
  xing_type_i:=0;      // straight crossing         *
  wing_tbcount:=3;     // 3 crossing timbers in front of A timber.
  retpar_i:=0;         // no return curve          *
  xing_list_i:=0;      // straight crossing type list index *
  xing_ret_i:=0;       // return curve centres as adjacent track. *

  entry_straight_code:=0;        // sl radio buttons default *
  fixed_sl:=def_req;             // init fixed sl dim.

  pt_i:=3;             // 0.93.a default now 60ft rails / 25 sleepers

  turnoutx:=scale*132; // default overall length of turnout 2 chains
  startx:=0;           // no blanking. default starting point. (f28000 list entry limit) 1-11-99.
  turnout_i:=0;        // length free.

  turnout_road_i:=0;                                              // normal turnout road exit length.
  pad_form.snap_exit_to_return_curve_menu_entry.Enabled:=False;   // no return curve.

  xform:=0;                            // zero transform data.
  yform:=0;
  kform:=0;
  xshift:=0;
  yshift:=0;

  pad_form.reset_peg_menu_entry.Checked:=True;       // radio item.
  peg_code:=0;
  pegx:=0;
  pegy:=g/2;

  exittb_i:=2;          // exit is sleepering       *

  calcs_done_and_valid:=False;    // no calcs done yet.

  adjacent_edges:=True;    // 0.93.a   False=adjacent tracks,  True=trackbed edges and platform edges.

           // trackbed ...

  trackbed_ms_width_ins:=90;       // 180 inches total full-size 15ft.      215a
  trackbed_ts_width_ins:=90;

  draw_ms_trackbed_edge:=False;
  draw_ts_trackbed_edge:=False;

  cess_ms_width_ins:=27;            // 215a
  cess_ts_width_ins:=27;            // 215a

  draw_ms_trackbed_cess_edge:=False;    // 215a
  draw_ts_trackbed_cess_edge:=False;    // 215a


           // platforms ...

  platform_ms_front_edge_ins:=57;        // 4ft-9in default
  platform_ts_front_edge_ins:=57;

  platform_ms_start_width_ins:=144;      // 12ft default
  platform_ms_end_width_ins:=144;        // 12ft default

  platform_ts_start_width_ins:=144;      // 12ft default
  platform_ts_end_width_ins:=144;        // 12ft default

  platform_ms_start_mm:=0;
  platform_ms_length_mm:=def_req;

  platform_ts_start_mm:=0;
  platform_ts_length_mm:=def_req;

  platform_ms_start_skew_mm:=0;      // 207a
  platform_ms_end_skew_mm:=0;        // 207a

  platform_ts_start_skew_mm:=0;      // 207a
  platform_ts_end_skew_mm:=0;        // 207a


  draw_ms_platform:=False;

  draw_ms_platform_rear_edge:=True;
  draw_ms_platform_start_edge:=True;
  draw_ms_platform_end_edge:=True;

  draw_ts_platform:=False;

  draw_ts_platform_rear_edge:=True;
  draw_ts_platform_start_edge:=True;
  draw_ts_platform_end_edge:=True;

  gauge_faces:=True;                               // gauge-faces wanted
  outer_edges:=True;                               // and outer-edges
  midline:=False;                                  // but not rail-centrelines.

  railedges(gauge_faces,outer_edges,centre_lines); // set up these flags.
  reset_defaults;     // do resettable defaults ( on def_req input )

  show_and_redraw(False,True);

end;
//_______________________________________________________________________________________

procedure fill_mark(p1,p2:TPoint; code:integer; num_str:string);   // enter this mark in list.

   // also called from  enter_mark();
{
     Tmark=record              // mark from p1 to p2.
          p1:TPoint;
	  p2:TPoint;
          code:integer;
	  tb_code:integer;      // 235b for timber numbers converted to character and number
          dxf_code:integer;     // 237a  chairing data for 3D exports
          options_bits:integer; // 555a
        end;
}

var
  ptr:^Tmark;          // pointer to a Tmark record.
  markmax:integer;
  c:char;

begin

  if Length(marks_list) = 0 then EXIT;        // pointer to marks list not valid.
  markmax:=High(marks_list);  // max index for the present list.

  if mark_index>markmax   // increase the length...
     then begin
            SetLength(marks_list, Length(marks_list)+500);
            markmax:=High(marks_list);
          end;

  ptr:=@marks_list[mark_index];  // pointer to the next Tmark record.

  ptr^.code:=code;                     // code for this entry :

    // -1=p1 is fixing peg centre (p2=0) (pad only),
    // -2=p1 is curving rad 1 centre (p2=0),
    // -3=p1 is curving rad 2 centre (p2=0),
    // {-4=p1 is timber selector mark (p2=0) (pad only). !!! removed 5-6-00}


    // 0=ignore, 1=guide mark,  2=rad end mark, 3=timber outline, 4=timber centre-line,
    // 5=timber reduced end,    6=rail joint mark,   7=transition marks,  8,9=peg arms,
    // 99=timber number string.
    // 101=switch drive mark 223d

                   // negative mark codes ...

                   //  if code negative, single point mark and p2 contains other info...

                 //  if code= -1 this is the fixing peg (but not the arms which are code= 8)
                 //  if code= -2,-3 this is the rad centre marks.

                 //  {if code= -4, this is the timber selector mark.!!! removed 5-6-00}

                 //  -4 = kerf lines for laser cutting

                 //  if code= 7, this is transition mark.

                 {
                 chair labels:
                 441..458      // as below-20 = label currently selected  556b MW

                 461,471 = interchangeable chair labels
                 462,472 = switch block chair labels
                 463,473 = V-crossing chair labels
                 464,474 = K-crossing chair labels
                 ... ... other specials
                 468,478 = SC customizable chair labels
                 }


                 // code 479   dropper wire ridge marks   // 243b

                 // code 480   normal chair key right-hand rail
                 // code 481   normal chair key left-hand rail (rotate 180 degs)

                 // code 482   check-rail chair key right-hand rail   also wing rails
                 // code 483   check-rail chair key left-hand rail (rotate 180 degs)   also wing rails

                 // code 484   X crossing chairs outline

                 // code 485   SC-CC  chair outline    check rail

                 // code 486   plug clip-fit tang

                 // code 487   switch drive slider rib  241c

                 // code 488   middle chair key right-hand rail (switch block chairs)
                 // code 489   middle chair key left-hand rail (rotate 180 degs)

                 // code 490   timber outer flange for 3-D

                 // code 491   chair plug                    // 227a
                 // code 492   3-D chair socket and chamfer  // 228a    and undercut 241b

                 // code 493   L1 chair outline, radiused corners, plinth

                 // code 494   chair outline, radiused corners, plinth    // 221a
                 // code 495   chair bolt centre marks across X
                 // code 496   chair bolt centre marks across Y

                 // code 497   P chair outline, radiused corners, plinth

                 // code 498   chair socket for 2-D

                 // code 499   slot for loose jaw pin  not used in DXF  236d

                 // code 501..508 = check rail labels  // 0.94.a

                 // code 601  toe tips label       // 206b
                 // code 602  set mark label       // 206b
                 // code 603  planing end label    // 206b
                 // code 604  stock gauge          // 206b
                 // code 605  joggle return        // 206b

                 // code 701  FP mark label             // 211b
                 // code 702  blunt nose label          // 211b
                 // code 703  half-diamond tips label   // 211b

                 // code 600  long toe mark          // 206b
                 //           long set mark          // 206b
                 //           long planing           // 206b
                 //           long stock gauge mark  // 206b

                 // code 700  long FP mark           // 211b
                 //           long blunt nose mark   // 211b

// 237c out...
// 1000+chair type code   rail seats, left-hand keys  221a
// 2000+chair type code   right-hand keys  221a

// 3000+chair type code   outer chair jaws
// 5000+chair type code   inner chair jaws ...6999


                 // 9996 2-D timber outlines with laser kerf adjustment
                 // 9997 2-D socket outlines with laser kerf adjustment
                 // 9998 used for 3D timber webs
                 // 9999 used for 3D timber sprues


  ptr^.p1.X:=p1.X;             // fill the MoveTo data.
  ptr^.p1.Y:=p1.Y;

  ptr^.p2.X:=p2.X;             // fill the LineTo data or other info.
  ptr^.p2.Y:=p2.Y;

    // 237a fill timber number, DXF chair code, option bits ...

  ptr^.tb_code:=Ord(timb_str[1])*10000+tbn;   // global   prefix for current timber number   +    // timber number
  ptr^.dxf_chair_code:=dxf_code;              // global   237a  chairing data for 3D exports
  ptr^.mark_bits:=bit_settings;               // global   MW 04-08-2024

  if code=99
     then timb_numbers_str:=timb_numbers_str+num_str+Chr($1B);  // add number string and separator to the accumulator string.

  INC(mark_index);                                              // bump index for next.
end;
//_______________________________________________________________________________________

function curve_point(p:Tpex):Tpex;

var
  x,y,t,r:extended;

begin                                     // return point p curved onto rad and transformed.
  docurving(True,True,p.x,p.y,x,y,t,r);   // t, r not used here.
  RESULT.x:=x;
  RESULT.y:=y;
end;
//_________________________________________________________________________________________

function convert_point(p:Tpex):Tpoint;     // input in mm f.p. , convert to 1/100ths mm. integer.

begin
       //  go convert to scale/hand, ensure all inputs are within range for 32-bits,
       //  do re-origination and distortions, round off and convert to integer.

  RESULT:=xy_to_list(p);
end;
//_______________________________________________________________________________________________________________________________

procedure enter_mark(track:boolean; p1,p2:Tpex; code:integer; num_str:string);    //  main mark entry point. dims in mm (floats)

		 //  enter with points p1 and p2 in mm floats.
		 //  first curve it, then enter it in list.
		 //  if track=False, this is part of background drawing etc (not actually used).

                 //  if code negative, single point mark and p2 contains other info.
                 //  if code= -1 this is the fixing peg (but not the arms which are code= 8)
                 //  if code= -2,-3 this is the rad centre marks.

                 //  if code= -4, not used  (was timber selector)

                 //  if code= 6, this is a rail-joint mark

                 //  if code= 7, this is transition mark.

                    {
                    chair labels:
                    441..458      // as below-20 = label currently selected  556b MW

                    461,471 = interchangeable chair labels
                    462,472 = switch block chair labels
                    463,473 = V-crossing chair labels
                    464,474 = K-crossing chair labels
                    ... ... other specials
                    468,478 = SC customizable chair labels
                    }

                 // code 480   normal chair key right-hand rail
                 // code 481   normal chair key left-hand rail (rotate 180 degs)

                 // code 482   check-rail chair key right-hand rail   also wing rails
                 // code 483   check-rail chair key left-hand rail (rotate 180 degs)   also wing rails

                 // code 484   X crossing chairs outline

                 // code 485   SC-CC chair outline    check rail

                 // code 488   middle chair key right-hand rail (switch block chairs)
                 // code 489   middle chair key left-hand rail (rotate 180 degs)

                 // code 490   timber outer flange for 3-D

                 // code 493   L1 chair outline, radiused corners, plinth

                 // code 494   chair outline, radiused corners, plinth    // 221a
                 // code 495   chair bolt centre marks across X
                 // code 496   chair bolt centre marks across Y

                 // code 497   P chair outline, radiused corners, plinth

                 // code 501..508 = check rail labels  // 0.94.a

                 // code 601  toe tips label       // 206b
                 // code 602  set mark label       // 206b
                 // code 603  planing end label    // 206b
                 // code 604  stock gauge          // 206b
                 // code 605  joggle return        // 206b

                 // code 701  FP mark label        // 211b
                 // code 702  blunt nose label     // 211b
                 // code 703  half-diamond tips label     // 211b

                 // code 600  long toe mark          // 206b
                 //           long set mark          // 206b
                 //           long planing           // 206b
                 //           long stock gauge mark  // 206b

                 // code 700  long FP mark           // 211b
                 //           long blunt nose mark   // 211b

{237c out        // 1000+chair type code   DXF block markers, left-hand keys  221a
                 // 2000+chair type code   DXF block markers, right-hand keys  221a

                 // 3000+chair type code   DXF block markers, outer chair jaws
                 // 5000+chair type code   DXF block markers, inner chair jaws ...6999
}
                 // 9996 2-D timber outlines with laser kerf adjustment
                 // 9997 2-D socket outlines with laser kerf adjustment
                 // 9998 used for 3D timber webs
                 // 9999 used for 3D timber sprues


    // !!! timbering outlines and centres are no longer done here, but numbering is (0.76.a 24-10-01).

var
  info:TPoint;   //  (use a TPoint for the info for convenience - not actually a point).
  dummy_i:integer;

begin
  if code<0 then begin
                    info.X:=Round(limits(minint,maxint,p2.x,dummy_i));     // p2 contains other info or none.
                    info.Y:=Round(limits(minint,maxint,p2.y,dummy_i));     // info must be integer to go in marks list.

                    fill_mark(convert_point(curve_point(p1)),info,code,''); // single-point mark on p1.   and info in p2.

                  end

             else begin

                      // first do any blanking...

                    if (plain_track=False) and (code=99) and (track=True)
                       then begin
                              if (Copy(num_str,1,1)<>'B') and (Copy(num_str,1,1)<>'R') and (p1.x<(startx-6*inscale)) then EXIT;  // blank timber numbering (not plain track or bonus timbers 0.76.a) .
                              if (Copy(num_str,1,1)='R') and (p1.x<(startx-30*inscale)) then EXIT;
                            end;

                    if (p1.x<(startx-minfp_big)) and (p2.x<(startx-minfp_big)) and (track=True)  // not timbering, these are joint marks, etc.
                       then begin
                              case code of
                                   1,2,3,4,5,6,14,33,44,54,55,93,95,203,233,293,460..499,501..508,600..605,700..703,1000..4999: EXIT;    // blank nearly everything.
                              end;//case
                            end;


                       // and no unnecessary marks beyond end of template (0,3,4 = timbers, etc, checked elsewhere)...
                       // (allow 1ft beyond railends for angled joint marks, etc.)

                    if ( (p1.x>(turnoutx+scale)) or (p2.x>(turnoutx+scale)) ) and (track=True)
                       then begin
                              case code of
                                   1,2,6,101,460..499,501,504,507,508,{501..508,}600..605,700..703,1000..4999: EXIT;    // no guide marks or joints.     227a   501,504,507,508 check rail labels, working ends and K-crossing
                              end;//case
                            end;

                    if (turnoutx<MIN(ckx_ms,cuckx)) and (track=True)    // 227a       check rail starts MS,TS
                       then begin
                              case code of
                                   502,503,505,506: EXIT;   // check and wing extension labels
                              end;//case
                            end;

                    case code of    // 99 timber numbers
                        99: begin
                              fill_mark(convert_point(curve_point(p1)),   // screen number string location in p1,
                                        convert_point(curve_point(p2)),   // output number string location in p2  // 208a
                                        code,num_str);
                            end;

         501..508,601..605: begin           // 0.94.a  501..508 check labels added.   601-605 added 206b switch labels.  !!! 701,702,703 crossing labels entered directly  211b
                              info.X:=0;
                              info.Y:=0;

                              fill_mark(convert_point(curve_point(p1)),
                                        info,code,'');                   // zero in p2.
                            end;


                       else fill_mark(convert_point(curve_point(p1)),   // normal mark with p1,p2 end points.
                                      convert_point(curve_point(p2)),
                                      code,'');
                    end;//case

                  end;

end;
//__________________________________________________________________________________________

procedure add_check_labels;          // 0.94.a   enter check-rail end labels

var
  p1,p2:Tpex;
  dummy_k:extended;

begin
  p2.x:=0;    // not used.
  p2.y:=0;

  if main_road_check_rail_flag=True
     then begin
            p1.x:=ckx_ms;
            p1.y:=g/3;
            enter_mark(True,p1,p2,501,'');  // main-side working end

            p1.x:=ckendx;
            p1.y:=g/3;
            enter_mark(True,p1,p2,502,'');  // main-side extension end
          end;

  if turnout_road_crossing_rail_flag=True
     then begin
            p1.x:=wingendox;
            p1.y:=g*2/3;
            enter_mark(True,p1,p2,503,'');  // main-side wing rail
          end;

  if turnout_road_check_rail_flag=True
     then begin
            p1.x:=cuckx;
            p1.y:=aq3offset(p1.x,dummy_k)-g/3;  // curved stock rail offset
            enter_mark(True,p1,p2,504,'');      // turnout-side working end

            p1.x:=cuckendx;
            p1.y:=aq3offset(p1.x,dummy_k)-g/3;
            enter_mark(True,p1,p2,505,'');      // turnout-side extension end
          end;

  if main_road_crossing_rail_flag=True
     then begin
            p1.x:=flcendox;
            p1.y:=aq2offset(p1.x,dummy_k)+g/3;
            enter_mark(True,p1,p2,506,'');      // turnout-side wing rail
          end;

  if (half_diamond=True) and (fixed_diamond=True)
     then begin
            if k_main_side_check_rail_flag=True
               then begin
                      p1.x:=kckmsflendox;
                      p1.y:=aq2offset(p1.x,dummy_k)-g/8;
                      enter_mark(True,p1,p2,507,'');      // main-side K check rail
                    end;

            if k_diagonal_side_check_rail_flag=True
               then begin
                      p1.x:=kckdsflendox;
                      p1.y:=g*9/8;
                      enter_mark(True,p1,p2,508,'');     // diagonal-side wing rail
                    end;
          end;
end;
//______________________________________________________________________________

procedure guidemarks;     // calculate all the guide marks.

var
  p1,p2,dummy_p:Tpex;

  joggle_labels_p1,joggle_labels_p2:Tpex;    // 206b

  temp,tempx,tempy,tempcos,tempsin,tempk:extended;
  gmi,gmo,gm,k,arm_long:extended;

  gm_swlabels_ms,gm_swlabels_ts:extended;  // 206b

  gm_xinglabels_ms,gm_xinglabels_ts:extended;  // 211b

  gm_hdlabels_ms,gm_hdlabels_ts:extended;  // 211b

  k_irreg:extended; // 211b

  xtb,xtb_hd:extended;
  sw2,tbe,tbh,tbw,kwl:extended;     // 226c tbh

  pin,pout:Tpex;

  ms_loop:extended;    // 216b

  veelong_tv,veelong_mv,veelong_ts,veelong_ms:extended;  // 227a

begin
  if fb_kludge<>0 then EXIT;   // 208a guide marks not wanted on FB foot templates

  dummy_p.x:=0;    // 206b init
  dummy_p.y:=0;    // 206b init

  p1.x:=pegx;          // first do the fixing peg...
  p1.y:=pegy;

  p2.x:=0;    // not used for peg.
  p2.y:=0;

  enter_mark(True,p1,p2,-1,'');      // fixing peg - goes on the pad only.

                 // calculate peg arm marks...

  docurving(True,True,pegx,pegy,p1.x,p1.y,tempk,temp);    // curve and transform peg to get position.

  arm_long:=peg_arm_length;

  p2.x:=p1.x-arm_long*COS(arm_angle);
  p2.y:=p1.y-arm_long*SIN(arm_angle);

  fill_mark(convert_point(p1),convert_point(p2),8,'');   // first arm.

  p2.x:=p1.x+arm_long*COS(arm_angle);
  p2.y:=p1.y+arm_long*SIN(arm_angle);

  fill_mark(convert_point(p1),convert_point(p2),9,'');   // second arm.

     // next comes the label position...

  if plain_track=False
     then begin
            if half_diamond=True
               then p1.x:=(dpx+fpx*2)/3   // half-diamond label top-left. (dpx+ 2/3 of lead to fpx).
               else p1.x:=dpx;            // turnout label top-left at deflection point.
            p1.y:=g/2;
          end
     else begin
            p1.x:=xorg/2;     // for plain track label at mid point.
            p1.y:=g/2;
          end;

  if p1.x>(turnoutx*3/4) then p1.x:=turnoutx*3/4;   // 0.82.a   not more than 3/4 of template length.

  p2.x:=0;   // not used.
  p2.y:=0;

  enter_mark(True,p1,p2,-5,'');

                 // now do other marks...

  gm:=4*inscale;       // 8" overall size of centre-line guide-marks.

  gmi:=5*inscale;      // 5" scale length of mark inside gauge-face.
  gmo:=gmi;            // 5" scale ditto beyond outer-edge.

  gm_swlabels_ms:=g/2+42*inscale;   // long toe and planing marks (to clear fixed timbers and numbers) 206b
  gm_swlabels_ts:=g/2+42*inscale;   // long set and stock gauge marks (to clear fixed timbers) 206b

  gm_xinglabels_ms:=g+66*inscale;  // long FP and blunt nose mark (to clear fixed timbers and numbers) 211b
  gm_xinglabels_ts:=g+75*inscale;  // long FP and blunt nose mark (to clear fixed timbers) 211b

  gm_hdlabels_ms:=g/2+36*inscale;  // long HD blunt tips mark (to clear fixed timbers and numbers) 211b
  gm_hdlabels_ts:=g/2+54*inscale;  // long HD blunt tips mark (to clear fixed timbers) 211b


  if (guide_marks=True) and ((ABS(nomrad)<max_rad_test) or (spiral=True)) and (turnoutx<>0)  // 0.93.a  (turnoutx<>0) added
     then begin            // rad centre markers. don't call enter_mark - no curving wanted.

            if   ( (spiral=True) and (ABS(nomrad1)<1.0E6) )    // 1E6 arbitrary max radius for marking centres (mm).
              or ( (spiral=False) and (ABS(nomrad)<1.0E6) )
                 then begin
                                          // rad 1 centre marker...    (p2=0)
                        pin.x:=xt1;
                        pin.y:=yt1;
                        dotransform(kform,xform,yform,pin,pout);

                        pout.x:=pout.x+xshift;
                        pout.y:=pout.y+yshift;

                        p1:=pout;

                        p2.x:=0;              // not used.
                        p2.y:=0;

                        fill_mark(convert_point(p1),convert_point(p2),-2,'');   // rad 1 mark location

                        if slewing=True     // centre(s) of slewed over portion ...
                           then begin
                                                             // slewed rad 1 centre marker...    (p2=0)
                                  pin.x:=xt1-slew*SIN(slew_t);
                                  pin.y:=yt1+slew*COS(slew_t);

                                  dotransform(kform,xform,yform,pin,pout);

                                  pout.x:=pout.x+xshift;
                                  pout.y:=pout.y+yshift;

                                  p1:=pout;

                                  p2.x:=0;              // not used.
                                  p2.y:=0;

                                  fill_mark(convert_point(p1),convert_point(p2),-2,'');   // rad 1 slewed mark location

                                end;
                      end;//rad 1

            if (spiral=True) and (ABS(nomrad2)<1.0E6)      // rad 2 centre marker ...
               then begin
                      pin.x:=xt2;
                      pin.y:=yt2;
                      dotransform(kform,xform,yform,pin,pout);

                      pout.x:=pout.x+xshift;
                      pout.y:=pout.y+yshift;

                      p1:=pout;

                      p2.x:=0;    // not used.
                      p2.y:=0;

                      fill_mark(convert_point(p1),convert_point(p2),-3,'');   // rad 2 mark location.


                      if slewing=True      // slewed rad 2 centre marker ...
                         then begin
                                pin.x:=xt2-slew*SIN(slew_t);
                                pin.y:=yt2+slew*COS(slew_t);

                                dotransform(kform,xform,yform,pin,pout);

                                pout.x:=pout.x+xshift;
                                pout.y:=pout.y+yshift;

                                p1:=pout;

                                p2.x:=0;    // not used.
                                p2.y:=0;

                                fill_mark(convert_point(p1),convert_point(p2),-3,'');   // rad 2 slewed mark location.
                              end;
                    end;//rad 2
	  end;

                       // transition curve,  also for plain track....

  if (rad_ends=True) and (spiral=True) and (turnoutx<>0)  // 0.93.a  (turnoutx<>0) added
     then begin
            p1.x:=os;
            p1.y:=0-g;                          // mark start of transition zone.
            p2.x:=os;
            p2.y:=g*2;                          // wide mark across g beyond gauge face each side.
            enter_mark(True,p1,p2,7,'');

            p1.x:=os;
            p1.y:=g*2;                          // mark start of transition zone.
            p2.x:=os+g*2;
            p2.y:=g/2;                          // put an arrow on the start marker.
            enter_mark(True,p1,p2,7,'');

            p1.x:=os;
            p1.y:=0-g;                          // mark start of transition zone.
            p2.x:=os+g*2;
            p2.y:=g/2;                          // put an arrow on the start marker.
            enter_mark(True,p1,p2,7,'');

            p1.x:=os+tst;
            p1.y:=0-g;                          // mark end of transition zone.
            p2.x:=os+tst;
            p2.y:=g*2;                          // wide mark across g beyond gauge face each side.
            enter_mark(True,p1,p2,7,'');

            p1.x:=os+tst;
            p1.y:=g*2;                          // mark end of transition zone.
            p2.x:=os+tst-scale*4;
            p2.y:=g*2;                          // put a 4ft scale top on the end marker.
            enter_mark(True,p1,p2,7,'');

            p1.x:=os+tst;
            p1.y:=0-g;                          // mark end of transition zone.
            p2.x:=os+tst-scale*4;
            p2.y:=0-g;                          // put a 4ft tail on the end marker.
            enter_mark(True,p1,p2,7,'');

          end;

                           // slewing zone...

  if (rad_ends=True) and (slewing=True) and (turnoutx<>0)  // 0.93.a  (turnoutx<>0) added
     then begin
            p1.x:=slew_s;
            p1.y:=0-g*3/2;                        // mark start of slewing zone.
            p2.x:=slew_s;
            p2.y:=g*5/2;                          // mark across beyond gauge face each side.
            enter_mark(True,p1,p2,7,'');

            p1.x:=slew_s;                         // then 2 arrows on start mark...
            p1.y:=g*3/2;
            p2.x:=slew_s+g;
            p2.y:=g*2;
            enter_mark(True,p1,p2,7,'');

            p1.x:=slew_s+g;
            p1.y:=g*2;
            p2.x:=slew_s;
            p2.y:=g*5/2;
            enter_mark(True,p1,p2,7,'');

            p1.x:=slew_s;
            p1.y:=0-g/2;
            p2.x:=slew_s+g;
            p2.y:=0-g;
            enter_mark(True,p1,p2,7,'');

            p1.x:=slew_s+g;
            p1.y:=0-g;
            p2.x:=slew_s;
            p2.y:=0-g*3/2;
            enter_mark(True,p1,p2,7,'');

            p1.x:=slew_s+slew_l;
            p1.y:=g*3/4;                            // 2 marks at end of slewing zone...
            p2.x:=slew_s+slew_l;
            p2.y:=g*3;
            enter_mark(True,p1,p2,7,'');

            p1.x:=slew_s+slew_l;
            p1.y:=g/4;
            p2.x:=slew_s+slew_l;
            p2.y:=0-g*2;
            enter_mark(True,p1,p2,7,'');

            p1.x:=slew_s+slew_l;
            p1.y:=g/4;                            // add arms to end mark.
            p2.x:=slew_s+slew_l-g;
            p2.y:=g/4;
            enter_mark(True,p1,p2,7,'');

            p1.x:=slew_s+slew_l;
            p1.y:=g*3/4;
            p2.x:=slew_s+slew_l-g;
            p2.y:=g*3/4;
            enter_mark(True,p1,p2,7,'');

          end;

  if cl_only=True then EXIT;     // only peg, rad centres and transition/slewing marks wanted.

            // turnouts or plain track marks...

  if (guide_marks=True) and (centre_lines=True) and (track_centre_lines_flag=True) and (turnoutx<>0)  // 0.93.a  (turnoutx<>0) added
     then begin
                          // rail-end mark...
            p1.x:=0;
            p1.y:=g/2-gm;
            p2.x:=0;
            p2.y:=g/2+gm;
            enter_mark(True,p1,p2,1,'');      // mark vertical.

                          // rail-joint centre mark (or plain track end mark)...

            if plain_track=False
               then begin
                      p1.x:=xorg;
                      p1.y:=g/2-gm;
                      p2.x:=xorg;
                      p2.y:=g/2+gm;
                      enter_mark(True,p1,p2,1,'');      // mark vertical.
                    end
               else begin                   // prominent marks for plain track CTRL-1 rail joint (start) end...
                                            // use 3 marks to avoid overwriting the rail joints (already drawn by the plain track prog.)
                      p1.x:=xorg;
                      p1.y:=g-gmi;
                      p2.x:=xorg;
                      p2.y:=gmi;
                      enter_mark(True,p1,p2,10,'');      // mark vertical.

                      p1.x:=xorg;
                      p1.y:=g+railtop+gmo;
                      p2.x:=xorg;
                      p2.y:=g+railtop+gmo+g;
                      enter_mark(True,p1,p2,10,'');      // mark vertical.

                      p1.x:=xorg;
                      p1.y:=0-railtop-gmo;
                      p2.x:=xorg;
                      p2.y:=0-railtop-gmo-g;
                      enter_mark(True,p1,p2,10,'');      // mark vertical.

                      ms_loop:=18*inscale;    // 216b  size of maker loop  18"

                      p1.x:=xorg;
                      p1.y:=0-railtop-gmo-g;
                      p2.x:=xorg-ms_loop;
                      p2.y:=0-railtop-gmo-g;
                      enter_mark(True,p1,p2,10,'');      // mark horizontal MS indicator loop 216a.

                      p1.x:=xorg-ms_loop;
                      p1.y:=0-railtop-gmo-g;
                      p2.x:=xorg-ms_loop;
                      p2.y:=0-railtop-gmo-g+ms_loop;     // loop
                      enter_mark(True,p1,p2,10,'');      // mark vertical MS indicator loop 216a.

                      p1.x:=xorg-ms_loop;
                      p1.y:=0-railtop-gmo-g+ms_loop;
                      p2.x:=xorg;
                      p2.y:=0-railtop-gmo-g+ms_loop;     // loop
                      enter_mark(True,p1,p2,10,'');      // mark horizontal MS indicator loop 216a.

                    end;
          end;

    if (guide_marks=True) and (plain_track=False)    // turnouts only guide marks...
       then begin
                              // joggle marks ...

              if (joggled=True) and (gaunt=False) and (half_diamond=False)   // h_d added 208a
                 then begin

                      if (turnout_road_crossing_rail_flag=True) and (main_road_stock_rail_flag=True)    // not if there is no switch blade or stock rail.
                         then begin
	                        p1.x:=toex-joggle_long;
	                        p1.y:=gmi;

	                        p2.x:=toex-joggle_long;
	                        p2.y:=0-railtop-gmo;

                                joggle_labels_p1:=p1;    // 206b

	                        enter_mark(True,p1,p2,1,'');      // main-side joggle mark. scale 6" each side of rail.
                              end;

                      if (main_road_crossing_rail_flag=True) and (turnout_road_stock_rail_flag=True)    // not if there is no switch blade or stock rail.
                         then begin
	                        p1.x:=setx-joggle_long;
	                        p1.y:=g-gmi;
	                        p2.x:=setx-joggle_long;
	                        p2.y:=g+railtop+gmo;

                                joggle_labels_p2:=p1;   // 206b

	                        enter_mark(True,p1,p2,1,'');      // turnout-side joggle mark. scale 6" each side of rail.
                              end;

                           // 206b  joggle label...

                       if  (turnout_road_crossing_rail_flag=True) and (main_road_stock_rail_flag=True)
                       and (main_road_crossing_rail_flag=True)    and (turnout_road_stock_rail_flag=True)
                          then begin
                                 enter_mark(True,joggle_labels_p1,joggle_labels_p2,600,'');    // long joggle mark across track linking first marks

                                 joggle_labels_p2.y:=g/2;

                                 enter_mark(True,joggle_labels_p2,dummy_p,605,'');    // joggle label on centre of joggle mark

                               end;

                      end;

                            // toe marks ...   206b changes...

            if gaunt=False     // 215a  flags ignored (for slip switches)
               then begin
       	              p1.x:=toex;
	              p1.y:=gmi;

                      p2.x:=toex;                            // 206b *TAN(k1/2);
                      p2.y:=0-railtop-gmo;

                      enter_mark(True,p1,p2,1,'');      // normal toe mark (trackpad). each side of rail.

                      if half_diamond=False   // 208a
                         then begin

                                p2.y:=0-gm_swlabels_ms;   // 206b longer mark to clear timbers after fixing on template

                                enter_mark(True,p1,p2,600,'');    // long toe mark (overwrite on output). each side of rail.

                                enter_mark(True,p2,dummy_p,601,'');    // 206b toe tips label
                              end;
                    end;

            if gaunt=False      // 215a
               then begin
                      p1.x:=setx;
                      p1.y:=g-gmi;

                      p2.x:=setx;
                      p2.y:=g+railtop+gmo;

                      enter_mark(True,p1,p2,1,'');      // normal set mark (trackpad). each side of rail.

                      if half_diamond=False   // 208a
                         then begin
                                p2.y:=g+gm_swlabels_ts;   // 206b longer mark to clear timbers after fixing on template

                                enter_mark(True,p1,p2,600,'');    // long set mark (overwrite on output). each side of rail.

                                enter_mark(True,p2,dummy_p,602,'');    // 206b set label
                              end;
                    end;

                       // no planing end marks if drawing rail centreline only or this is a fixed-diamond...

            if (midline=False)
           and ((half_diamond=False) or ((fixed_diamond=False) and (hd_switch_timbering=False)))
           and (gaunt=False)
	       then begin
                      p1.x:=plox;
                      p1.y:=railtop+gmi;

                      p2.x:=plox;
                      p2.y:=0-railtop-gmo;

                      enter_mark(True,p1,p2,1,'');      // normal planing end mark (trackpad). each side of rail.

                      if half_diamond=False   // 208a
                         then begin

                                p2.y:=0-gm_swlabels_ms;

                                enter_mark(True,p1,p2,600,'');    // long planing end mark (overwrite on output). each side of rail.

                                enter_mark(True,p2,dummy_p,603,'');    // planing end label  206b
                              end;

                      p1.x:=stox;
                      p1.y:=g-gmi;

                      p2.x:=stox;
                      p2.y:=g+railtop+railtop+gmo;

                      enter_mark(True,p1,p2,1,'');      // normal stock gauge mark (trackpad). each side of rail.

                      if half_diamond=False   // 208a
                         then begin

                                p2.y:=g+railtop+gm_swlabels_ts;

                                enter_mark(True,p1,p2,600,'');    // long stock gauge mark (overwrite on output). each side of rail.

                                enter_mark(True,p2,dummy_p,604,'');    // stock gauge label  206b
                              end;
		    end;

                      // 211b half-diamond...

            if (midline=False) and (half_diamond=True) and (fixed_diamond=True) and (gaunt=False)    // no diamond points mark if rail centreline only or this is a fixed-diamond.
	       then begin

                      if (turnout_road_crossing_rail_flag=True) or (main_road_crossing_rail_flag=True) // either rail, mark across both
                         then begin

                                if tradius_is_straight=True    // regular diamond

                                   then p1:=draw_xing_label(hdk/2,seg_termx[3,2],0-gm_hdlabels_ms,g+gm_hdlabels_ts,fw,0,0)  // 211b do blunt nose marks, and get BN note position
                                   else begin
                                              // irregular diamond

                                          k_irreg:=hdk+seg_termx[3,2]/tradius;   // angle at tips

                                          p1:=draw_xing_label(k_irreg/2,seg_termx[3,2],0-gm_hdlabels_ms,g+gm_hdlabels_ts,fw,0,0);
                                        end;

                                if (p1.x<>0) and (p1.y<>0)
                                   then fill_mark(convert_point(p1),convert_point(dummy_p),703,'bt');   // label note into marks list ('bt' string ignored, label added for 703 code in print_unit, pdf_unit).

                              end;

                    end;

                                // knuckle marks  214a ...

            if ((turnout_road_crossing_rail_flag=True) and (main_road_crossing_rail_flag=True))   // both rails
               and (knuckle_code<>-1)                                                             // no marks if sharp bend
               then begin

                      p1.x:=wingcx_minus;
                      p1.y:=g;

                      p2.x:=wingx_minus;
                      p2.y:=g-fw-wing_bend_offset;

                      enter_mark(True,p1,p2,1,'');    // start of knuckle bend


                      p1.x:=wingcx;
                      p1.y:=g;

                      p2.x:=wingx;
                      p2.y:=g-fw;

                      enter_mark(True,p1,p2,1,'');    // knuckle - middle of knuckle bend


                      p1.x:=wingcx_plus;
                      p1.y:=g+wing_bend_offset;

                      p2.x:=wingx_plus;
                      p2.y:=g-fw;

                      enter_mark(True,p1,p2,1,'');    // end of knuckle bend

                    end;

                                // checking-extent marks  244e

            if check_diffs_form.show_checking_extent_checkbox.Checked=True
               then begin
                      p1.x:=bnx;
                      p1.y:=g;

                      p2.x:=bnx;
                      p2.y:=fw;

                      enter_mark(True,p1,p2,1,'');    // blunt nose to MS stock rail

                      p1.x:=bnxx;
                      p1.y:=aq2offset(bnxx,temp_angle);

                      p2.x:=p1.x-(g-fw)*SIN(temp_angle);
                      p2.y:=p1.y+(g-fw)*COS(temp_angle);

                      enter_mark(True,p1,p2,1,'');    // blunt nose to TS stock rail

                      p1.x:=wingcx_minus;
                      p1.y:=g;

                      p2.x:=wingcx_minus;
                      p2.y:=fw;

                      enter_mark(True,p1,p2,1,'');    // knuckle start to MS stock rail

                      p1.x:=wingx_minus;
                      p1.y:=aq2offset(wingx_minus,temp_angle);

                      p2.x:=p1.x-(g-fw)*SIN(temp_angle);
                      p2.y:=p1.y+(g-fw)*COS(temp_angle);

                      enter_mark(True,p1,p2,1,'');    // knuckle_start to TS stock rail

                    end;


				// f.p. cross mark

            if ((turnout_road_crossing_rail_flag=True) and (main_road_crossing_rail_flag=True))   // both rails
            or (crossing_vee_flag=True)                                                           // or vee
               then begin

                      p1.x:=fpx-1.5*inscale;     // fine point target horizontal 3 scale inches overall size.
                      p1.y:=g;
                      p2.x:=fpx+1.5*inscale;
                      p2.y:=g;
                      enter_mark(True,p1,p2,1,'');      // mark f.p. target horizontal.

                      p1.x:=fpx;
                      p1.y:=g-inscale;           // and 2 scale inches overall vertical.
                      p2.x:=fpx;
                      p2.y:=g+inscale;
                      enter_mark(True,p1,p2,1,'');      // mark f.p. target vertical.

                       // crosing label mods 211b...

                       // for equalized label marker lines, must do curving and transforms and fill_mark directly ...

                      p1:=draw_xing_label(k3/2,fpx,g/2,g-3*inscale,g,0,0);                                          // 211b do short FP mark

                      p1:=draw_xing_label(k3/2,fpx,g+3*inscale,g+gm_xinglabels_ts+6*inscale,g,0-scale,15*inscale);  // 211b do long FP marks, and get FP note position

                      if (p1.x<>0) and (p1.y<>0)
                         then fill_mark(convert_point(p1),convert_point(dummy_p),701,'fp');   // label note into marks list ('fp' string ignored, label added for 701 code in print_unit, pdf_unit).

                    end;

            if (midline=False) and (crossing_vee_flag=True)     // blunt nose label  211b...
               then begin
                          // for equalized label marker lines, must do curving and transforms and fill_mark directly ...

                      p1:=draw_xing_label(k3/2,bnx,g-gm_xinglabels_ms,g+gm_xinglabels_ts,g,scale,18*inscale);  // 211b do blunt nose marks, and get BN note position

                      if (p1.x<>0) and (p1.y<>0)
                         then fill_mark(convert_point(p1),convert_point(dummy_p),702,'bn');   // label note into marks list ('bn' string ignored, label added for 702 code in print_unit, pdf_unit).

                    end;


            if (centre_lines=True) and (track_centre_lines_flag=True)
               then begin
                                          // toe centre-line mark...
                      p1.x:=toemidx;
                      p1.y:=g/2-gm;
                      p2.x:=toemidx;
                      p2.y:=g/2+gm;
                      enter_mark(True,p1,p2,1,'');      // mark vertical.

                                          // d.p. mark...
                      p1.x:=dpx;
                      p1.y:=g/2-gm;
                      p2.x:=dpx;
                      p2.y:=g/2+gm;
                      enter_mark(True,p1,p2,1,'');      // mark vertical.

                                          // turnout side crossover mid-point mark...
                      p1.x:=txpx;
                      p1.y:=txpy-gm;
                      p2.x:=txpx;
                      p2.y:=txpy+gm;
                      enter_mark(True,p1,p2,1,'');      // mark vertical.

                                          // main side crossover mid-point mark...
                      p1.x:=mxpx;
                      p1.y:=g/2-gm;
                      p2.x:=mxpx;
                      p2.y:=g/2+gm;
                      enter_mark(True,p1,p2,1,'');      // mark vertical.

                                         // 226c turnout side THD mark ...

                      temp:=aq25offset(thdpx,tempk);
                      p1.x:=thdpx;
                      p1.y:=temp-gm;
                      p2.x:=thdpx;
                      p2.y:=temp+gm;
                      enter_mark(True,p1,p2,1,'');      // mark vertical.


                                          // turnout side vee joint (splice rail end) centre mark...

                      temp:=aq25offset(tvjpx,tempk);
                      p1.x:=tvjpx;
                      p1.y:=temp-gm;
                      p2.x:=tvjpx;
                      p2.y:=temp+gm;
                      enter_mark(True,p1,p2,1,'');      // mark vertical.

                                          // main side vee joint (point rail end) centre mark...
                      p1.x:=mvjpx;
                      p1.y:=g/2-gm;
                      p2.x:=mvjpx;
                      p2.y:=g/2+gm;
                      enter_mark(True,p1,p2,1,'');      // mark vertical.


                      if retpar_i=1       // main side return-point mark...  (turnout-side mark is a rad-end mark instead).
                         then begin
                                p1.x:=mrpx;
                                p1.y:=g/2-gm;
                                p2.x:=mrpx;
                                p2.y:=g/2+gm;
                                enter_mark(True,p1,p2,1,'');      // mark vertical.
                              end;

                    end;
          end;

                                // tie-bar slot mark...

  if (switch_drive_markx=True) and (switch_drive_flag=True)
 and (plain_track=False) and (gaunt=False)
 and ( (half_diamond=False) or ((half_diamond=True) and (fixed_diamond=False)) )      // 227a movable K-crossings
     then begin
            tbe:=scale;         // tie-bar near end 1ft scale beyond gauge.
            tbh:=scale*3;       // 226c tie-bar far-end 3ft scale beyond gauge (turnout hand indicator)
            tbw:=2*inscale;     // tie-bar half-width 2" scale.

            if (half_diamond=True) and (hd_switch_timbering=False)
               then begin
                      xtb:=toex+12*inscale;       // 227a movable K-crossings..
                      try
                        xtb_hd:=xtb-(g+tbh)/hdkn/2;
                      except
                        xtb_hd:=xtb;
                      end;//try
                    end
               else begin                    // turnout or hd timbered as switch..
                      xtb:=toex+18*inscale;  // now fixed at 18" from toe to tie-bar. (mod mainly for slip switches) // 0.82.a  13-10-06

                      if half_diamond=True   // timbered as switch
                         then begin
                                try
                                  xtb_hd:=xtb-(g+tbh)/hdkn/2;
                                except
                                  xtb_hd:=xtb;
                                end;//try
                              end
                      else xtb_hd:=xtb;   // turnout
                    end;

            if ((turnout_road_crossing_rail_flag=True) or (main_road_crossing_rail_flag=True))  // not if no switch blades.
            and (xtb>startx)                                                                    // not if blanked   555a  MW 21-AUG-2024
               then begin
                      p1.x:=xtb_hd-tbw;
                      p1.y:=g+tbh;  // 226c  was g+tbe;
                      p2.x:=xtb_hd+tbw;
                      p2.y:=g+tbh;  // 226c  was g+tbe;
                      enter_mark(True,p1,p2,101,'');      // top end of tie-bar.

                      p1.x:=xtb-tbw;
                      p1.y:=0-tbe;
                      p2.x:=xtb+tbw;
                      p2.y:=0-tbe;
                      enter_mark(True,p1,p2,101,'');      // bottom end of tie-bar.

                      p1.x:=xtb_hd-tbw;
                      p1.y:=g+tbh;  // 226c  was g+tbe;
                      p2.x:=xtb-tbw;
                      p2.y:=0-tbe;
                      enter_mark(True,p1,p2,101,'');      // left side of tie-bar.

                      p1.x:=xtb_hd+tbw;
                      p1.y:=g+tbh;  // 226c  was g+tbe;
                      p2.x:=xtb+tbw;
                      p2.y:=0-tbe;
                      enter_mark(True,p1,p2,101,'');      // right side of tie-bar.
                    end;
          end;

  if (joint_marks=True) and (plain_track=False)
     then begin
                   // switch front joints (we may be overwriting approach track joints already done)...

            if (omit_swfj_marks=False) and (csi.stock_rail>minfp) and (half_diamond=False)  // ignore if stock rail length is zero (custom switch).
               then begin
                      if turnout_road_stock_rail_flag=True
                         then begin
                                p1.x:=xorg;
                                p1.y:=g-gmi;
                                p2.x:=xorg;
                                p2.y:=g+gmo+railtop;
                                enter_mark(True,p1,p2,6,'');  // curved stock rail end joint mark.
                              end;

                      if main_road_stock_rail_flag=True
                         then begin
                                p1.x:=xorg;
                                p1.y:=gmi;
                                p2.x:=xorg;
                                p2.y:=0-(gmo+railtop);
                                enter_mark(True,p1,p2,6,'');  // straight stock rail end joint mark.
                              end;
                    end;

                           // switch rail joints...

            if ((half_diamond=False) and (gaunt=False))
            or ((half_diamond=True) and (fixed_diamond=False) and (hd_switch_timbering=True))  // 213a
               then begin
                      if (omit_swrj_marks=False) and (csi.switch_rail>minfp)  // ignore if switch rail length is zero (custom switch).
                         then begin
                                tempx:=toex+csi.switch_rail*inscale;   // switch rail length (from TOE).
                                tempy:=aq3offset(tempx,k);             // offset to aq=3 (curved stock rail). also returns k angle.
                                tempcos:=COS(k);
                                tempsin:=SIN(k);
                                if ABS(tempcos)>minfp then tempy:=tempy-g/tempcos   // back to curved switch rail (aq=1).
                                                      else tempy:=tempy-g;

                                if turnout_road_crossing_rail_flag=True
                                   then begin
                                          p1.x:=tempx+gmo*tempsin;       // ignore rail-width - so mark is offset to the gauge side.
                                          p1.y:=tempy-gmo*tempcos;
                                          p2.x:=tempx-gmi*tempsin;
                                          p2.y:=tempy+gmi*tempcos;
                                          enter_mark(True,p1,p2,6,'');   // curved switch rail joint mark.
                                        end;

                                if main_road_crossing_rail_flag=True
                                   then begin
                                          p1.x:=tempx;                   // ignore rail-width - so mark is offset to the gauge side.
                                          p1.y:=g+gmo;
                                          p2.x:=tempx;
                                          p2.y:=g-gmi;
                                          enter_mark(True,p1,p2,6,'');   // straight switch rail joint mark.
                                        end;
                              end;
                                          // stock rail joints...

                      if (omit_skj_marks=False) and (csi.stock_rail>minfp)  // ignore if stock rail length is zero (custom switch).
                         then begin

                                if half_diamond=True    // 213a timbering movable K-crossing as a switch..

                                   then tempx:=toex+(csi.stock_rail-csi.switch_front_inches)*inscale   // K-crossing
                                   else tempx:=xorg+csi.stock_rail*inscale;   // turnout stock rail length (from JOINT).

                                tempy:=aq3offset(tempx,k);       // offset to aq=3 (curved stock rail). also returns k angle.
                                tempcos:=COS(k);
                                tempsin:=SIN(k);

                                if turnout_road_stock_rail_flag=True
                                   then begin
                                          p1.x:=tempx-(gmo+railtop)*tempsin;
                                          p1.y:=tempy+(gmo+railtop)*tempcos;
                                          p2.x:=tempx+(gmi-railtop)*tempsin;      // offset mark to outer edge.
                                          p2.y:=tempy-(gmi-railtop)*tempcos;
                                          enter_mark(True,p1,p2,6,'');            // curved stock rail joint mark.
                                        end;

                                if main_road_stock_rail_flag=True
                                   then begin
                                          p1.x:=tempx;
                                          p1.y:=gmi-railtop;
                                          p2.x:=tempx;
                                          p2.y:=0-(gmo+railtop);
                                          enter_mark(True,p1,p2,6,'');              // straight stock rail joint mark.
                                        end;
                              end;
                    end
               else begin    // normal half_diamond (wing rail joints, not point rails NYI) ...

                      if (omit_kx_marks=False) and ((hd_proto_timbering=True) or (fixed_diamond=False)) and (gaunt=False)    // no joints if non prototypical fixed-crossing spacings.
                         then begin

                                if use_k_custom_wing_rails=True      // // 0.95.a
                                   then kwl:=k_custom_wing_long/2
                                   else begin
                                          if fixed_diamond=True
                                             then begin
                                                    if hdkn<=6.375       // 0.94.a bug fix was k3n
                                                       then begin
                                                              if rail_section<>2   // BH or none.
                                                                 then kwl:=173/2   //  14'5" BH k-wing rails overall.
                                                                 else kwl:=168/2;  //  14'0" FB.
                                                            end
                                                       else begin
                                                              if rail_section<>2   // BH or none.
                                                                 then kwl:=185/2   //  15'5" BH k-wing rails overall.
                                                                 else kwl:=220/2;  //  18'4" FB.
                                                            end;
                                                  end
                                             else begin
                                                    if rail_section<>2 then kwl:=216      // BH 18'0"
                                                                       else kwl:=193;     // FB 16'1" half-switch-diamond wing rail.
                                                  end;
                                        end;

                                tempx:=setx+(kwl*inscale*COS(hdk/2)); // 0.95.a  was k3
                                tempy:=aq3offset(tempx,k);            // offset to aq=3 (curved stock rail). also returns k angle.
                                tempcos:=COS(k);
                                tempsin:=SIN(k);

                                if turnout_road_stock_rail_flag=True
                                   then begin
                                          p1.x:=tempx-(gmo+railtop)*tempsin;
                                          p1.y:=tempy+(gmo+railtop)*tempcos;
                                          p2.x:=tempx+gmi*tempsin;        // offset mark to outer edge.
                                          p2.y:=tempy-gmi*tempcos;
                                          enter_mark(True,p1,p2,6,'');            // curved stock rail joint mark.
                                        end;

                                if main_road_stock_rail_flag=True
                                   then begin

                                          tempx:=toex+(kwl*inscale/COS(hdk/2));  // 0.95.a  was k3

                                          p1.x:=tempx;
                                          p1.y:=gmi;
                                          p2.x:=tempx;
                                          p2.y:=0-railtop-gmo;
                                          enter_mark(True,p1,p2,6,'');              // straight stock rail joint mark.
                                        end;
                              end;
                    end;

                                // crossing (wing rail front) joints...

            if ( (half_diamond=False) or (hd_vcheck_rails=0) or (k3n>7.99) )  // 205a  remove joint marks if slip and shorter than 1:8
            and (k3n>=4)                                                      // 228a  no joints under 1:4  (combined wing diamond-point rail)
               then begin

                      if (omit_wj_marks=False) and (no_timbering=False)      // need the timbering calcs for these.
                         then begin
                                tempx:=(xtbzz+xtbz)/2;

                                if main_road_crossing_rail_flag=True
                                   then begin
                                          p1.x:=tempx;                               // crossing joint timber centres.
                                          p1.y:=g+gmo+railtop;
                                          p2.x:=tempx;
                                          p2.y:=g-gmi;
                                          enter_mark(True,p1,p2,6,'');               // straight turnout rail crossing joint mark.
                                       end;

                                tempx:=fpx-(fpx-tempx)*COS(k3);   //  move crossing joint in turnout rail (ignores turnout rad).

                                tempy:=aq2offset(tempx,k);
                                tempcos:=COS(k);
                                tempsin:=SIN(k);

                                if turnout_road_crossing_rail_flag=True
                                   then begin
                                          p1.x:=tempx-gmi*tempsin;
                                          p1.y:=tempy+gmi*tempcos;
                                          p2.x:=tempx+(gmo+railtop)*tempsin;
                                          p2.y:=tempy-(gmo+railtop)*tempcos;
                                          enter_mark(True,p1,p2,6,'');   // curved turnout rail crossing joint mark.
                                        end;
                              end;
                    end;

            // vee point and splice rail joints ...          227a mods...

              // TS splice rail joint mark...

            tempx:=tvjp_veejx;

            tempy:=aq2offset(tempx,k);

            tempcos:=COS(k);  // 227a
            tempsin:=SIN(k);  // 227a

            p1.x:=tempx-gmi*tempsin;
            p1.y:=tempy+gmi*tempcos;
            p2.x:=tempx+(gmo+railtop)*tempsin;
            p2.y:=tempy-(gmo+railtop)*tempcos;

            if  (omit_vj_marks=False) and (crossing_vee_flag=True)   // 227a moved here
                then enter_mark(True,p1,p2,6,'');                    // splice rail joint mark.


              // TS stock rail joint mark...

            tempx:=tvjp_stockjx;

            tempy:=aq3offset(tempx,k);  // on stock rail

            tempcos:=COS(k);
            tempsin:=SIN(k);

            p1.x:=tempx+gmi*tempsin;
            p1.y:=tempy-gmi*tempcos;
            p2.x:=tempx-(gmo+railtop)*tempsin;
            p2.y:=tempy+(gmo+railtop)*tempcos;

            if (omit_tsxj_marks=False) and (turnout_road_stock_rail_flag=True)
            and (turnout_road_i<>-1)                                              // 227a not for a crossover - vee splice rail joint shown instead on opposite turnout
               then enter_mark(True,p1,p2,6,'');                                  // TS stock rail joint mark.


              // MS point rail joint mark...

            tempx:=mvjpx;       // x to MS point rail joint  (already modified)

            mvjp_veejx:=tempx;  // for locator if needed

            p1.x:=tempx;
            p1.y:=g-gmi;
            p2.x:=tempx;
            p2.y:=g+gmo+railtop;

            if (omit_vj_marks=False) and (crossing_vee_flag=True)         // 227a moved here
               then enter_mark(True,p1,p2,6,'');                          // point rail joint mark.


              // MS stock rail joint mark...

            tempx:=mvjp_nx+cpi.ms_joint_offset_pi*inscale;    // x to MS stock rail joint

            mvjp_stockjx:=tempx;  // for locator if needed

            p1.x:=tempx;
            p1.y:=gmi;
            p2.x:=tempx;
            p2.y:=0-(gmo+railtop);

            if (omit_msxj_marks=False) and (main_road_stock_rail_flag=True)
               then enter_mark(True,p1,p2,6,'');                            // MS stock rail joint mark.

          end;//joint marks

                                // radial end marks...

  if (rad_ends=True) and (plain_track=False)                                           // mark across gauge-faces at each end of curves.
  and ((turnout_road_stock_rail_flag=True) or (turnout_road_crossing_rail_flag=True))  // only if there is some rail to mark.
     then begin
                 // start of switch curve (end of planing)...

            if (Abs(k1-k2)>minfp) and (swrad<max_rad_test) and (half_diamond=False)      // (do nothing for straight switch.)
               then begin
                      if switch_type=0   // curved planing, switch curve starts at toe (mark across between tips instead of strictly radial).
                         then begin
                                p1.x:=setx;
                                p1.y:=g;
                                p2.x:=toex;
                                p2.y:=0;
                                enter_mark(True,p1,p2,2,'');
                              end
                         else begin         //  semi-curved switch (mark across between planing end marks instead of strictly radial).

                                p1.x:=stox;
		                p1.y:=g+j;
		                p2.x:=plox;
		                p2.y:=j;
                                enter_mark(True,p1,p2,2,'');
                              end;
                    end;

            if tradius_is_straight=False
               then begin
                      if half_diamond=False   // turnout only (and gaunt curve start).
                         then begin
                                p1.x:=torgx+csradius*SIN(k2);    // switch curve end / turnout curve start.
                                p1.y:=torgy-csradius*COS(k2);
                                p2.x:=torgx+tradius*SIN(k2);     //^^^
                                p2.y:=torgy-tradius*COS(k2);     //^^^
                                enter_mark(True,p1,p2,2,'');
                              end;

                      if xing_calc_i<>1     // not curviform V-crossing
                         then begin
                                p1.x:=torgx+csradius*SIN(k3);    // turnout/diagonal curve end.
                                p1.y:=torgy-csradius*COS(k3);
                                p2.x:=torgx+tradius*SIN(k3);     //^^^
                                p2.y:=torgy-tradius*COS(k3);     //^^^
                                enter_mark(True,p1,p2,2,'');
                              end;
                    end
               else begin                     // straight turnout radius.
                      if (half_diamond=False) and (gaunt=False)
                         then begin
                                p1.x:=sworgx+(swrad-g)*SIN(k2);    // switch "curve" end mark.
                                p1.y:=sworgy-(swrad-g)*COS(k2);
                                p2.x:=sworgx+swrad*SIN(k2);
                                p2.y:=sworgy-swrad*COS(k2);
                                enter_mark(True,p1,p2,2,'');
                              end;
                    end;
          end;


                         // return curves..

  if (rad_ends=True) and (plain_track=False)
  and (retpar_i=1) and (crossing_vee_flag=True) and (turnout_road_stock_rail_flag=True)
     then begin
            p1.x:=retrorgx-(retr+g/2)*SIN(k3);         // return curve start.
            p1.y:=retrorgy+(retr+g/2)*COS(k3);
            p2.x:=retrorgx-(retr-g/2)*SIN(k3);
            p2.y:=retrorgy+(retr-g/2)*COS(k3);
            enter_mark(True,p1,p2,2,'');

            p1.x:=retrorgx;                          // return curve end.
            p1.y:=trtscent+g;
            p2.x:=retrorgx;
            p2.y:=trtscent;
            enter_mark(True,p1,p2,2,'');
          end;
end;
//_______________________________________________________________________________________

procedure do_railends;      // calc rail end marks...

var
  aq:integer;
  p1,p2:Tpex;

  aq_begin,aq_end:integer;

  start_boundary,end_boundary:extended;

begin
  for aq:=0 to aq_max_c do begin
    endmarks_yn[aq,0]:=False;         // init defaults.
    endmarks_yn[aq,1]:=False;
  end;

  if (plain_track=True) and (draw_ts_platform=False) and (draw_ms_platform=False) then EXIT;

  if plain_track=True
     then begin
            aq_begin:=16;   // platform ends
            aq_end:=23;
          end
     else begin
            aq_begin:=0;         // turnout
            aq_end:=aq_max_c;
          end;

  for aq:=aq_begin to aq_end do begin

    if cl_only=True     // platforms /trackbed only
       then begin
              if (aq<16) or (aq>23) then CONTINUE;
            end;

    if aqyn[aq]=True
       then begin
              case aq of

                  16,17,20,21 : begin
                                  start_boundary:=startx-scale; //minfp_big;   // platforms - increase limits by 1ft to show ends always
                                  end_boundary:=turnoutx+scale; //minfp_big;
                                end;

                           else begin
                                  start_boundary:=startx+minfp_big;  // rails - reduce limits to exclude ends if rail not complete
                                  end_boundary:=turnoutx-minfp_big;
                                end;
              end;//case

              p1:=xy_ends[aq,0];        // start end of rail
              p2:=xy_ends[aq,1];        // finish end of rail

              if (p1.x<end_boundary) and (p1.x>start_boundary)
                 then begin
                        endmarks[aq,0]:=convert_point(curve_point(p1));
                        endmarks_yn[aq,0]:=True;
                      end;

              if (p2.x<end_boundary) and (p2.x>start_boundary) and (ABS(p2.x-p1.x)>minfp_big) // ignore end mark if same position as start mark.
                 then begin
                        endmarks[aq,1]:=convert_point(curve_point(p2));
                        endmarks_yn[aq,1]:=True;
                      end;
            end;
  end;//for
end;
//_____________________________________________________________________________________________

procedure draw_rail_endmarks(on_canvas:TCanvas; ink:boolean);       // draw the rail ends.

var
  P1, P2: TPoint;
  move_to, line_to: TPoint;

		  ////////////////////////////////////////////////////////////////////////

                  procedure mark_end(aq1, aq1end, aq2, aq2end:integer; pen_solid:boolean);      // make the mark

                  begin
                    try

                    if (endmarks_yn[aq1,aq1end]=True) and (endmarks_yn[aq2,aq2end]=True)
                       then begin
                              P1:=endmarks[aq1,aq1end];
                              P2:=endmarks[aq2,aq2end];

                              with on_canvas do begin

                                Pen.Width:=1;

                                Brush.Color:=paper_colour;  // gaps in dotted lines.
                                Brush.Style:=bsSolid;

                                if pen_solid=True then Pen.Style:=psSolid   // 0.93.a mods for platforms
                                                  else Pen.Style:=psDot;

                                if ink=True                           // not erasing ?
                                   then begin
                                          if (aq1=16) or (aq1=20)     // 0.93.a platforms
                                             then Pen.Color:=guide_colour
                                             else Pen.Color:=rail_colour;
                                        end
                                   else Pen.Color:=paper_colour;      // erase existing marks.

                                Pen.Mode:=pmCopy;

                                move_to.X:=Round(P1.X*sx+ex-gx);  move_to.Y:=Round((P1.Y+yd)*sy+by-gy);
                                line_to.X:=Round(P2.X*sx+ex-gx);  line_to.Y:=Round((P2.Y+yd)*sy+by-gy);
                                if check_limits(move_to, line_to)=True then begin MoveTo(move_to.X, move_to.Y); LineTo(line_to.X, line_to.Y); end;
                              end;//with
                            end;
                    except
                      EXIT;   // ignore rail-end mark if any calc errors.
                    end;
                  end;
                  ////////////////////////////////////////////////////////////

begin

       // 0.93.a platform ends ...

  // 0.93.a TS platform start

  mark_end(16,0,17,0,draw_ts_platform_start_edge);

  // 0.93.a TS platform end

  mark_end(16,1,17,1,draw_ts_platform_end_edge);

  // 0.93.a MS platform start

  mark_end(20,0,21,0,draw_ms_platform_start_edge);

  // 0.93.a MS platform end

  mark_end(20,1,21,1,draw_ms_platform_end_edge);


  if plain_track=False
     then begin                                       // mark rail-ends...

            mark_end(1,1,9,1,True);    // main rail wing rail finish. Solid line.
            mark_end(2,1,10,1,True);   // turnout rail wing rail finish.

            mark_end(6,0,14,0,True);   // main side check rail start.
            mark_end(6,1,14,1,True);   // main side check rail finish.

            mark_end(7,0,15,0,True);   // turnout side check rail start.
            mark_end(7,1,15,1,True);   // turnout side check rail finish.

            mark_end(4,0,5,0,True);    // blunt nose.

            if (half_diamond=True) and (fixed_diamond=True)
               then begin
                      if isolated_crossing=False    // 217a
                         then begin
                                mark_end(1,0,9,0,True);       // planed faced of point rails for a fixed-diamond. Solid line.
                                mark_end(2,0,10,0,True);
                              end;

                      mark_end(26,1,27,1,True);     // MS K-crossing check rails.
                      mark_end(28,1,29,1,True);     // DS K-crossing check rails.
                    end;
          end;
end;
//____________________________________________________________________________________________

function pad_marks_current(on_canvas:TCanvas; ink:boolean):boolean;  // draw all the marks (control template). (not rail ends)
                                                                     // on the specified canvas.
var
  i,n,nn,code:integer;
  p1,p2,p3,p4:TPoint;

  p:array[0..27] of TPoint;

  move_to, line_to: TPoint;
  radcenx, radceny, radcen_dim:integer;

  ptr_1st:^Tmark;         // pointers to a Tmark record..
  ptr_2nd:^Tmark;
  ptr_nn:^Tmark;
  markmax:integer;

  check_int1x, check_int1y, check_int2x, check_int2y:extended;
  check_int3x, check_int3y, check_int4x, check_int4y:extended;

  shove_dim, pad_shovex, pad_shovey:integer;

  infill_points:array[0..34] of TPoint;    // 28 (0-27) points for chair outlines with radiused corners
                                           // then 4 points (29..31) for plinth rectangle, +3 spares       221a

  num_str, tbnum_str:string;
  half_stringwidth, half_stringheight:integer;
  temp_str:string;
  omitted:boolean;

  dummy_i:integer;

  pad_guide_marks:boolean;
  pad_timber_outlines:boolean;
  pad_timber_centres:boolean;
  pad_timber_numbers:boolean;

  list_chair_code:integer;   // MW 03-08-2024  555a
  chair_label_str:string;

begin
  RESULT:=False;  // default init.

  with pad_form do begin
    pad_guide_marks:=show_current_template_guides_menu_entry.Checked;  // 0.79.a  current_guides_menu_entry.Checked;
    pad_timber_outlines:=current_timber_outlines_menu_entry.Checked;
    pad_timber_centres:=current_timber_centres_menu_entry.Checked;
    pad_timber_numbers:=current_timber_numbers_menu_entry.Checked;
  end;//with

  if Length(marks_list) = 0 then EXIT;        // pointer to marks list not valid.
  markmax:=High(marks_list);  // max index for the present list.

  if mark_index>markmax then mark_index:=markmax;  // ??? shouldn't be.

  tbnum_str:=timb_numbers_str;      // the full string of timber numbering for the control template.

  with on_canvas do begin

    for i:=0 to (mark_index-1) do begin   // (mark_index is always the next free slot)

      try
        ptr_1st:=@marks_list[i];  // pointer to the next Tmark record.
        if ptr_1st=nil then EXIT;

	code:=ptr_1st^.code;

        list_chair_code:=ptr_1st^.dxf_chair_code;    // MW 03-08-2024  555a

        if code=0 then CONTINUE;     // ignore mark entries with code zero (might be the second or third of a multi-mark entry, e.g. for timber infill or chair outlines).

        if (code>599) and (code<9996) then CONTINUE;  // 206b 211b ignore long marks and switch/xing labels for control template on trackpad  600,601-605, 700,701-703      9996-9999 is DXF exports

        if ((code=203) or (code=233) or (code=293) or (code=484) or (code=485) or (code=487) or (code=493) or (code=494) or (code=497)) and (i<(mark_index-1))      // timber infill, chair outlines
           then begin
                  ptr_2nd:=@marks_list[i+1];        // pointer to the second infill Tmark record.

                  p3:=ptr_2nd^.p1;              // x3,y3 in  1/100ths mm
                  p4:=ptr_2nd^.p2;              // x4,y4 in  1/100ths mm
                end
           else ptr_2nd:=nil;    // keep compiler happy.

        if (code=484) or (code=485) or (code=493) or (code=494) or (code=497)    // another 12 marks for radiused corners
           then begin

                  n:=0;
                  nn:=2;

                  repeat
                    ptr_nn:=@marks_list[i+nn];        // pointer to the next infill Tmark record.

                    p[n]:=ptr_nn^.p1;       // 1/100ths mm
                    p[n+1]:=ptr_nn^.p2;     // 1/100ths mm

                    INC(n);   //+2
                    INC(n);
                    INC(nn);  //+1

                  until n>26;     // was 22

                end;

        p1:=ptr_1st^.p1;              // x1,y1 in  1/100ths mm

        if ((code<>99) and (code<501)) or (code>9995)  // 501-508 is check-rail labels        9996-9999 is DXF exports
           then begin
                  p2:=ptr_1st^.p2;    // x2,y2 in  1/100ths mm

                  Pen.Style:=psSolid;  // solid lines look better on screen.
                  Pen.Width:=1;

                  if ink=True            // not erasing ?
                     then begin
                            case code of
                                   -5: CONTINUE;                                        // ignore label position.

                                -3,-2: if pad_guide_marks=True then Pen.Color:=guide_colour  // curving rad centres.
                                                               else CONTINUE;

                                   -1: if paper_colour<>clRed then Pen.Color:=clRed     // fixing peg.
                                                              else Pen.Color:=clBlack;

                                1,101: if pad_guide_marks=True then Pen.Color:=guide_colour  // guide marks.   223d switch drive
                                                               else CONTINUE;

                                    2: if pad_guide_marks=True then Pen.Color:=align_colour  // radial ends.
                                                               else CONTINUE;

                                    3: if pad_timber_outlines=True then Pen.Color:=timber_colour   // timber outlines
                                                                   else CONTINUE;

                                 4,14: if pad_timber_centres=True then Pen.Color:=timber_colour   // timber centre-lines.
                                                                  else CONTINUE;

                                    5: CONTINUE;    // timber reduced ends not on screen.

                                    6: if pad_guide_marks=True then Pen.Color:=joint_colour  // rail joint marks.
                                                               else CONTINUE;

                                    7: if pad_guide_marks=True then Pen.Color:=trans_colour  // transition marks.
                                                               else CONTINUE;

                                    8: begin                  // peg 1st arm.
                                         peg_arm1:=ptr_1st^;  // save mark for marking later.
                                         CONTINUE;            // next mark.
                                       end;

                                    9: begin                  // peg 2nd arm.
                                         peg_arm2:=ptr_1st^;  // save mark for marking later.
                                         CONTINUE;            // next mark.
                                       end;

                                   10: if pad_guide_marks=True then Pen.Color:=guide_colour   // plain track start marks.
                                                               else CONTINUE;

                            33,55,233: if paper_colour<>clRed then Pen.Color:=clRed        // selected for shoving timber outline and infill.
                                                              else Pen.Color:=clYellow;

                            93,95,293: if paper_colour<>clBlue then Pen.Color:=clBlue      // other shoved timbers outline and infill.
                                                               else Pen.Color:=clWhite;

                                44,54: if check_dark_paper=False then Pen.Color:=clBlack   // shoved timber centre-line.
                                                                 else Pen.Color:=clWhite;

                                  203: if (pad_timb_infill_style>0) and ( (screenx<200*scale) or (pad_timb_infill_style>2) )  // infill on pad if solid/blank fill or large enough to see hatching.
                                          then Pen.Color:=timber_infill_colour                        // timber infilling. n.b. gets changed to paper colour later, but NOT yet !
                                          else CONTINUE;

                             441..478: Pen.Color:=clBlack;   // MW 03-08-2024  555a    chair labels

                     480..483,488,489: begin
                                         Pen.Color:=clBlue;  // 233d  chair key marks
                                         Pen.Width:=2;
                                       end;

                                  486: Pen.Color:=$00B0FFFF;  // clip-fit tangs

                                  492: CONTINUE;  // 3-D chair sockets not wanted, see  498

                                  491: Pen.Color:=clWhite;  // plug and pyramid marks

                          495,496,499: Pen.Color:=timber_colour;  // 221a  chair bolt marks, pin slot mark

                                  498: Pen.Color:=clBlack;  // chair sockets for 2-D

                                  490: if pad_form.show_3d_timber_flanges_menu_entry.Checked=True   // timber outer flanges for 3-D
                                          then Pen.Color:=timber_colour
                                          else CONTINUE;

                            9996,9997: if pad_form.show_2d_cutter_kerf_lines_menu_entry.Checked=True
                                          then Pen.Color:=$00FF0080                                   // cutter kerf lines for 2-D   violet
                                          else CONTINUE;

                            9998,9999: if pad_form.show_3d_timber_flanges_menu_entry.Checked=True
                                          then Pen.Color:=timber_colour                               // timber webs and sprues for 3-D
                                          else CONTINUE;

                              else Pen.Color:=clBlack;

                            end;//case
                          end
                     else Pen.Color:=paper_colour;       // erase existing marks.

                  Pen.Mode:=pmCopy;

                  check_int1x:=limits(h_minint,h_maxint,p1.X*sx+ex-gx,dummy_i);      // h_min, h_max 31 bit to give room for some arithmetic on the data (shift keeps, etc.)
                  check_int1y:=limits(h_minint,h_maxint,(p1.Y+yd)*sy+by-gy,dummy_i);

                  check_int2x:=limits(h_minint,h_maxint,p2.X*sx+ex-gx,dummy_i);
                  check_int2y:=limits(h_minint,h_maxint,(p2.Y+yd)*sy+by-gy,dummy_i);

                  if ((code>0) and (code<200)) or (code=480) or (code=481) or (code=482) or (code=483) or (code=486) or (code=490) or (code=491) or (code=495) or (code=496) or (code=498) or (code=499) or (code>9995)
                     then begin
                            move_to.X:=Round(check_int1x);  move_to.Y:=Round(check_int1y);
                            line_to.X:=Round(check_int2x);  line_to.Y:=Round(check_int2y);
                            if check_limits(move_to, line_to)=True then begin MoveTo(move_to.X, move_to.Y); LineTo(line_to.X, line_to.Y); end;
                          end
                     else begin
                            if code=-1              // code -1, draw fixing peg (not arms) ...
                               then begin
                                      if ink=True
                                         then draw_notch(on_canvas);   // first draw the pegging notch again between the timbers and the rails.
                                                                       // This routine is in the grid unit (notch already drawn underneath timbers).
                                                                       // Draw before the peg so that the peg overwrites.

                                      pad_pegx:=Round(check_int1x);    // save peg co-ords for drawing after rails, and rotate action.
                                      pad_pegy:=Round(check_int1y);

                                    end;//peg

                            if (code=-2) or (code=-3)              // draw curving rad centres...
                               then begin
                                      radcen_dim:=6; // 0.91.b was Screen.Width div 150; // 150 arbitrary. (larger for the current than bgnd keeps).
                                      if radcen_dim>Round(scale*5*fx) then radcen_dim:=Round(scale*5*fx);     // but not more than 5ft scale.

                                      radcenx:=Round(check_int1x);
                                      radceny:=Round(check_int1y);

                                      if spiral=False
                                         then begin
                                                rad_centx:=radcenx;      // save pad co-ords for orbit action (r1).
                                                rad_centy:=radceny;
                                              end;

                                      if (spiral=True) and (adjust_trans_rad=1) and (code=-2)
                                         then begin
                                                rad_centx:=radcenx;      // save pad co-ords for orbit action (r1).
                                                rad_centy:=radceny;
                                              end;

                                      if (spiral=True) and (adjust_trans_rad=2) and (code=-3)
                                         then begin
                                                rad_centx:=radcenx;      // save pad co-ords for orbit action (r2).
                                                rad_centy:=radceny;
                                              end;

                                      move_to.X:=radcenx-radcen_dim*2;  move_to.Y:=radceny;
                                      line_to.X:=radcenx+radcen_dim*2;  line_to.Y:=radceny;
                                      if check_limits(move_to, line_to)=True then begin MoveTo(move_to.X, move_to.Y); LineTo(line_to.X, line_to.Y); end;

                                      move_to.X:=radcenx;  move_to.Y:=radceny-radcen_dim*2;
                                      line_to.X:=radcenx;  line_to.Y:=radceny+radcen_dim*2;
                                      if check_limits(move_to, line_to)=True then begin MoveTo(move_to.X, move_to.Y); LineTo(line_to.X, line_to.Y); end;
                                    end;

                            if ((code=203) or (code=233) or (code=293) or (code=484) or (code=485) or (code=487) or (code=493) or (code=494) or (code=497)) and (ptr_2nd<>nil)   // timber infill, chair outlines...
                               and (pad_timb_infill_style>0)

                               then begin
                                      check_int3x:=limits(h_minint,h_maxint,p3.X*sx+ex-gx,dummy_i);      // h_min, h_max 31 bit to give room for some arithmetic on the data (shift keeps, etc.)
                                      check_int3y:=limits(h_minint,h_maxint,(p3.Y+yd)*sy+by-gy,dummy_i);

                                      check_int4x:=limits(h_minint,h_maxint,p4.X*sx+ex-gx,dummy_i);
                                      check_int4y:=limits(h_minint,h_maxint,(p4.Y+yd)*sy+by-gy,dummy_i);

                                      infill_points[0].X:=Round(check_int1x);
                                      infill_points[0].Y:=Round(check_int1y);

                                      infill_points[1].X:=Round(check_int2x);
                                      infill_points[1].Y:=Round(check_int2y);

                                      infill_points[2].X:=Round(check_int3x);
                                      infill_points[2].Y:=Round(check_int3y);

                                      infill_points[3].X:=Round(check_int4x);
                                      infill_points[3].Y:=Round(check_int4y);

                                      if (code=484) or (code=485) or (code=493) or (code=494) or (code=497)         // 221a  chair outlines
                                         then begin
                                                for n:=0 to 27 do begin               // 221a  was 23
                                                  infill_points[n+4].X:=Round(limits(h_minint,h_maxint,p[n].X*sx+ex-gx,dummy_i));
                                                  infill_points[n+4].Y:=Round(limits(h_minint,h_maxint,(p[n].Y+yd)*sy+by-gy,dummy_i));
                                                end;//next
                                              end;


                                      if (check_limits(infill_points[0],infill_points[1])=True) and (check_limits(infill_points[2],infill_points[3])=True)    // 221a ignore checks on remaining points for chairs
                                         then begin
                                                if Pen.Color=paper_colour then begin                          // erasing...
                                                                                 Brush.Color:=paper_colour;
                                                                                 Brush.Style:=bsSolid;        // always a solid infill on erase, in case of problems with hatched infill (e.g. Tony Miles' version of Windows).
                                                                               end
                                                                          else begin
                                                                                 Pen.Color:=paper_colour;
                                                                                 Brush.Color:=timber_infill_colour;

                                                                                 case pad_timb_infill_style of
                                                                                     0: CONTINUE;
                                                                                     1: Brush.Style:=bsFDiagonal;  // hatched, forward diagonal in foreground (control template).
                                                                                     2: Brush.Style:=bsDiagCross;
                                                                                     3: Brush.Style:=bsSolid;
                                                                                     4: begin                     // blank.
                                                                                          Brush.Style:=bsSolid;
                                                                                          Brush.Color:=paper_colour;   // overide.
                                                                                        end;
                                                                                   else CONTINUE;                 // ??
                                                                                 end;//case

                                                                                         // shoved timber overides...

                                                                                 if code=293        // shoved but not selected.
                                                                                    then begin
                                                                                           if paper_colour<>clBlue then Brush.Color:=clBlue
                                                                                                                   else Brush.Color:=clWhite;
                                                                                         end;

                                                                                 if code=233        // currently selected for shoving.
                                                                                    then begin
                                                                                           if paper_colour<>clRed then Brush.Color:=clRed
                                                                                                                  else Brush.Color:=clYellow;
                                                                                         end;

                                                                                         // chair outline overides...

                                                                                 if (code=484) or (code=485) or (code=493) or (code=494) or (code=497)        // chair outlines
                                                                                    then begin
                                                                                           Pen.Color:=timber_colour;
                                                                                           Brush.Color:=$00D0D0E0;
                                                                                           Brush.Style:=bsSolid;
                                                                                         end;

                                                                                         // 241c switch-drive rib ..
                                                                                         
                                                                                 if code=487
                                                                                    then begin
                                                                                           Pen.Color:=clBlack;
                                                                                           Brush.Color:=$00D0D0E0;
                                                                                           Brush.Style:=bsDiagCross;
                                                                                         end;

                                                                               end;

                                                if (code=484) or (code=485) or (code=493) or (code=494) or (code=497)
                                                   then Polygon(Slice(infill_points,28))      // chair outline with radiused corners
                                                   else Polygon(Slice(infill_points,4));      // timber infill, chair with square corners
                                              end;
                                    end;

                            if (code>=441) and (code<=478) and (brick_form.show_labels_checkbox.Checked=True)    // MW 03-08-2024  555a
                               then begin
                                      chair_label_str:=get_chair_str(list_chair_code);

                                      if list_chair_code=14      // over-ride check flare-in from "CCL/R"
                                         then case code of
                                            441,461: chair_label_str:='CCL';     // MS LH TEMPLATE - TS RH TEMPLATE
                                            451,471: chair_label_str:='CCR';     // TS LH TEMPLATE - MS RH TEMPLATE
                                              end;

                                      if list_chair_code=16      // over-ride check flare-out from "CCR/L"
                                         then case code of
                                            441,461: chair_label_str:='CCR';     // MS LH TEMPLATE - TS RH TEMPLATE
                                            451,471: chair_label_str:='CCL';     // TS LH TEMPLATE - MS RH TEMPLATE
                                              end;

                                      move_to.X:=Round(check_int1x);
                                      move_to.Y:=Round(check_int1y);

                                      if check_limit(True,True,move_to)=True
                                         then begin
                                                enter_timber_form.Font.Assign(pad_form.Font);  // use to save pad font for reset

                                                Font.Assign(pad_form.pad_timber_font_label.Font);    // for font name

                                                Font.Style:=[fsBold];

                                                Font.Height:=Round(0-4.5*inscale/ffx);     // scale 4.5" arbitrary    ffx mm per screen dot

                                                half_stringwidth:=TextWidth(chair_label_str) div 2;
                                                half_stringheight:=TextHeight(chair_label_str) div 2;

                                                case code of
                                                  441..458: begin                       // selected chair label
                                                              Brush.Style:=bsSolid;
                                                              Brush.Color:=clYellow;

                                                              Font.Color:=clBlack;
                                                            end;

                                                  461..478: begin
                                                              Brush.Style:=bsClear;     // chair label not selected

                                                              if mouse_is_over_chair_label_mark=i
                                                                 then begin
                                                                        Brush.Style:=bsSolid;
                                                                        Brush.Color:=clLime;       // mouse is over it
                                                                      end;

                                                              Font.Color:=clBlack;
                                                            end;
                                                end;//case

                                                Pen.Width:=1;       // draw the rectangle first...
                                                Pen.Mode:=pmCopy;
                                                Pen.Style:=psSolid;
                                                Pen.Color:=Font.Color;

                                                RoundRect(move_to.X-half_stringwidth-Round(ABS(Font.Height/2)), move_to.Y-half_stringheight-2, move_to.X+half_stringwidth+Round(ABS(Font.Height/2)), move_to.Y+half_stringheight+2, Round(ABS(Font.Height/2.5)), Round(ABS(Font.Height/2.5)) );   // font/2 padding, font/2.5 corner rads, arbitrary

                                                TextOut(move_to.X-half_stringwidth, move_to.Y-half_stringheight, chair_label_str);

                                                Font.Assign(enter_timber_form.Font);  // reset..
                                                Brush.Color:=paper_colour;
                                              end;

                                    end;
                     end;
                end
          else begin   // 99 or 501+  0.94.a

                 if code=99
                    then begin
                           if pad_timber_numbers=True // code=99, text mark (timber numbering).
                              then begin
                                     check_int1x:=limits(h_minint,h_maxint,p1.X*sx+ex-gx,dummy_i);      // h_min, h_max 31 bit to give room for some arithmetic on the data (shift keeps, etc.)
                                     check_int1y:=limits(h_minint,h_maxint,(p1.Y+yd)*sy+by-gy,dummy_i);

                                     move_to.X:=Round(check_int1x);  move_to.Y:=Round(check_int1y);

                                     num_str:=extract_tbnumber_str(tbnum_str); // get next timber numbering string from the acummulated string.
                                     if num_str='' then CONTINUE;              // no string available??

                                     if check_limit(True,True,move_to)=True
                                        then begin
                                               if num_str[1]='!'   // it's an omitted timber
                                                  then begin
                                                         Delete(num_str,1,1);    // remove the omit indicator.
                                                         omitted:=True;
                                                       end
                                                  else omitted:=False;

                                               Font.Assign(pad_form.pad_timber_font_label.Font);

                                               half_stringwidth:=TextWidth(num_str) div 2;
                                               half_stringheight:=TextHeight(num_str) div 2;

                                               Brush.Color:=paper_colour;
                                               Brush.Style:=bsSolid;

                                               if ink=False
                                                  then Font.Color:=paper_colour             // erasing.
                                                  else if shove_timber_form.Showing=True
                                                          then begin
                                                                      // see if current timber number is selected for shoving..

                                                                 temp_str:=timb_num_strip(num_str);

                                                                 if (num_str=current_shove_str) or (temp_str=current_shove_str)  or (omitted=True)   // needs a rectangle box.
                                                                    then begin
                                                                           if (num_str=current_shove_str) or (temp_str=current_shove_str)   // shoving this one?
                                                                              then begin
                                                                                     if paper_colour=clYellow
                                                                                        then begin
                                                                                               Brush.Color:=clBlack;      // highlight selected timber number for shoving.
                                                                                               Font.Color:=clWhite;
                                                                                             end
                                                                                        else begin
                                                                                               Brush.Color:=clYellow;
                                                                                               Font.Color:=clBlack;
                                                                                             end;
                                                                                   end;

                                                                           Pen.Width:=1;       // looks neater if we draw/highlight the rectangle first...
                                                                           Pen.Mode:=pmCopy;
                                                                           Pen.Style:=psSolid;
                                                                           Pen.Color:=Font.Color;
                                                                           Rectangle(move_to.X-half_stringwidth-3,move_to.Y-half_stringheight-2,move_to.X+half_stringwidth+4,move_to.Y+half_stringheight+3);
                                                                         end;
                                                               end;//if shove_timber showing

                                               TextOut(move_to.X-half_stringwidth,move_to.Y-half_stringheight,num_str);
                                               Font.Assign(pad_form.Font);
                                               Brush.Color:=paper_colour;
                                             end;
                                   end;//numbers wanted
                         end
                    else begin     // 501+  0.94.a  check-rail labels ...

                           if check_diffs_form.Showing=True
                              then begin
                                     check_int1x:=limits(h_minint,h_maxint,p1.X*sx+ex-gx,dummy_i);      // h_min, h_max 31 bit to give room for some arithmetic on the data (shift keeps, etc.)
                                     check_int1y:=limits(h_minint,h_maxint,(p1.Y+yd)*sy+by-gy,dummy_i);

                                     move_to.X:=Round(check_int1x);  move_to.Y:=Round(check_int1y);

                                     case code of

                                          501: num_str:='MS1';
                                          502: num_str:='MS2';
                                          503: num_str:='MS3';
                                          504: if half_diamond=True then num_str:='DS1' else num_str:='TS1';
                                          505: if half_diamond=True then num_str:='DS2' else num_str:='TS2';
                                          506: if half_diamond=True then num_str:='DS3' else num_str:='TS3';
                                          507: num_str:='MS4';
                                          508: num_str:='DS4';

                                          else num_str:='';

                                     end;//case

                                     if check_limit(True,True,move_to)=True
                                        then begin
                                               Font.Assign(pad_form.pad_timber_font_label.Font);

                                               half_stringwidth:=TextWidth(num_str) div 2;
                                               half_stringheight:=TextHeight(num_str) div 2;

                                               Brush.Color:=paper_colour;
                                               Brush.Style:=bsSolid;

                                               if ink=False
                                                  then Font.Color:=paper_colour             // erasing.
                                                  else begin
                                                              // see if current check-end is selected for diffing..
                                                         if code=current_diff_code
                                                            then begin
                                                                   if paper_colour=clYellow
                                                                      then begin
                                                                             Brush.Color:=clBlack;      // highlight selected timber number for shoving.
                                                                             Font.Color:=clWhite;
                                                                           end
                                                                      else begin
                                                                             Brush.Color:=clYellow;
                                                                             Font.Color:=clBlack;
                                                                           end;
                                                                 end;

                                                         Pen.Width:=1;       // draw the rectangle first...
                                                         Pen.Mode:=pmCopy;
                                                         Pen.Style:=psSolid;
                                                         Pen.Color:=Font.Color;
                                                         RoundRect(move_to.X-half_stringwidth-3,move_to.Y-half_stringheight-2,move_to.X+half_stringwidth+4,move_to.Y+half_stringheight+3,6,6);
                                                       end;//ink=True

                                               TextOut(move_to.X-half_stringwidth,move_to.Y-half_stringheight,num_str);
                                               Font.Assign(pad_form.Font);
                                               Brush.Color:=paper_colour;
                                             end;//check_limits

                                   end;//check form showing
                         end;//check label mark
               end;//text mark

        except
          CONTINUE;     // ignore this mark if any calc errors.
        end;//try

      end;//next i mark

              // 226a mods  finally overdraw timber infill for shoved colours...

      if (Length(current_shoved_timbers)>0) and (no_timbering=False) and (timber_fill_overdraw_generated=True)   // 227d
         then begin
                for i:=0 to Length(current_shoved_timbers)-1 do begin

                  with current_shoved_timbers[i] do begin

                    if (shove_data.sv_option_bits AND $40)=0 then CONTINUE;     // 232c  if shove_data.sv_col_has_been_set=False   no modified infill

                    if shoved_mod_infill.shoved_number_str='' then CONTINUE;  // 227e timber didn't get drawn

                    with shoved_mod_infill do begin

                      if shove_data.sv_use_tcol=True // overdraw previous infill on output
                         then begin

                                check_int1x:=limits(h_minint,h_maxint,current_shoved_corners.p1.X*sx+ex-gx,dummy_i);      // h_min, h_max 31 bit to give room for some arithmetic on the data (shift keeps, etc.)
                                check_int1y:=limits(h_minint,h_maxint,(current_shoved_corners.p1.Y+yd)*sy+by-gy,dummy_i);

                                check_int2x:=limits(h_minint,h_maxint,current_shoved_corners.p2.X*sx+ex-gx,dummy_i);
                                check_int2y:=limits(h_minint,h_maxint,(current_shoved_corners.p2.Y+yd)*sy+by-gy,dummy_i);

                                check_int3x:=limits(h_minint,h_maxint,current_shoved_corners.p3.X*sx+ex-gx,dummy_i);      // h_min, h_max 31 bit to give room for some arithmetic on the data (shift keeps, etc.)
                                check_int3y:=limits(h_minint,h_maxint,(current_shoved_corners.p3.Y+yd)*sy+by-gy,dummy_i);

                                check_int4x:=limits(h_minint,h_maxint,current_shoved_corners.p4.X*sx+ex-gx,dummy_i);
                                check_int4y:=limits(h_minint,h_maxint,(current_shoved_corners.p4.Y+yd)*sy+by-gy,dummy_i);

                                infill_points[0].X:=Round(check_int1x);
                                infill_points[0].Y:=Round(check_int1y);

                                infill_points[1].X:=Round(check_int2x);
                                infill_points[1].Y:=Round(check_int2y);

                                infill_points[2].X:=Round(check_int3x);
                                infill_points[2].Y:=Round(check_int3y);

                                infill_points[3].X:=Round(check_int4x);
                                infill_points[3].Y:=Round(check_int4y);

                                if (check_limits(infill_points[0],infill_points[1])=True) and (check_limits(infill_points[2],infill_points[3])=True)    // 221a ignore checks on remaining points for chairs
                                   then begin
                                          Pen.Width:=1;
                                          Pen.Style:=psSolid;
                                          Pen.Mode:=pmCopy;
                                          Pen.Color:=timber_colour;

                                          Brush.Color:=shove_data.sv_tcol;

                                          case shove_data.sv_tcol_infill of

                                              0: Brush.Style:=bsSolid;
                                              1: begin Brush.Color:=clWhite; Brush.Style:=bsSolid; end;  // blank style
                                              2: Brush.Style:=bsHorizontal;
                                              3: Brush.Style:=bsVertical;
                                              4: Brush.Style:=bsFDiagonal;
                                              5: Brush.Style:=bsBDiagonal;
                                              6: Brush.Style:=bsCross;
                                              7: Brush.Style:=bsDiagCross;
                                            else Brush.Style:=bsSolid;

                                          end;//case

                                          Polygon(Slice(infill_points,4));   // number of points, not index

                                        end;
                              end;//use tcol
                    end;//with
                  end;//with
                end;//next

              end;//if any

      draw_rings(on_canvas,False,False);   //  re-draw the spacing-ring  (not copies).

  end;//with Canvas
  RESULT:=True;
end;
//______________________________________________________________________________

procedure calctimbers;               //  calc all timbering

var
 n:integer;
 tbnext, tbint:integer;
 last_xtb:extended;
 exit_begin:extended;
 full_length:boolean;

 eqfix_closeup, xtbclose_start:extended;

 xingtb_w, xingtb_v:extended;
 bontimb_posx:extended;

 mod_for_curvi:boolean;               // 215a ...
 unmod_xtb,curvi_alpha:extended;
 xtb_mod:extended;

 timbered_joint_adjusted:boolean;  // 227a

begin
  //debug_str:='debug: ';

  ms_ccl_done_clx:=0; // 243a    init...
  ms_ccr_done_clx:=0; // 243a

  ts_ccl_done_clx:=0; // 243a
  ts_ccr_done_clx:=0; // 243a

  heel_blankingx:=0;  // 243a
  heel_lengthx:=0;    // 243a

  xing_blankingx:=0;  // 243a
  xing_lengthx:=0;    // 243a

  chair_count:=0;            // init for experimental chairing
  approach_last_xtb:=0;      // init for length snapping...
  exit_last_xtb:=turnoutx;

  SetLength(current_jaws,0);   // clear the 3D jaws array
  SetLength(current_seats,0);  // clear the 3D seats array
  SetLength(current_keys,0);   // clear the 3D keys array
  SetLength(current_jmods,0);  // clear the 3D jmods array

  //SetLength(current_normal_chairs,0);    // clear array of normal chairs 243b

  if (half_diamond=True) and (hd_timbers>0)     // init for slip timber extensions...
     then begin
            sliptipsl:=fpx-toex-scale*k3n;   // from hd toe to slip-switch toe (arbitrary where wing rails 12" separation).
            sliprad:=sliptipsl/TAN(k3/2);    // approx slip road radius.
          end;

  if timber_marks=True
     then begin

            if outline_extensions=True
               then tbl:=4.5*inscale             // outline extensions = 4.5" scale
               else tbl:=0;

            tbq:=scale;              // centre-line extensions = 1ft scale

            if nine_foot=True then tbred:=3*inscale     // standard 9ft timbering, with
                                                        // 3" length reduction each end for 8ft 6" timbers.
                              else tbred:=0;            // non-standard timber lengths, no reductions.

            ynsnorm:=0-(tb-g)/2;   // timber near-end same for all xtb (before randomizing)

                    // 208a mods...

            tbnumy_screen:=ynsnorm-tbq-1.5;  // default y-position of timber numbering. 1.5mm below end of timber centre-line (only used if timber centre-lines not drawn).

            tbnumy_output:=ynsnorm-tbq-6;    // 208a mod   // default y-position of timber numbering. 6mm below end of timber centre-line (only used if timber centre-lines not drawn).

            frackeq:=0;           // ensure switch timbers square-on. 29-3-99.

            if gaunt=False then switchtimbers    // first do switch timbers, end sleepers, and approach track.
                           else begin
                                  if xorg>0 then plain_sleepers(xorg,-1,True,False);   // approach plain-track wanted on gaunt template.
                                  xtbswend:=xorg-(psleep[pt_i,0]*inscale);             // start at A1 timber for closure timbering.
                                end;

                 // now do crossing timbers from Z timber on...

            if plain_track=False
               then begin

                      if (include_xing_timbers=True) and (k3n>=1.5) // 228
                         then begin

                                timb_str:='X';        // restart numbering. "X" for crossing
                                tbn:=1;

                                       // mod 0.75.a 10-10-01...

                                xtba:=bnx+{4}bn_to_a*inscale;   // A timber from blunt nose

                                xingtb_w:=wingtimb_sp*inscale;    // wing front timber spacing.
                                xingtb_v:=veetimb_sp*inscale;     // vee timber spacing.

                                xtbz:=xtba-wing_tbcount*xingtb_w; // x to first crossing timber (usually Z, count of 3).

                                if (half_diamond=True) or (timbers_equalized=True) or (square_on_angled=True) or (force_eq=True)  // 239b         // mod 29-7-01.
                                   then frackeq:=1    // full equalizing angle or angled-on through crossing.
                                   else frackeq:=0;   // square-on through crossing. 29-3-99.

                                xtb:=xtbz;    // first wing front timber.

                                tb_xing_end:=xtba-(xingtb_w/2);  // wing front timbers don't include "A".

                                repeat
                                  dotimber(False,0,0);   // do at timber width.

                                  last_xtb:=xtb;           // save position of last one.
                                  xtb:=xtb+xingtb_w;       // all at wing front spacing.
                                until xtb>tb_xing_end;

                                xtb:=xtba;        // now first vee timber "A".

                                case exittb_i of
                                             0,2: tb_xing_end:=mvjp_nx-(mvj_sp*inscale)+minfp;  // none, or exit sleepering start.
                                               1: tb_xing_end:=turnoutx;                        // long timbers continue.
                                             else run_error(129);
                                    end;//case

                                xtb_mod:=0;                      // 215a init..
                                timbered_joint_adjusted:=False;  // init  227a

                                repeat
                                        // 215a mods for curviform crossings, shorten spacings to suit standard chairs ..
                                        // 227a improvements...

                                  unmod_xtb:=xtb;            // so can restore for spacing calc...
                                  mod_for_curvi:=False;      // init

                                  if (curviform_timbering=True) and (tradius_is_straight=False) and (xing_calc_i=1)
                                  and (xtb>(xtba+xingtb_v)) and (xtb<mvjp_nx)                                         // only for timbers "C" and above inside joint

                                     then begin
                                            mod_for_curvi:=True;
                                            try
                                              curvi_alpha:=(xtb-fpx)/ABS(tradius);              // angle turned through beyond FP.
                                              xtb_mod:=ABS(tradius)*(1-COS(curvi_alpha))*k3n;   // adjust chair position to maintain vee rail separation

                                              xtb:=xtb-xtb_mod;    // and mod this one for the chair

                                              if xtb>(mvjp_nx-mvj_sp*inscale-15*inscale)
                                                 then xtb:=mvjp_nx-mvj_sp*inscale-15*inscale;     // prevent overlap on inserted joint timber (15" centres)

                                            except
                                              xtb:=unmod_xtb;         // abandon mod
                                              xtb_mod:=0;
                                              mod_for_curvi:=False;
                                            end;//try

                                          end;

                                  if xtb>(tb_xing_end-inscale)         // 233d
                                     then dotimber(False,1,0)          // adjacent rail joint
                                     else dotimber(False,0,0);         // do at xtb, timber width.

                                  if mod_for_curvi=True          // 215a
                                     then begin
                                            xtb:=unmod_xtb;                              // restore unmodded for next
                                            if (ABS(xtb-(mvjp_nx-mvj_sp*inscale))<minfp) // was joint timber?
                                               then begin
                                                      xtb:=mvjp_nx-mvj_sp*inscale;       // joint timber was modded away from joint
                                                      dotimber(False,1,0);               // so insert extra timber at joint
                                                    end;
                                          end;

                                  last_xtb:=xtb;         // save position of last one.
                                  xtb:=xtb+xingtb_v;     // all at vee spacing.

                                  if (timbered_joint_adjusted=False) and (xtb>mvjp_nx)    // 227a
                                     then begin
                                            xtb:=mvjp_nx+mvj_sp*inscale;        // adjust to joint spacing
                                            timbered_joint_adjusted:=True;    // only once, then continue at previous spacings
                                          end;

                                until xtb>(tb_xing_end+inscale);   // 1" added to ensure end timber is hit


                              end

                         else begin    // no xing timbers   218a ...

                                last_xtb:=mvjp_nx-(mvj_sp*inscale);   // so start exit timbering at last timber before MVJP

                                xtba:=bnx+bn_to_a*inscale;          // A timber from blunt nose
                                xingtb_w:=wingtimb_sp*inscale;      // wing front timber spacing.
                                xtbz:=xtba-wing_tbcount*xingtb_w;   // for closure timbers     x to first crossing timber (usually Z, count of 3).

                                               // for wing rail joints ...

                                if (half_diamond=True) and (hd_timbers<>0)   // 215a this is a slip
                                   then xtbzz:=xtbz-28*inscale         // 215a so use 28" next spacing
                                   else xtbzz:=xtbz-wingj_sp*inscale;  // move back joint spacing from Z. mod 0.75.a 11-10-01.
                              end;

                      if (include_closure_timbers=True) and (k3n>=1.5) // 228a
                         then begin

                                   // then fill closure timbers between switch and crossing...

                                timb_str:='T';        // restart closure numbering.

                                if gaunt=True
                                   then tbn:=1
                                   else tbn:=2;       // first closure timber has been drawn with the switch.

                                if (half_diamond=True) and (hd_timbers<>0)   // 215a this is a slip

                                   then xtbzz:=xtbz-28*inscale   // 215a so use 28" next spacing

                                   else xtbzz:=xtbz-wingj_sp*inscale;  // move back joint spacing from Z. mod 0.75.a 11-10-01.

                                if ( (timbers_equalized=True) or (force_eq=True) ) and (equalizing_fixed=True) and (half_diamond=False) and (ABS(k3n)>minfp)
                                   then begin
                                          eqfix_closeup:=20*inscale/k3n;                                  // 20"/xing RAM arbitrary - close up a bit for constant equalizing.
                                          if eqfix_closeup>(6*inscale) then eqfix_closeup:=6*inscale;     // but not more than 6"

                                          xtbclose_start:=xtbswend-eqfix_closeup;
                                        end
                                   else xtbclose_start:=xtbswend;

                                xclose:=xtbzz-xtbclose_start;                  // length of closure rails between timbers already drawn.
                                tbints:=xclose/(ftimbspmax*inscale);           // number of max intervals in this length.

                                tbint:=do_truncx(tbints);
                                if do_fracx(tbints)>minfp then tbint:=tbint+1;  // round up the number of intervals unless max spacing fits exactly.

                                if tbint<1 then tbint:=1;     // no division by zero.
                                xclosespace:=xclose/tbint;    // spacing for each interval

                                for tbnext:=1 to tbint do begin
                                  xtb:=xtbclose_start+tbnext*xclosespace;      // x to next timber

                                  if half_diamond=True
                                     then frackeq:=1      // ignore flags, always constant.
                                     else begin

                                            if (timbers_equalized=True) or (force_eq=True)  // 239b
                                               then begin
                                                      if equalizing_fixed=True then frackeq:=1              // constant equalizing angle.
                                                                               else frackeq:=tbnext/tbint;  // incremental, so fraction of the equalizing angle to avoid sudden change at heel of switch.
                                                    end
                                               else begin
                                                      if square_on_angled=True then frackeq:=1   // angled-on 20-7-01
                                                                               else frackeq:=0;  // square-on closure timbers. 29-3-99.
                                                    end;
                                          end;

                                  dotimber(False,0,0);
                                end;//next
                              end;

                           // finally add exit sleepering if any ...

                      if exittb_i=2
                         then begin
                                full_length:=True;
                               
                                frackeq:=0;

                                if last_xtb>(mvjp_nx-(mvj_sp*inscale)+minfp)
                                   then exit_begin:=last_xtb+(mvj_sp*inscale)   // crossover with wide spacing? - space exit joint beyond last timber drawn.
                                   else exit_begin:=mvjp_nx;                    // normal - rail joint for exit sleepers.

                                plain_sleepers(exit_begin,1,full_length,False);

                                if retpar_i=1    // and finally any return curve sleepering...
                                   then begin
                                          full_length:=True;
                                          frackeq:=0;                                     // square-on.

                                          plain_sleepers(exit_begin+(22.5-k3n/2)*inscale,1,full_length,True);  // +(22.5-k3n/2)" interlacing (arbitrary).
                                        end;
                              end;//exit sleepering
                    end;//if turnout

                        // 0.76.a 23-10-01 finally add any bonus timbers...

            if bontimb>0
               then begin
                      timb_str:='B';        //  prefix for bonus timber numbering.
                      tbn:=1;               //  init numbering.

                      if plain_track=True then bontimb_posx:=turnoutx-tb_roll_percent*railen[pt_i]*inscale/100  // adjust for any timber rolling.
                                          else bontimb_posx:=xorg;  // for turnout always at CTRL-1

                      for n:=1 to bontimb do endsleeper(bontimb_posx,True,False,0,0,1);

                    end;

          end;//if timber_marks

end;
//_____________________________________________________________________________________________

procedure plain_sleepers(xtb:extended; dir:integer; full_length,retcurve:boolean);     // dir = direction  -1 = approach tracks, +1 = exit tracks

var
  xrail:extended;
  p1, p2:Tpex;
  first_joint:boolean;
  sl_space,tb_roll_mm:extended;
  ret_offset,ret_k:extended;
  odd_rail:boolean;

  key_towards:integer;  //233d     -1/0/+1   for keying towards nearest joint  0= not a plain track sleeper

  joint_sl:integer;     // 233d    -1/0/+1  for keying towards fishplate    0=not a joint sleeper

                        ////////////////////////////////////////////////////////////

                        procedure fill_joints_and_timbers;

                        var
                          slco:integer;

                        begin
                          repeat
                            if (joint_marks=True) and (rjcode>-1)                         // first mark the next rail-joint.   rjcode: 0=normal, 1=staggered, -1=none (cwr).
                            and ( NOT ( (plain_track=True) and (xtb>(xorg-pt_blank)) ) )  // 234d plain track blanking
                               then begin
                                      if first_joint=False           // skip the point rail joint
                                         then begin
                                                if retcurve=True then ret_offset:=aq25offset(xtb,ret_k)-g/2
                                                                 else ret_offset:=0;

                                                if (odd_rail=True) or (rjcode=0)  // staggered or normal, main-side rail.
                                                   then begin
                                                          p1.x:=xtb;
                                                          p1.y:=gmi+ret_offset;
                                                          if retcurve=True then dotransform(ret_k, xtb, g/2, p1, p1);       // twist joint marks.

                                                          p2.x:=xtb;
                                                          p2.y:=0-j-gmo+ret_offset;
                                                          if retcurve=True then dotransform(ret_k, xtb, g/2, p2, p2);       // twist joint marks.

                                                          if  ( ((main_road_stock_rail_flag=True) and ((timb_str='A') or (timb_str='E') or (timb_str='N')))
                                                          or  ((crossing_vee_flag=True) and (timb_str='R')) )
                                                          and (omit_mspt_marks=False)             // 227a
                                                             then enter_mark(True,p1,p2,6,'');    // make rail-joint mark, straight stock rail.
                                                        end;

                                                if (odd_rail=False) or (rjcode=0)  // staggered or normal, turnout-side rail.
                                                   then begin
                                                          p1.x:=xtb;
                                                          p1.y:=g-gmi+ret_offset;
                                                          if retcurve=True then dotransform(ret_k, xtb, g/2, p1, p1);       // twist joint marks.

                                                          p2.x:=xtb;
                                                          p2.y:=g+j+gmo+ret_offset;
                                                          if retcurve=True then dotransform(ret_k, xtb, g/2, p2, p2);       // twist joint marks.

                                                          if ( ((turnout_road_stock_rail_flag=True) and ((timb_str='A') or (timb_str='R') or (timb_str='N')))
                                                          or ((crossing_vee_flag=True) and (timb_str='E')) )
                                                          and (omit_tspt_marks=False)             // 227a
                                                             then enter_mark(True,p1,p2,6,'');    // make rail-joint mark, curved stock rail.
                                                        end;
                                              end;

                                      first_joint:=False;          // do all remaining joints.
                                      odd_rail:= NOT odd_rail;     // for staggered joints.
                                    end;

                            for slco:=0 to psleep_c do begin              // do the sleepers for a rail length.

                              if slco=0 then joint_sl:=0-dir                      // 233d mods   joint sleeper
                                        else if slco=(sleeper_count[pt_i]-1)
                                                then joint_sl:=dir
                                                else joint_sl:=0;             // not a joint sleeper

                              if slco<(sleeper_count[pt_i] div 2)   // 233d  for keying towards nearest joint
                                 then key_towards:=0-dir
                                 else key_towards:=dir;

                              sl_space:=psleep[pt_i,slco];
                              if sl_space<minfp then BREAK;               // no more spacing data.

                              xtb:=xtb+dir*sl_space*inscale;

                              case dir of
                                    -1: if xtb<(xrail-railen[pt_i]*inscale) then BREAK; // gone past end of this rail..
                                     1: if xtb>(xrail+railen[pt_i]*inscale) then BREAK;
                                   else run_error(216);
                              end;//case

                              if (xtb<(0-scale/3)) or (xtb>(turnoutx+scale/3))
                                 then begin
                                        if timb_str<>'N'
                                           then BREAK    // gone far enough on this rail (4 inches past rail end).
                                           else begin
                                                  INC(tbn);     // keep rolled timber numbering in line (for shoves).
                                                  CONTINUE;     // timber rolling (turnoutx might be less than the roll, so first rolled timbers negative xtb, but may want subsequent ones).
                                                end;
                                      end;

                              if (xtb>fpx+k3n*4.5*scale) or (dir=-1)
                                 then endsleeper(xtb,True,retcurve,joint_sl,key_towards,dir)         // draw normal sleepering beyond 4ft6in scale vee separation, or for approach track.
                                 else endsleeper(xtb,full_length,retcurve,joint_sl,key_towards,dir); // might be reduced sleepers for crossover exit.

                              if retcurve=False
                                 then begin
                                        case dir of
                                             -1: approach_last_xtb:=xtb;   // save last xtb drawn for length snapping...
                                              1: exit_last_xtb:=xtb;
                                        end;//case
                                      end;

                            end;//for

                            case dir of
                                  -1: if xtb<(0-scale/3) then BREAK;            // don't do any more rails.
                                   1: if xtb>(turnoutx+scale/3) then BREAK;
                                 else run_error(218);
                            end;//case

                            xrail:=xrail+dir*railen[pt_i]*inscale;    // to the next rail.
                            xtb:=xrail;

                          until 0<>0;
                        end;
                        /////////////////////////////////////////////////////////////

begin
  if railen[pt_i]=0 then run_error(151);        // no plain track data.

  tb_roll_mm:=tb_roll_percent*railen[pt_i]*inscale/100;  // timber rolling.

  if retcurve=False
     then begin
            case dir of
                 -1: approach_last_xtb:=0;      // init for length snapping...
                  1: exit_last_xtb:=turnoutx;
            end;//case
          end;

  if dir=1
     then begin
            first_joint:=True;                     // point rail joints already drawn.
            if retcurve=True then timb_str:='R'    //  prefix numbering for return curve.
                             else timb_str:='E';   //  prefix for numbering exit plain track.
          end
     else begin
            first_joint:=False;   // but not if approach track or plain track.
            timb_str:='A';        // prefix for numbering approach plain track.
          end;

  if plain_track=True then xtb:=xtb-tb_roll_mm;  // timber rolling offset 0.76.a 13-5-02
                                                 // (first xtb is on turnoutx).

  tbn:=1;           // start numbering.
  xrail:=xtb;       // init for rail lengths.
  odd_rail:=False;  // init for staggered rails

  fill_joints_and_timbers;

          // now add any timbering in the rolled section, if any...

  if (plain_track=True) and (tb_roll_mm>minfp)
     and (rolled_in_sleepered=True)              // 223a
     then begin
            timb_str:='N';             // "new" inserted sleepers.
            xtb:=turnoutx-tb_roll_mm;  // start from first joint already done...
            dir:=1;
            first_joint:=True;

            tbn:=1;           // re-start numbering.
            xrail:=xtb;       // init for rail lengths.
            odd_rail:=False;  // init for staggered rails

            fill_joints_and_timbers;
          end;
end;
//_______________________________________________________________________________________________________________________________

procedure toetimber(joint:boolean);   //  deal with switch front sleepers or timbers.

  // joint=True means adjacent to rail joint (may be wide joint sleeper if switch front sleepered)  // 212a

var
  sf_width:extended;

begin

  if include_front_timbers=False then EXIT; //218a

  if xtb<(0-scale) then EXIT;             // min 12" outside datum. mod 0.77.b
  if xtb>(turnoutx+scale/3) then EXIT;    // max 4" beyond rail end (much shortened template?).

  timberend(1);     // get yfs, yfsred to sleeper end

  if (pad_form.timber_centres_menu_entry.Checked=True) or (exp_chairing=True) then drawtimbcl(False);   // sleeper centre-lines wanted.    or needed for continuous check rail chairs 237b

  if pad_form.timber_outlines_menu_entry.Checked=True                          // sleeper outlines wanted.
     then begin
            if csi.front_timbered=True then sf_width:=tbwide    // timber width.
                                       else if joint=True
                                               then sf_width:=jt_slwide    // joint sleeper width.
                                               else sf_width:=slwide;      // sleeper width.

            xns:=xtb-sf_width*inscale/2;      // timber width, ( half each side of centre )
            xfs:=xtb+sf_width*inscale/2;

            if joint=True                              // 233d
               then drawtimber(True,False,-1,1)        // key driven towards fishplate
               else drawtimber(True,False,0,1);
          end;

  if pad_form.timber_numbers_menu_entry.Checked=True then tbnumber(False)  // numbering wanted.
                                                     else tbn:=tbn+1;      // do numbering last because increments tbn. (needed for timber shoves).
end;
//_______________________________________________________________________________________________________________________________

procedure switchtimbers;         // do switch timbering and approach track.
                                 // (no equalizing for switch timbers).
var
  n,swtb:integer;
  xdpb,kwl,ksp:extended;

  dummy:extended;  // 218b

begin                             // for rail-joint marks.
  gmi:=5*inscale;                 // 5" scale length of mark inside gauge-face.
  gmo:=gmi;                       // 5" scale ditto beyond outer-edge.

                 // deal first with switch front timbers...

  timb_str:='J';        //  prefix for numbering joint sleepers at toe.
  tbn:=1;               //  init numbering.

                        // !!! spacings are negative...

  if (plain_track=False) and (half_diamond=False) and (include_switch_timbers=True)   // 218a
     then begin
            repeat
              if csi.sleeper_j1<(0-minfp)
                 then begin
                        xtb:=toex+csi.sleeper_j1*inscale; // first switch front timber J1 (next to toe).
                        toetimber(csi.sleeper_j2=0);
                      end
                 else BREAK;

              if csi.sleeper_j2<(0-minfp)
                 then begin
                        xtb:=xtb+csi.sleeper_j2*inscale; // next switch front timber J2.
                        toetimber(csi.sleeper_j3=0);
                      end
                 else BREAK;

              if csi.sleeper_j3<(0-minfp)
                 then begin
                        xtb:=xtb+csi.sleeper_j3*inscale; // next switch front timber J3.
                        toetimber(csi.sleeper_j4=0);
                      end
                 else BREAK;

              if csi.sleeper_j4<(0-minfp)
                 then begin
                        xtb:=xtb+csi.sleeper_j4*inscale; // next switch front timber J4.
                        toetimber(csi.sleeper_j5=0);
                      end
                 else BREAK;

              if csi.sleeper_j5<(0-minfp)
                 then begin
                        xtb:=xtb+csi.sleeper_j5*inscale; // next switch front timber J5.
                        toetimber(True);                // True = joint sleeper  212a
                      end
                 else BREAK;
              BREAK;
            until 0<>0;
          end;

  if (xorg>0)  and (half_diamond=False) then plain_sleepers(xorg,-1,True,False);     // approach plain-track wanted.

                        // now do the switch timbering...

  if plain_track=False
     then begin
            if (half_diamond=False)       // turnout or...
            or ((half_diamond=True) and (fixed_diamond=False) and (hd_switch_timbering=True))  // 213a
               then begin
                      swtb:=0;             // initialise switch timber number
                      xtb:=toex;           // switch timber centres start from toe.

                      swend_for_tandem:=0;  // init 218b

                      tbn:=1;         // restart numbering for switch.
                      timb_str:='S';  // prefix for switch timber numbers.

                      swbegin_for_tandem:=toex+csi.timber_centres[0]*inscale;  // 218a  x to S1 timber

                      repeat
                        // if csi.timber_centres[swtb+1]=0 then swend_for_tandem:=xtb;   // 218a  x to last S timber

                        xtb:=xtb+csi.timber_centres[swtb]*inscale;  // get next timber centre
                        if csi.timber_centres[swtb+1]=0             // this is actually the first closure timber
                           then begin
                                  timb_str:='T';                 // so reset numbering
                                  tbn:=1;
                                  if (timbers_equalized=False) and (square_on_angled=True) and (force_eq=False)  // 239b
                                     then frackeq:=1  // 29-7-01 angled-on style.
                                     else frackeq:=0;
                                end
                           else frackeq:=0; // switch timbering square-on.

                        if ( ((timb_str<>'T') and (include_switch_timbers=True)) or ((timb_str='T') and (include_closure_timbers=True)) ) and (k3n>=1.5)  // 228a
                           then dotimber(False,0,0);

                        if (aq2offset(xtb,dummy)>(10.5*inscale)) and (swend_for_tandem=0) then swend_for_tandem:=xtb; // 218b find location for next tandem switch   more than 10.5" offset needed for opening

                        INC(swtb);                           // to the next timber
                      until csi.timber_centres[swtb]=0;      // no more switch timbers

                      if swend_for_tandem=0 then swend_for_tandem:=xtb;   // ??? GWR switch, set last switch timber regardless of offset

                      xtbswend:=xtb;     // save last xtb for closure fill.

                                      //   ( rail-joint spacing is included in switch timbering,
                    end               //     so the first closure timber has been drawn.)
               else begin
                      tbn:=1;         // restart numbering for half-diamond.
                      timb_str:='K';  // prefix for k-crossing timber numbers.
                      frackeq:=1;     // equalized constant;

                      if fixed_diamond=False  // switch diamond...
                         then begin

                                if hdkn>=1.5     // 228a
                                   then begin
                                          // half-k-wing rail = 18'0" = 216"  BH = 0 + 24 + 6*30 + 12 to joint.
                                          //                    16'1" = 193"  FB = 11 + 6*28 + 14 to joint.

                                          case rail_section of
                                           0,1: begin                    // bullhead or no rails.
                                                  xtb:=0;                // centre timber for bullhead switch diamonds.
                                                  dotimber(False,0,0);

                                                  xtb:=xtb+24*inscale;    // 24" spacing for next.
                                                  dotimber(False,0,0);

                                                  for n:=0 to 5 do begin    // 6 spaces
                                                    xtb:=xtb+30*inscale;    // at 30" spacing.
                                                    dotimber(False,0,0);
                                                  end;

                                                  timb_str:='T';     // reset numbering for fill - this the first..
                                                  tbn:=1;

                                                  xtb:=xtb+24*inscale;                                            // k-wing rail 12" joint.
                                                  if include_closure_timbers=True
                                                     then dotimber(False,0,0);

                                                end;

                                             2: begin                  // flat-bottom.
                                                  xtb:=11*inscale;     // separate timbers for flat-bottom switch diamonds, tips 4.5" from centre.
                                                  dotimber(False,0,0);

                                                  for n:=0 to 5 do begin    // 6 spaces
                                                    xtb:=xtb+28*inscale;    // at 28" spacing.
                                                    dotimber(False,0,0);
                                                  end;

                                                  timb_str:='T';     // reset numbering for fill - this the first..
                                                  tbn:=1;

                                                  xtb:=xtb+28*inscale;                                           // k-wing rail 14" joint.
                                                  if include_closure_timbers=True
                                                     then dotimber(False,0,0);
                                                end;

                                           end;//case

                                        end;//>1.5
                              end
                         else begin           // fixed diamond...

                                  if hd_proto_timbering=True    // no model corrections
                                     then begin
                                            if ((rail_section<>2) and (hdkn>=5.25))
                                            or ((rail_section=2) and (hdkn>=4.875))
                                               then begin
                                                      xtb:=0;             // centre timber for BH 5.5 or more, FB 5 or more.
                                                      dotimber(False,0,0);
                                                    end;

                                            xtb:=((1.625+bn_wide)*hdkn+5)*inscale;   // 223a diamond point blunt nose + 5", flangeway 1.75" (K-nose 1/8" less than V-nose).    was 6"
                                            dotimber(False,0,0);

                                            if use_k_custom_wing_rails=True      // // 0.95.a
                                               then kwl:=k_custom_wing_long/2
                                               else begin
                                                      if hdkn<=6.375
                                                         then begin
                                                                if rail_section<>2   // BH or none.
                                                                   then kwl:=173/2   //  14'5" BH k-wing rails overall.
                                                                   else kwl:=168/2;  //  14'0" FB.
                                                              end
                                                         else begin
                                                                if rail_section<>2   // BH or none.
                                                                   then kwl:=185/2   //  15'5" BH k-wing rails overall.
                                                                   else kwl:=220/2;  //  18'4" FB.
                                                              end;
                                                    end;

                                            if rail_section<>2 then ksp:=((kwl-12)*inscale-xtb)  // BH 12" = k-wing rail joint.
                                                               else ksp:=((kwl-13)*inscale-xtb); // FB 13" to joint.

                                            if (rail_section<>2) or (hdkn<=6.375)  // BH or none. 2 spaces.
                                               then begin
                                                      xtb:=xtb+ksp/2;
                                                      dotimber(False,0,0);

                                                      xtb:=xtb+ksp/2;
                                                      dotimber(False,0,0);
                                                    end
                                               else begin                // long FB. 3 spaces.
                                                      xtb:=xtb+ksp/3;
                                                      dotimber(False,0,0);

                                                      xtb:=xtb+ksp/3;
                                                      dotimber(False,0,0);

                                                      xtb:=xtb+ksp/3;
                                                      dotimber(False,0,0);
                                                    end;

                                            timb_str:='T';     // reset numbering for fill - this the first..
                                            tbn:=1;

                                            if rail_section<>2 then xtb:=xtb+24*inscale  // BH k-wing rail joint.
                                                               else xtb:=xtb+26*inscale;

                                            if (include_closure_timbers=True)
                                                then dotimber(False,0,0);

                                          end
                                     else begin         // model timbering (based on actual flangeway, a timber under diamond points)...
                                            xtb:=0;

                                            if ( ((fw/inscale)<2.5) and (hdkn>=5.25) )     // scale flangeway under 2.5"
                                            or ( ((fw/inscale)>=2.5) and (hdkn>=3.5) )     // or wider
                                               then dotimber(False,0,0);                 // centre timber K1

                                            xdpb:=seg_termx[3,2]-(g/2)*SIN(hdk/2)+5*inscale;     // 223a diamond point blunt nose + 5".    was 4"

                                            if xdpb>(33*inscale)   // arbitrary 33" spacing (e.g. wide 00/H0 flangeways at 1:8)
                                               then begin
                                                      xtb:=xdpb/2;         // insert additional timber.
                                                      dotimber(False,0,0);
                                                    end;

                                            //if k3n>=4    bug?

                                            if (hdkn>=4.125) or (exp_chairing=True)
                                               then begin

                                                      xtb:=xdpb;               // under tips   K2
                                                      dotimber(False,0,0);

                                                      xtb:=xtb+28*inscale;    // 28" arbitrary spacing for next.  K3
                                                      dotimber(False,0,0);

                                                      if exp_chairing=True
                                                         then timb_str:='D'     // 244a replaces T1   preserve legacy T2+ numbering for shoved timbers
                                                         else timb_str:='T';
                                                      tbn:=1;

                                                      xtb:=xtb+28*inscale;    // 28" arbitrary
                                                      if include_closure_timbers=True
                                                      then dotimber(False,0,0);
                                                    end
                                               else begin

                                                      timb_str:='T';     // reset numbering for fill
                                                      tbn:=1;

                                                      xtb:=xdpb;               // under tips
                                                      dotimber(False,0,0);
                                                    end;

                                          end;

                                end;
                        xtbswend:=xtb;    // save last xtb for start fill.
                      end;
          end;
end;
//_______________________________________________________________________________________________________________________________

procedure dotimber(retcurve:boolean; joint_timber,key_towards:integer);     // process this timber

   // joint_timber=-1/+1 is a joint timber 212a  (never actually used here)     0=not a joint timber

   // first_half = in first half of rail length (if plain track)

begin
  if xtb>(turnoutx-scale/2) then EXIT;  // (6" inside rail end) in case catch points, or F4 length adjust, etc.

  if (tandem_timb=4) and (xtb>(atx+66*inscale)) then EXIT;  // 218b tandems, not beyond C timber

  timberend(0);                         // get yns, yfs for it, and equalizing angle for timber width.

  if (pad_form.timber_centres_menu_entry.Checked=True) or (exp_chairing=True) then drawtimbcl(retcurve);   // timber centre-lines wanted.    or needed for check rail chairs 237b
  if pad_form.timber_outlines_menu_entry.Checked=True then drawtimbol(retcurve,joint_timber,key_towards);  // timber outlines wanted.

  if pad_form.timber_numbers_menu_entry.Checked=True then tbnumber(retcurve)             // timber numbering wanted.
                                                     else tbn:=tbn+1;                    // do numbers last because increments tbn (needed for timber shoves).
end;
//_______________________________________________________________________________________________________________________________

procedure drawtimbol(retcurve:boolean; joint_timber,key_towards:integer);        // draw main timber outlines.

   // joint_timber=-1/+1 this is a joint sleeper 212a     0=not a joint timber

var
  sl_width:extended;

begin
  if maintimb=1
   then begin
          xns:=xtb-tbwide*inscale/2;   // timber width, ( half each side of centre )
          xfs:=xtb+tbwide*inscale/2;
        end
   else begin
          if joint_timber<>0
             then sl_width:=jt_slwide
             else sl_width:=slwide;

          xns:=xtb-sl_width*inscale/2;   // sleepers width, ( half each side of centre )
          xfs:=xtb+sl_width*inscale/2;
        end;

  drawtimber(True,retcurve,joint_timber,key_towards);
end;
//_______________________________________________________________________________________________________________________________

procedure tbnumber(retcurve:boolean);    // mark timber numbering !!! n.b. increments tbn !!!

var
  y,kret:extended;
  pnum,p1,p2:Tpex;
  n:integer;
  marktext_str:string;
  shove_omitted:boolean;

begin
  shove_omitted:=False;       // init.

    // mods 208a  p1=number position on screen,  p2=number position for print/output...

  try
    if retcurve=True    // sleepering the return curve..
       then begin
              y:=aq25offset(xtb,kret)-g-scale;     // g +1ft scale arbitrary.

              p1.y:=y+(tb+g/2)*COS(kret);          // (tb+g/2) arbitrary approx to the text position for screen.
              p2.y:=y+(tb+g/2+6)*COS(kret);        // 208a      // (tb+g/2+6mm) arbitrary approx to the text position for output.

              p1.x:=xtb-(tb+g/2)*SIN(kret)-2;
              p2.x:=p1.x;
            end
       else begin
              if pad_form.timber_centres_menu_entry.Checked=True
                 then begin
                        p1:=number_point_screen;     // gets changed for omitted timber below.
                        p2:=number_point_output;
                      end
                 else begin                // default positions if no timber centre-line calcs.
                        pnum.x:=xtb;

                        pnum.y:=tbnumy_screen;
                        dotransform(keq,xtb,yeq,pnum,p1);   // equalize/shove transform pnum to p1.

                        pnum.y:=tbnumy_output;
                        dotransform(keq,xtb,yeq,pnum,p2);   // equalize/shove transform pnum to p2.

                      end;
            end;

    marktext_str:=timb_str+IntToStr(tbn);     // set up numbering text string

    if shove_timber_form.Showing=False        // not shoving.
       then begin
              for n:=0 to Length(current_shoved_timbers)-1 do begin
                with current_shoved_timbers[n].shove_data do begin
                  if (marktext_str=current_shoved_timbers[n].sv_str) and (sv_code=-1) then EXIT; // omit this timber, shove list.
                end;//with
              end;//next
            end
       else begin                             // show numbers for omitted timbers when shoving...
              for n:=0 to Length(current_shoved_timbers)-1 do begin
                with current_shoved_timbers[n].shove_data do begin
                  if (marktext_str=current_shoved_timbers[n].sv_str) and (sv_code=-1)
                     then begin                   // find somewhere to put it, timber centre-line calcs will be omitted.
                            pnum.x:=xtb;
                            pnum.y:=tbnumy_screen/2;            //  /2 arbitrary, to highlight omitting.
                            dotransform(keq,xtb,yeq,pnum,p1);   // equalize/shove transform pnum to p1.
                            shove_omitted:=True;

                            p2.x:=p1.x;  // not needed for output if omitted..
                            p2.y:=p1.y;

                          end;
                end;//with
              end;//next
            end;

    if timb_str='X'
       then begin
              case (tbn+3-wing_tbcount) of
                    1: marktext_str:=marktext_str+' - Z';
                    2: marktext_str:=marktext_str+' - Y';
                    3: marktext_str:=marktext_str+' - X';
                    4: marktext_str:=marktext_str+' - A';
                    5: marktext_str:=marktext_str+' - B';
                    6: marktext_str:=marktext_str+' - C';
                    7: marktext_str:=marktext_str+' - D';
              end;//case
            end;

    if shove_omitted=True then marktext_str:='!'+marktext_str;   // indicate it's omitted while shoving.

    enter_mark(True,p1,p2,99,marktext_str);

  finally
    tbn:=tbn+1;      // increment number even if omitted.
  end;//try
end;
//_______________________________________________________________________________________________________________________________

procedure endsleeper(x:extended;full_length,retcurve:boolean; joint_sleeper,key_towards,dir:integer);  //  deal with approach and exit plain-track sleepers
                                                                                                       //  enter with xtb to sleeper centre.
   // joint_sleeper=+/-1  this is a joint sleeper 212a   0=not a joint sleeper

   // first_half = in first half of rail length

   // dir added 218a

var
  sl_width:extended;

begin
  if x>(turnoutx+scale/3) then EXIT;     // max 4" beyond rail end.

  if (plain_track=False) and (dir=-1) and (approach_rails_only=True) then EXIT;   // 218a   no approach sleepering

  if joint_sleeper<>0
     then sl_width:=jt_slwide   // joint sleeper
     else sl_width:=slwide;

  xtb:=x;                                   // !!! xtb is still global.
  if full_length=True then timberend(1)     // get yfs, yfsred to sleeper end
                      else timberend(2);

  if (pad_form.timber_centres_menu_entry.Checked=True) or (exp_chairing=True) then drawtimbcl(retcurve);   // sleeper centre-lines wanted.    or needed for check rail chairs 237b

  if pad_form.timber_outlines_menu_entry.Checked=True                               // sleeper outlines wanted
     then begin
            xns:=xtb-sl_width*inscale/2;      // sleeper width, ( half each side of centre )
            xfs:=xtb+sl_width*inscale/2;
            drawtimber(full_length,retcurve,joint_sleeper,key_towards);
          end;
  if pad_form.timber_numbers_menu_entry.Checked=True then tbnumber(retcurve)  // numbering wanted.
                                                     else tbn:=tbn+1;         // do numbering last because increments tbn. (needed for timber shoves).
end;
//_______________________________________________________________________________________________________________________________

procedure drawtimbcl(retcurve:boolean);       // mark timber centre-line

var                                // enter with xtb, yns, yfs, tbq
  n:integer;
  pnum,p1,p2:Tpex;

  p3,p4:Tpex;     // 237b

  shove_this:integer;
  omit:boolean;
  xtimbcl:extended;
  xshove,kshove,oshove,lshove,wshove,cshove:extended;
  save_keq:extended;

  yret,kret:extended;

  str:string;

  dummy:extended;
  x_curmod,x_curtimb,y_curmod,y_curtimb,k_curtimb:extended;

                  //////////////////////////////////////////////////////////////

                  procedure nout_calc_fill_timber_mark(code:integer);

                     // this routine used only if timber outlines are not being drawn.

                  var
                    pk1,pk2,ponpad,pp1,pp2:Tpex;

                  begin
                            // first any blanking? (not bonus timbers)...

                    if (timb_str<>'B') and (plain_track=True) and (p1.x>(turnoutx+scale/3-pt_blank)) and (p2.x>(turnoutx+scale/3-pt_blank)) then EXIT;     // 229b blanking plain track from CTRL-1

                    if (timb_str<>'B') and (plain_track=False) and (p1.x<(startx-minfp)) and (p2.x<(startx-minfp)) then EXIT;

                    dotransform(keq+k_curtimb, {xeq}xtimbcl, yeq{ytimbcl}, p1, pk1);       // timber equalising + curving line angle, transform p1 to pk1 (relative to equalising centre).
                    dotransform(keq+k_curtimb, {xeq}xtimbcl, yeq{ytimbcl}, p2, pk2);       // ditto p2

                    pk1.x:=pk1.x+x_curmod;      // shift p1 to final position for the curving.
                    pk1.y:=pk1.y+y_curmod;

                    dotransform(kform,xform,yform,pk1,ponpad);  // transform to template position on pad..
                    pp1.x:=ponpad.x+xshift;
                    pp1.y:=ponpad.y+yshift;

                    pk2.x:=pk2.x+x_curmod;      // shift p2 to final position for the curving.
                    pk2.y:=pk2.y+y_curmod;

                    dotransform(kform,xform,yform,pk2,ponpad);     // transform to template position on pad..
                    pp2.x:=ponpad.x+xshift;
                    pp2.y:=ponpad.y+yshift;

                    fill_mark(convert_point(pp1),convert_point(pp2),code,str);  // into marks list.
                  end;
                  //////////////////////////////////////////////////////////////


begin
  timbcentre_wait.valid:=False;             // init no data for drawing the centreline later.
  omit:=False;                              // init omission flag

  xtimbcl:=xtb;                             // so we don't modify xtb (global).

  xshove:=0;                                // init shove data for normal timber...
  kshove:=0;
  oshove:=0;
  lshove:=0;
  wshove:=0;
  cshove:=0;

  save_keq:=keq;                            // ditto.

  str:=timb_str+IntToStr(tbn);              // timber number string.

  shove_this:=0;     // default init - draw in normal centre-line colours.

                     // see if this one gets shoved or omitted...

  for n:=0 to Length(current_shoved_timbers)-1 do begin
    with current_shoved_timbers[n].shove_data do begin
      if (str=current_shoved_timbers[n].sv_str) and (sv_code<>0)   // this timber number is in shove list.
         then begin
                if (shove_timber_form.Showing=True) and (shove_timber_form.show_all_blue_checkbox.Checked=True) then shove_this:=40;  // will be drawn highlighted in blue or red.

                if sv_code=-1            // this value in the list is a flag.
                   then omit:=True       // he wants this timber omitted.
                   else begin
                          xshove:=sv_x;
                          kshove:=sv_k;
                          oshove:=sv_o;
                          lshove:=sv_l;
                          wshove:=sv_w;
                          cshove:=sv_c;

                          xtimbcl:=xtb+xshove;   // he wants it shoved.
                        end;
                BREAK;
              end;
    end;//with
  end;//for
                    // see if this one is selected for shoving...

  if (str=current_shove_str) and (shove_timber_form.Showing=True)
     then begin
            shovetimbx:=xtimbcl;       // centre of shoved timber.

            with shove_timber_form do begin
                                             // first clear these read-outs in case timber outlines (which calcs them) are switched off.
              throw_panel.Caption:='';
              length_panel.Caption:='';
              width_panel.Caption:='';
              twist_panel.Caption:='';
              crab_panel.Caption:='';

              if show_shove_fs=True then xtb_panel.Caption:=' centre :  '+round_str((shovetimbx-shovetimbx_zero)/inscale,2)+' ins'
                                    else xtb_panel.Caption:=' centre :  '+round_str((shovetimbx-shovetimbx_zero),2)+' mm';

            end;//with

            shovex:=xshove;            // pick-up current shove data in case needed for mouse actions.
            shovek:=kshove;
            shoveo:=oshove;
            shovel:=lshove;
            shovew:=wshove;
            shovec:=cshove;

            shove_this:=40;                // draw highlighted.
          end;

  if omit=True then EXIT;      // omit this timber.

  if retcurve=True    // sleepering the return curve..
     then begin
            yret:=aq25offset(xtimbcl,kret)-g/2;
          end
     else begin
            yret:=0;
            kret:=0;
          end;
                     // ready to draw cl.  add any shoves...

  p1.x:=xtimbcl+cshove;
  p1.y:=yfs+tbq+oshove+lshove+yret;
  p2.x:=xtimbcl+cshove;
  p2.y:=yns-tbq+oshove+yret;

  p3.x:=xtimbcl+cshove;                // 237b TS stock rail
  p3.y:=aq3offset(p3.x,dummy)+yret;

  p4.x:=xtimbcl+cshove;     // 237b MS stock rail
  p4.y:=yret;               // no shove offset

    // 208a mods ...

  keq:=keq+kshove+kret;       // add any twist.

  pnum.x:=p2.x;

  pnum.y:=p2.y-1.5;     // position for numbering on screen 1.5mm below end of centre-line.

  dotransform(keq,xtimbcl,yeq,pnum,number_point_screen);   // save screen numbering position in number_point.

  pnum.y:=p2.y-6;    // 208a    // position for numbering on output 6mm below end of centre-line.

  dotransform(keq,xtimbcl,yeq,pnum,number_point_output); // 208a  // save output numbering position in number_point.

  with timbcentre_wait do begin  // save the actual co-ords, to be entered in list after the outlines.
    pex1:=p1;                    // (this is a fix - we must do the centre-line calcs first for shove timbers,
    pex2:=p2;                    //  but don't want the centre-lines overdrawn by timber infill.)

    pex_ms:=p4;  // 237b  centre-line at stock rails..
    pex_ts:=p3;

    kq:=keq;
    shove_code:=shove_this;
    valid:=True;
  end;//with

     //     end;

  if pad_form.timber_outlines_menu_entry.Checked=False
     then begin        // not doing any outlines, so do the timber centres now...

                  // mods for parallel edges - 0.76.a  16-10-01...

            docurving(False,True,xtimbcl,yeq,x_curtimb,y_curtimb,k_curtimb,dummy);    // calc curving for the timber rotation point.

            x_curmod:=x_curtimb-xtimbcl;   // shift required to rotation point, to suit the curving..
            y_curmod:=y_curtimb-yeq;

            if midline=False then nout_calc_fill_timber_mark( 4+shove_this)  // 4 = centre-line,  44 = selected timber.
                             else nout_calc_fill_timber_mark(14+shove_this); // 14,54 = timber centre-line, use solid line if drawing rail centrelines. (for rivets?)
          end;

  keq:=save_keq;       // restore global, for outlines, etc.
end;
//_______________________________________________________________________________________________________________________________

procedure drawtimber(full_length,retcurve:boolean; joint_timber,key_towards:integer);     // mark timber outline and chairing

   // enter with xtb, xns, xfs, yns, yfs, tbl (all unshoved)
   // if full_length=False, this is for crossover exit road - so no far end.


   // key_towards  +1=in first half of plain track panel, -1=in second half      0=not plain track sleeper

   // joint_timber -1/0/+1  for key direction adjacent to rail joint  233d       0=not joint timber



type
  Tcheck_end_flare=record
                     check_chair:integer;     // 0=no  1=flare-in  2=parallel  3=flare-out
                     check_add:extended;
                     check_k:extended;
                   end;

  Twing_end_flares=record
                     ms_wing_chair:integer;     // 0=no, beyond   2=parallel  3=flare-out
                     ms_wing_add:extended;
                     ms_wing_k:extended;

                     ts_wing_chair:integer;     // 0=no, beyond   2=parallel  3=flare-out
                     ts_wing_add:extended;
                     ts_wing_k:extended;
                   end;


  Tchairing_dims=record                        // 2D chair dimensions
                   chair_code:integer;

                           //  0=ignore

                           //  1=S1 chair

                           //  2=P chair

                           //  3=

                           //  4=

                           //  5=L1 bridge chair

                           //  6=M1 bridge chair

                           //  7=S1J joint chair

                           //  8=S1O half-bolted chair outer

                           //  9=S1N half-bolted chair inner

                           //  10=SC fictional 4-bolt chair

                           //  11=switch block slide chair (no jaw)    A B C D switches  1PR and 2PR
                           //  12=switch block heel chair (normal key) A B C D E F switches 3PR..11PR
                           //  13=switch block heel chair type 3 (inside key)  E F switches 1PR and 2PR

                           //  14-15-16  // check rail chairs  flare-in, parallel, flare-out    237a




                           //  98=FG dummy chair  foot groove        234a
                           //  99=RG dummy chair  rail head groove   234a


                   chair_outlong:extended;
                   chair_inlong:extended;
                   chair_halfwide:extended;

                   chair_mid_halfwide_near:extended;
                   chair_mid_halfwide_far:extended;

                   inbolt_1x:extended;
                   inbolt_2x:extended;

                   outbolt_1x:extended;
                   outbolt_2x:extended;

                   bolts_from_end:extended;

                   crad_inner:extended;
                   crad_outer:extended;

                 end;//record


  Theaved_chairing_dims=record                            // 556 including chair heaving data
                          chairing_dims:Tchairing_dims;
                          heave_rail_chair:Theave_rail_chair;
                          heaved_valid:boolean;
                        end;//record

  Tchair_twist=record
                 x:extended;
                 y:extended;
                 k:extended;
               end;//record

var
  n,rnum,o:integer;

  p1,p2,p3,p4,p5,p6:Tpex;

  pm1,pm2,pm3,pm4:Tpex;       // 245a 8-sided chair outline mid corners
  km1,km2,km3,km4:extended;   // 245a 8-sided chair outline mid side angles

  near_snib_link_pex1,near_snib_link_pex2:Tpex;
  far_snib_link_pex1,far_snib_link_pex2:Tpex;

  //ppms,ppts:Tpex;  // 237b

  check_end_flare1:Tcheck_end_flare;  // rail 1  check rail

  check_end_flare2:Tcheck_end_flare;  // rail 2

  check_end_flare3:Tcheck_end_flare;  // rail 3

  check_end_flare4:Tcheck_end_flare;  // rail 4  check rail

  check_end_flare:Tcheck_end_flare;

  wing_end_flares:Twing_end_flares;


  b1,b2,b3,b4:Tpex;  // chair bolt centres

  shove_this:integer;
  save_keq:extended;
  timber_str:string;
  list_str:string;    // 227a
  pre_str:string;     // 227a
  xtimbcl:extended;

  xtimbcl2:extended;
  xt:extended;

  yret, kret:extended;
  throw,crab:extended;
  temp_k:extended;
  dummy,dummy1,dummy2,dummy3:extended;
  deg_str:string;

  x_curmod,x_curtimb,y_curmod,y_curtimb,k_curtimb:extended;

  chair_y:extended;         // offset to chair centre.
  chair_k:extended;         // chair angle (rail angle).

  eql:extended;

  rail_sep:extended;  // 239a rail separation between rails 2 and 3

  cd_yes:boolean;

  chairing_dims1:Tchairing_dims;       // rails 1..4
  chairing_dims2:Tchairing_dims;
  chairing_dims3:Tchairing_dims;
  chairing_dims4:Tchairing_dims;

  now_chairing_dims1:Tchairing_dims;   // on rail 1 (check chairs)               // 237c
  now_chairing_dims4:Tchairing_dims;   // on rail 4 (check chairs)    243b

  heaved_chairing_dims1:Theaved_chairing_dims;   // on rail 1  556..
  heaved_chairing_dims2:Theaved_chairing_dims;   // on rail 2
  heaved_chairing_dims3:Theaved_chairing_dims;   // on rail 3
  heaved_chairing_dims4:Theaved_chairing_dims;   // on rail 4

  null_chair:Tchairing_dims;  // ignore this chair

  S1_chairing_dims:Tchairing_dims;
  S1J_chairing_dims:Tchairing_dims;
  L1_chairing_dims:Tchairing_dims;
  P_chairing_dims:Tchairing_dims;

  SC_chairing_dims:Tchairing_dims;

  PLR1_chairing_dims:Tchairing_dims;   // first 2 switch block slide  A..D switches
  PLR3_chairing_dims:Tchairing_dims;   // remaining switch block heel chairs 3P..11P

  PLR1X_chairing_dims:Tchairing_dims;  // first 2 switch block slide  E and F switches (inside keyed)

  CC_chairing_dims:Tchairing_dims;     // check chair parallel part
  CCW_chairing_dims:Tchairing_dims;    // check end chair WORKING near end
  CCE_chairing_dims:Tchairing_dims;    // check end chair EXTENSION far end

  ZY_chairing_dims:Tchairing_dims;     // chair code 21    V-crossing chairs...
  XN_chairing_dims:Tchairing_dims;     // chair code 22
  XA_chairing_dims:Tchairing_dims;     // chair code 23
  AA_chairing_dims:Tchairing_dims;     // chair code 24
  AB_chairing_dims:Tchairing_dims;     // chair code 25
  BB_chairing_dims:Tchairing_dims;     // chair code 26
  CD_chairing_dims:Tchairing_dims;     // chair code 27
  EF_chairing_dims:Tchairing_dims;     // chair code 28

  FG_chairing_dims:Tchairing_dims;
  RG_chairing_dims:Tchairing_dims;

  chair_space_y:extended;

  dummy_k:extended;

  tempex:Tpex;

  shove_colours_wanted:integer;    // 226a

  shoved_corners_pex:Tshoved_corners_pex;  // 226a

  xdiff,ydiff:extended;

  socket_code,label_code,label_code_mod:integer;       // MW 03-08-2024  555a

  xnso,xfso,ynso,yfso:extended;

  plug_inner_fit_sides:extended;
  plug_inner_fit_ends:extended;

  web_side_offset,web_end_offset,flexi:extended;

  n_web_length_mm,f_web_length_mm,web_width_mm:extended; // 241f

  kerf_offset:extended;

  n_flange_wanted,    // 229b
  f_flange_wanted,
  ms_flange_wanted,
  ts_flange_wanted:boolean;

  chair1_wanted,      // 233  not omitted
  chair2_wanted,
  chair3_wanted,
  chair4_wanted:boolean;

  key_wanted:array[1..4] of boolean;      // 556  MW

  swap_s1j,pt_chairs:boolean;  // 237c

  outline_wanted:boolean;

  this_chair_wanted:boolean;  // 232a
  end_of_wing:integer;        // 232a

  switch_opening:extended;

  aq2pt,aq3pt:TPoint; // 235b

  ckmspt,cktspt:Tpoint;  // 237b

  extra_opening:integer;    // 236a

  sp_side,sp_end_near,sp_end_far,tbl_mod:extended;  // 236b

  ms_snib2_extent,ts_snib2_extent:extended;  // 244a ..
  ms_snib2_angle,ts_snib2_angle:extended;

  tsn_snib_space,tsf_snib_space,msn_snib_space,msf_snib_space:extended;

  tn_snibs_wanted,tf_snibs_wanted,mn_snibs_wanted,mf_snibs_wanted,near_snib_link_wanted,far_snib_link_wanted:boolean;

  tn_web_wanted,tf_web_wanted,mn_web_wanted,mf_web_wanted:boolean;

  half_kerfed_width,half_nib_width:extended;

  check_flare_shift,check_flare_twist:extended;    // 237c

  fw_mods:extended;  // 239a

  chair_twist:Tchair_twist;

  xnl,xnl_3,xnl_2:extended;

  need_vee_chairs,need_EF_chairs:boolean;

  curvi_mod,curvi_k,curvi_k_mod:extended;      // 239b

  plug_extended_depth,plug_taper_depth,plug_inset_depth,plug_total_depth:extended;   // 241b

  rail_code:integer;


                  //////////////////////////////////////////////////////////////

                  function check_plain_track_for_blanking:boolean;     // 229a

                  begin
                    RESULT:=( (timb_str<>'B') and (plain_track=True) and (xtb>(turnoutx+scale/3-pt_blank)) );     // 229a blanking plain track from CTRL-1
                  end;
                  //////////////////////////////////////////////////////////////

                  procedure calc_fill_bolts(twist:Tchair_twist; chair_code:integer);

                    // enter with bolt centres  b1,b2,b3,b4

                  var
                    pk1,pk2,ponpad,pp1,pp2,pc1,pc2:Tpex;

                    dummy1,dummy2:extended;

                                      //////////////////////////////////////////////////

                                      procedure do_bolt_mark(pt1,pt2:Tpex; code:integer);

                                      var
                                        p1,p2,pk1,pk2,pc1,pc2,ponpad,pp1,pp2:Tpex;


                                      begin
                                        if check_plain_track_for_blanking=True then EXIT;  // 229a

                                        if twist.k<>0      // rotate chair on timber (crossing chairs equalized)
                                           then begin
                                                  dotransform(twist.k,twist.x,twist.y,pt1,pt1);
                                                  dotransform(twist.k,twist.x,twist.y,pt2,pt2);
                                                end;


                                        dotransform(chair_k,xtimbcl,0,pt1,pc1);       // pt1 rotate to required angle
                                        dotransform(chair_k,xtimbcl,0,pt2,pc2);       // ditto pt2

                                        pc1.y:=pc1.y+chair_y;   // to actual rail position
                                        pc2.y:=pc2.y+chair_y;

                                        dotransform(keq+k_curtimb, xtimbcl, yeq, pc1, pk1);       // timber equalising + curving line angle, transform p1 to pk1 (relative to equalising centre).
                                        dotransform(keq+k_curtimb, xtimbcl, yeq, pc2, pk2);       // ditto p2

                                        pk1.x:=pk1.x+x_curmod;      // shift p1 to final position for the curving.
                                        pk1.y:=pk1.y+y_curmod;

                                        dotransform(kform,xform,yform,pk1,ponpad);  // transform to template position on pad..
                                        pp1.x:=ponpad.x+xshift;
                                        pp1.y:=ponpad.y+yshift;

                                        pk2.x:=pk2.x+x_curmod;      // shift p2 to final position for the curving.
                                        pk2.y:=pk2.y+y_curmod;

                                        dotransform(kform,xform,yform,pk2,ponpad);     // transform to template position on pad..
                                        pp2.x:=ponpad.x+xshift;
                                        pp2.y:=ponpad.y+yshift;

                                        fill_mark(convert_point(pp1),convert_point(pp2),code,'');  // into marks list.
                                      end;
                                      /////////////////////////////////////////////////////////

                                      procedure create_bolt_mark(bolt:Tpex);

                                      var
                                        mark1,mark2:Tpex;

                                      begin
                                           // horizontal mark ...

                                        mark1.x:=bolt.x+inscale/2;  // mark 1/2" long each side of centre
                                        mark1.y:=bolt.y;

                                        mark2.x:=bolt.x-inscale/2;
                                        mark2.y:=bolt.y;

                                        do_bolt_mark(mark1,mark2,495);

                                          // vertical mark ...

                                        mark1.x:=bolt.x;
                                        mark1.y:=bolt.y+inscale/2;

                                        mark2.x:=bolt.x;
                                        mark2.y:=bolt.y-inscale/2;

                                        do_bolt_mark(mark1,mark2,496);

                                      end;
                                      ///////////////////////////////////////////////////

                  begin
                    create_bolt_mark(b1);
                    if b2.x<>b1.x then create_bolt_mark(b2);

                    create_bolt_mark(b3);
                    if b4.x<>b3.x then create_bolt_mark(b4);

                  end;
                  //////////////////////////////////////////////////////////////

                  procedure get_xtimbcl2;    // find xtimbcl2 intersect of ZY, XN, XA, AA, AB, BB, CD, EF skewed crossing chairs centre-line (equalized at k3/2) on rail 2       239a

                  var
                    xt:extended;

                  begin
                    xt:=0;  // init

                    if xtimbcl<fpx
                       then begin
                              try
                                repeat
                                  xt:=xt+inscale/200;   // increase in 5 thou (prototype) steps    arbitrary
                                until ARCTAN(ABS(xt/(g-aq2offset(xtimbcl+xt,dummy_k))))>ABS(k3/2);

                                xtimbcl2:=xtimbcl+xt-inscale/400;  // better approx reverse half-step
                              except
                                xtimbcl2:=xtimbcl; // ???
                              end;//try;
                            end
                       else begin
                              try
                                repeat
                                  xt:=xt+inscale/200;   // increase in 5 thou (prototype) steps    arbitrary
                                until ARCTAN(ABS(xt/(aq2offset(xtimbcl-xt,dummy_k)-g)))>ABS(k3/2);

                                xtimbcl2:=xtimbcl-xt+inscale/400;  // better approx reverse half-step
                              except
                                xtimbcl2:=xtimbcl; // ???
                              end;//try;
                            end;

                  end;
                  //////////////////////////////////////////////////////////////

                  procedure calc_fill_chair_outline(twist:Tchair_twist; heaved_chairing_dims:Theaved_chairing_dims; chcode:integer; side:extended);  // 214a

                    // enter with chair rectangle (centres of corner rads)  p1,p2,p3,p4  and chair cl, yret for jaw loose pin slot
                    // 245a and pm1,pm2,pm3,pm4 mid corners of 8-sided chairs (0=ignore if 4-sided), km1,km2,km3,km4 mid-side angles

                    // if side=1      clockwise from bottom-left    MS chairs
                    // if side=-1 anticlockwise from top-left       TS chairs

                    // chcode = 484(X crossing chair),  485(switch block chair, CC chair), or  493(L1 chair), or 494(S1-S1J chair), or 497(P chair)

                  var
                    pk1,pk2,ponpad,pp1,pp2,pc1,pc2:Tpex;

                    arc1,arc2:Tpex;
                    arck:extended;

                    n:integer;

                    dummy1,dummy2:extended;

                    plug1,plug2:Tpex;
                    sock1,sock2:Tpex;    // 228a

                    tang_pex1,tang_pex6,tang_pex9,tang_pex10,tang_pex13,tang_pex14,tang_pex17,tang_pex18,tang_pex21,tang_pex22,tang_pex36,tang_pex37,tang_pex38,tang_pex39,tang_pex40,tang_pex41,tang_pex46,tang_pex50:Tpex;      // 241b

                    tang_pex42,tang_pex43,tang_pex52,tang_pex53,tang_pex54,tang_pex55,tang_pex56,tang_pex57,tang_pex58,tang_pex59:Tpex;

                    plug_rect_xleft:extended;   // 233d
                    plug_rect_xright:extended;  // 233d
                    plug_rect_yinner:extended;  // 233d
                    plug_rect_youter:extended;  // 233d

                    plug_spacing_sides:extended;       // 233d
                    plug_spacing_inner_end:extended;   // 236d
                    plug_spacing_outer_end:extended;   // 236d

                    plinth_rect_xleft:extended;   // 233d
                    plinth_rect_xright:extended;  // 233d
                    plinth_rect_yinner:extended;  // 233d
                    plinth_rect_youter:extended;  // 233d

                    plinth_spacing_sides:extended;       // 233d
                    plinth_spacing_inner_end:extended;   // 556a
                    plinth_spacing_outer_end:extended;   // 556a

                    loose_pin_org1, loose_pin_org2, loose_pin_org3, loose_pin_org4:Tpex;   // original unshifted slot

                    loose_pin1, loose_pin2, loose_pin3, loose_pin4:Tpex;     // rectangle slot for loose pin
                    loose_pin5, loose_pin6, loose_pin7, loose_pin8:Tpex;     // sheared base rectangle for angled slot through plug (L1 chairs) 236d

                    loose_pin9,  loose_pin10, loose_pin11, loose_pin12:Tpex;    // rectangle for second jaw slot (check rail chairs) 237a
                    loose_pin13, loose_pin14, loose_pin15, loose_pin16:Tpex;    // sheared base rectangle for angled second slot through plug (L1 chairs) 237a

                    plug_clear_sides:extended;
                    plug_clear_ends:extended;   // 234e

                    support_half_top_side_mm,support_half_bottom_side_mm:extended;
                    support_half_top_end_mm,support_half_bottom_end_mm:extended;

                    midx,midy:extended;

                    chamf,undercut,socket_undercut,kerf_undercut,sf_sides,sf_ends,sf_flap_ends:extended;

                    flap_jut_length,flap_rear_length,flap_clear_width:extended;   // 241c


                    slot_cl:extended;  // 236d

                    slot_shear:extended;  // 236d

                    shift_slot1,shift_slot2,twist_slot1,twist_slot2:extended; // 237a

                    shift_slot1_along,shift_slot2_along:extended;

                    tempex:Tpex;

                    jaw2_dir:integer;

                    len:extended;


                    tang2_spacing,tang2_long:extended;

                    tang_outlong1,tang_inlong1,tang_top_width,tang_top_clear,tang_bottom_space:extended;  // 241b

                    outer_beefing:extended;
                    inner_beefing:extended;


                                    //==========================================

                                    procedure do_chair_mark(pt1,pt2:Tpex; code:integer);

                                    var
                                      p1,p2,pk1,pk2,pc1,pc2,ponpad,pp1,pp2:Tpex;


                                    begin
                                      if check_plain_track_for_blanking=True then EXIT;  // 229a

                                      if twist.k<>0      // rotate chair on timber (crossing chairs equalized)
                                         then begin
                                                dotransform(twist.k,twist.x,twist.y,pt1,pt1);
                                                dotransform(twist.k,twist.x,twist.y,pt2,pt2);
                                              end;

                                      dotransform(chair_k,xtimbcl,0,pt1,pc1);       // pt1 rotate to required angle
                                      dotransform(chair_k,xtimbcl,0,pt2,pc2);       // ditto pt2

                                      pc1.y:=pc1.y+chair_y;   // to actual rail position
                                      pc2.y:=pc2.y+chair_y;

                                      dotransform(keq+k_curtimb, xtimbcl, yeq, pc1, pk1);       // timber equalising + curving line angle, transform p1 to pk1 (relative to equalising centre).
                                      dotransform(keq+k_curtimb, xtimbcl, yeq, pc2, pk2);       // ditto p2

                                      pk1.x:=pk1.x+x_curmod;      // shift p1 to final position for the curving.
                                      pk1.y:=pk1.y+y_curmod;

                                      dotransform(kform,xform,yform,pk1,ponpad);  // transform to template position on pad..
                                      pp1.x:=ponpad.x+xshift;
                                      pp1.y:=ponpad.y+yshift;

                                      pk2.x:=pk2.x+x_curmod;      // shift p2 to final position for the curving.
                                      pk2.y:=pk2.y+y_curmod;

                                      dotransform(kform,xform,yform,pk2,ponpad);     // transform to template position on pad..
                                      pp2.x:=ponpad.x+xshift;
                                      pp2.y:=ponpad.y+yshift;

                                      fill_mark(convert_point(pp1),convert_point(pp2),code,'');          // into marks list.
                                    end;
                                    //==========================================

                                    procedure do_plug_socket;      // moved here 556

                                    var
                                      heaved_plug:integer;

                                    begin
                                              // plug rectangle outline (code 491, 24 marks) 227a  ...
                                              // with 45-deg corners 8-sides clockwise with side=+1 , repeat 8-inner sides for bottom taper
                                              // plus 8 marks for top and bottom of support pyramid

                                              // 233d  modify plug/socket size rectangle if wanted   default mods zero  ...

                                      with heaved_chairing_dims.chairing_dims do begin

                                        p1.x:=plug_rect_xleft-plugsock_width_mod*inscale/2;
                                        p2.x:=p1.x;

                                        p3.x:=plug_rect_xright+plugsock_width_mod*inscale/2;
                                        p4.x:=p3.x;

                                        p1.y:=plug_rect_youter-plugsock_length_mod*inscale*side/2;
                                        p2.y:=plug_rect_yinner+plugsock_length_mod*inscale*side/2;
                                        p3.y:=p2.y;
                                        p4.y:=p1.y;

                                        if heaved_chairing_dims.heaved_valid=True
                                           then heaved_plug:=heaved_chairing_dims.heave_rail_chair.hv_plug  // 1=force clip-fit  2=force snap-fit  3=force press-fit  4= force COT (no plug)
                                           else heaved_plug:=0;

                                        if (    (dxf_form.chairs_bullet_shape.Visible=True)
                                             or (dxf_form.reset_both_bullet_shape.Visible=True)
                                             or ( (dxf_form.cot_bullet_shape.Visible=True) and (heaved_plug<>0) )
                                            )
                                        and ( (dxf_form.cot_radio.Checked=False) or (heaved_plug<>0) )
                                        and (heaved_plug<>4)
                                           then begin
                                                       // do plug and support...

                                                           // break plug corners ..

                                                  if dxf_form.cnc_corners_checkbox.Checked=True
                                                     then begin
                                                            plug_clear_sides:=plug_corner_clear_cnc*inscale;
                                                            plug_clear_ends:=(ABS(p3.x-p1.x)-plug_end_width_cnc*inscale)/2;    // 234e calc end corner relief
                                                          end
                                                     else begin
                                                            plug_clear_sides:=plug_corner_clear_fdm*inscale;

                                                            {if chair_code=5
                                                               then plug_clear_ends:=(ABS(p3.x-p1.x)-plug_end_width_fdm*2*inscale)/2   // wider on L1 chairs to clear loose-pin slot
                                                               else}

                                                            plug_clear_ends:=(ABS(p3.x-p1.x)-plug_end_width_fdm*inscale)/2;    // 234e calc end corner relief
                                                          end;

                                                    // outer sides...

                                                    // plug_fit = fine adjust plug size, default 0,  marks 0-7...

                                                  plug1.x:=p1.x-plug_fit_sides+plug_clear_ends;
                                                  plug1.y:=p1.y-plug_fit_ends*side;

                                                  plug2.x:=p1.x-plug_fit_sides;
                                                  plug2.y:=p1.y-plug_fit_ends*side+plug_clear_sides*side;

                                                  tang_pex1:=plug2;

                                                  do_chair_mark(plug1,plug2,491);    // 0..1 break across p1 corner

                                                  plug1:=plug2;
                                                  plug2.x:=p2.x-plug_fit_sides;
                                                  plug2.y:=p2.y+plug_fit_ends*side-plug_clear_sides*side;

                                                  do_chair_mark(plug1,plug2,491);    // 1..2 side of plug

                                                  plug1:=plug2;
                                                  plug2.x:=p2.x-plug_fit_sides+plug_clear_ends;
                                                  plug2.y:=p2.y+plug_fit_ends*side;

                                                  do_chair_mark(plug1,plug2,491);    // 2..3 break across p2 corner

                                                  plug1:=plug2;
                                                  plug2.x:=p3.x+plug_fit_sides-plug_clear_ends;
                                                  plug2.y:=p3.y+plug_fit_ends*side;

                                                  do_chair_mark(plug1,plug2,491);    // 3..4 end of plug

                                                  plug1:=plug2;
                                                  plug2.x:=p3.x+plug_fit_sides;
                                                  plug2.y:=p3.y+plug_fit_ends*side-plug_clear_sides*side;

                                                  do_chair_mark(plug1,plug2,491);    // 4..5 break across p3 corner

                                                  plug1:=plug2;
                                                  plug2.x:=p4.x+plug_fit_sides;
                                                  plug2.y:=p4.y-plug_fit_ends*side+plug_clear_sides*side;

                                                  tang_pex6:=plug2;

                                                  do_chair_mark(plug1,plug2,491);    // 5..6 side of plug

                                                  plug1:=plug2;
                                                  plug2.x:=p4.x+plug_fit_sides-plug_clear_ends;
                                                  plug2.y:=p4.y-plug_fit_ends*side;

                                                  do_chair_mark(plug1,plug2,491);    // 6..7 break across p4 corner


                                                  plug1:=plug2;
                                                  plug2.x:=p1.x-plug_fit_sides+plug_clear_ends;
                                                  plug2.y:=p1.y-plug_fit_ends*side;

                                                  do_chair_mark(plug1,plug2,491);    // 7...0 end of plug


                                                     // repeat for inner sides (below taper), upper ...    marks 8-15...

                                                  if (dxf_form.clip_fit_radio.Checked=True) or (heaved_plug=1)
                                                     then begin
                                                            plug_inner_fit_sides:=plug_fit_sides-clip_plug_inset_clear_upper*inscale;  // 1/4" clear below taper
                                                            plug_inner_fit_ends:=plug_fit_ends-clip_plug_inset_clear_upper*inscale;    // 1/4" clear below taper
                                                          end
                                                     else if (dxf_form.snap_fit_radio.Checked=True) or (heaved_plug=2)
                                                             then begin
                                                                    plug_inner_fit_sides:=plug_fit_sides+snap_plug_overcut_mm;    // width of plug overcut to clip under socket undercut  241b
                                                                    plug_inner_fit_ends:=plug_fit_ends;
                                                                  end
                                                             else begin        // press-fit
                                                                    plug_inner_fit_sides:=plug_fit_sides-press_plug_inset_clear_upper*inscale;  // 1/4" clear below taper
                                                                    plug_inner_fit_ends:=plug_fit_ends-press_plug_inset_clear_upper*inscale;    // 1/4" clear below taper
                                                                  end;

                                                  plug1.x:=p1.x-plug_inner_fit_sides+plug_clear_ends;
                                                  plug1.y:=p1.y-plug_inner_fit_ends*side;

                                                  plug2.x:=p1.x-plug_inner_fit_sides;
                                                  plug2.y:=p1.y-plug_inner_fit_ends*side+plug_clear_sides*side;

                                                  do_chair_mark(plug1,plug2,491);    // break across p1 corner

                                                  plug1:=plug2;
                                                  plug2.x:=p2.x-plug_inner_fit_sides;
                                                  plug2.y:=p2.y+plug_inner_fit_ends*side-plug_clear_sides*side;

                                                  tang_pex9:=plug1;
                                                  tang_pex10:=plug2;

                                                  do_chair_mark(plug1,plug2,491);    // side of plug

                                                  plug1:=plug2;
                                                  plug2.x:=p2.x-plug_inner_fit_sides+plug_clear_ends;
                                                  plug2.y:=p2.y+plug_inner_fit_ends*side;

                                                  do_chair_mark(plug1,plug2,491);    // break across p2 corner

                                                  plug1:=plug2;
                                                  plug2.x:=p3.x+plug_inner_fit_sides-plug_clear_ends;
                                                  plug2.y:=p3.y+plug_inner_fit_ends*side;

                                                  do_chair_mark(plug1,plug2,491);    // end of plug

                                                  plug1:=plug2;
                                                  plug2.x:=p3.x+plug_inner_fit_sides;
                                                  plug2.y:=p3.y+plug_inner_fit_ends*side-plug_clear_sides*side;

                                                  do_chair_mark(plug1,plug2,491);    // break across p3 corner

                                                  plug1:=plug2;
                                                  plug2.x:=p4.x+plug_inner_fit_sides;
                                                  plug2.y:=p4.y-plug_inner_fit_ends*side+plug_clear_sides*side;

                                                  tang_pex13:=plug1;
                                                  tang_pex14:=plug2;

                                                  do_chair_mark(plug1,plug2,491);    // side of plug

                                                  plug1:=plug2;
                                                  plug2.x:=p4.x+plug_inner_fit_sides-plug_clear_ends;
                                                  plug2.y:=p4.y-plug_inner_fit_ends*side;

                                                  do_chair_mark(plug1,plug2,491);    // break across p4 corner


                                                  plug1:=plug2;
                                                  plug2.x:=p1.x-plug_inner_fit_sides+plug_clear_ends;
                                                  plug2.y:=p1.y-plug_inner_fit_ends*side;

                                                  do_chair_mark(plug1,plug2,491);    // end of plug


                                                     // repeat for inner sides, bottom ...    234e another 8 marks  16-23...

                                                  if (dxf_form.clip_fit_radio.Checked=True) or (heaved_plug=1)
                                                     then begin
                                                            plug_inner_fit_sides:=plug_fit_sides-clip_plug_inset_clear_bottom*inscale;  // 1/2" clear at bottom
                                                            plug_inner_fit_ends:=plug_fit_ends-clip_plug_inset_clear_bottom*inscale;    // 1/2" clear at bottom
                                                          end
                                                     else if (dxf_form.snap_fit_radio.Checked=True) or (heaved_plug=2)
                                                             then begin
                                                                    plug_inner_fit_sides:=plug_fit_sides-snap_plug_inset_clear_bottom*inscale;  // 1/2" clear at bottom
                                                                    plug_inner_fit_ends:=plug_fit_ends-snap_plug_inset_clear_bottom*inscale;    // 1/2" clear at bottom
                                                                  end
                                                             else begin        // press-fit
                                                                    plug_inner_fit_sides:=plug_fit_sides-press_plug_inset_clear_bottom*inscale;  // 1/2" clear at bottom
                                                                    plug_inner_fit_ends:=plug_fit_ends-press_plug_inset_clear_bottom*inscale;    // 1/2" clear at bottom
                                                                  end;

                                                  plug1.x:=p1.x-plug_inner_fit_sides+plug_clear_ends;
                                                  plug1.y:=p1.y-plug_inner_fit_ends*side;

                                                  plug2.x:=p1.x-plug_inner_fit_sides;
                                                  plug2.y:=p1.y-plug_inner_fit_ends*side+plug_clear_sides*side;

                                                  do_chair_mark(plug1,plug2,491);    // break across p1 corner

                                                  plug1:=plug2;
                                                  plug2.x:=p2.x-plug_inner_fit_sides;
                                                  plug2.y:=p2.y+plug_inner_fit_ends*side-plug_clear_sides*side;

                                                  tang_pex17:=plug1;
                                                  tang_pex18:=plug2;

                                                  do_chair_mark(plug1,plug2,491);    // side of plug

                                                  plug1:=plug2;
                                                  plug2.x:=p2.x-plug_inner_fit_sides+plug_clear_ends;
                                                  plug2.y:=p2.y+plug_inner_fit_ends*side;

                                                  do_chair_mark(plug1,plug2,491);    // break across p2 corner

                                                  plug1:=plug2;
                                                  plug2.x:=p3.x+plug_inner_fit_sides-plug_clear_ends;
                                                  plug2.y:=p3.y+plug_inner_fit_ends*side;

                                                  do_chair_mark(plug1,plug2,491);    // end of plug

                                                  plug1:=plug2;
                                                  plug2.x:=p3.x+plug_inner_fit_sides;
                                                  plug2.y:=p3.y+plug_inner_fit_ends*side-plug_clear_sides*side;

                                                  do_chair_mark(plug1,plug2,491);    // break across p3 corner

                                                  plug1:=plug2;
                                                  plug2.x:=p4.x+plug_inner_fit_sides;
                                                  plug2.y:=p4.y-plug_inner_fit_ends*side+plug_clear_sides*side;

                                                  tang_pex21:=plug1;
                                                  tang_pex22:=plug2;

                                                  do_chair_mark(plug1,plug2,491);    // side of plug

                                                  plug1:=plug2;
                                                  plug2.x:=p4.x+plug_inner_fit_sides-plug_clear_ends;
                                                  plug2.y:=p4.y-plug_inner_fit_ends*side;

                                                  do_chair_mark(plug1,plug2,491);    // break across p4 corner


                                                  plug1:=plug2;
                                                  plug2.x:=p1.x-plug_inner_fit_sides+plug_clear_ends;
                                                  plug2.y:=p1.y-plug_inner_fit_ends*side;

                                                  do_chair_mark(plug1,plug2,491);    // end of plug


                                                    // top of support pyramid 227a  (4 more marks  24-27) ...     (and bottom of plug)

                                                          // 237a  allow for a twisted slot in plug base (e.g. crossing chairs, check-end chairs)
                                                          // max twist (equalized chairs) say max 15 degs


                                                  support_half_top_side_mm:=pyramid_top_side_inset*inscale;  // 241b  2" default   was 2.3/8"     was ((pin_slot_halfwide+loose_pin_clear_sides)*inscale*2*COS(tw)+(pin_slot_length+loose_pin_clear_front+loose_pin_clear_back)*inscale*SIN(tw))/2;  // half twisted slot extent     pyramid top dims

                                                  if (k3n<4.99) and (chair_code>20) then support_half_top_side_mm:=support_half_top_side_mm+inscale*3/8;  // 241b 3/8" wider to clear twisted slots on short angle crossings   1:5 arbitrary

                                                  support_half_top_end_mm:=(ABS(p1.y-p2.y)-pyramid_top_end_inset*inscale)/2;   // 237a pyramid top dims

                                                  if chair_code=5 then support_half_top_end_mm:=support_half_top_end_mm+inscale*3/8;  // 241b L1 chair -- pyramid to clear pin slot

                                                    // bottom dims of support pyramid ..

                                                  support_half_bottom_side_mm:=support_half_top_side_mm+pyramid_taper*inscale;   // 234e pyramid bottom dims
                                                  support_half_bottom_end_mm:=support_half_top_end_mm+pyramid_taper*inscale;     // 234e pyramid bottom dims

                                                    // top ...

                                                  midx:=(p1.x+p4.x)/2;
                                                  midy:=(p1.y+p2.y)/2;

                                                  plug1.x:=midx-support_half_top_side_mm;
                                                  plug1.y:=midy-support_half_top_end_mm*side;

                                                  plug2.x:=midx-support_half_top_side_mm;
                                                  plug2.y:=midy+support_half_top_end_mm*side;

                                                  do_chair_mark(plug1,plug2,491);    // 1st side

                                                  plug1:=plug2;
                                                  plug2.x:=midx+support_half_top_side_mm;
                                                  plug2.y:=midy+support_half_top_end_mm*side;

                                                  do_chair_mark(plug1,plug2,491);    // 2nd side

                                                  plug1:=plug2;
                                                  plug2.x:=midx+support_half_top_side_mm;
                                                  plug2.y:=midy-support_half_top_end_mm*side;

                                                  do_chair_mark(plug1,plug2,491);    // 3rd side

                                                  plug1:=plug2;
                                                  plug2.x:=midx-support_half_top_side_mm;
                                                  plug2.y:=midy-support_half_top_end_mm*side;

                                                  do_chair_mark(plug1,plug2,491);    // 4th side


                                                    // bottom of plug support pyramid 227a  (4 more marks 29-31) ...

                                                  midx:=(p1.x+p4.x)/2;
                                                  midy:=(p1.y+p2.y)/2;

                                                  plug1.x:=midx-support_half_bottom_side_mm;
                                                  plug1.y:=midy-support_half_bottom_end_mm*side;

                                                  plug2.x:=midx-support_half_bottom_side_mm;
                                                  plug2.y:=midy+support_half_bottom_end_mm*side;

                                                  do_chair_mark(plug1,plug2,491);    // 1st side

                                                  plug1:=plug2;
                                                  plug2.x:=midx+support_half_bottom_side_mm;
                                                  plug2.y:=midy+support_half_bottom_end_mm*side;

                                                  do_chair_mark(plug1,plug2,491);    // 2nd side

                                                  plug1:=plug2;
                                                  plug2.x:=midx+support_half_bottom_side_mm;
                                                  plug2.y:=midy-support_half_bottom_end_mm*side;

                                                  do_chair_mark(plug1,plug2,491);    // 3rd side

                                                  plug1:=plug2;
                                                  plug2.x:=midx-support_half_bottom_side_mm;
                                                  plug2.y:=midy-support_half_bottom_end_mm*side;

                                                  do_chair_mark(plug1,plug2,491);    // 4th side


                                                    // dummy top mid-rectangle on support pyramid 237a  (4 more marks 32-35  36 marks total) ...
                                                    // corners used in DXF but not sides (for twisted slots)

                                                  midx:=(p1.x+p4.x)/2;
                                                  midy:=(p1.y+p2.y)/2;

                                                  plug1.x:=midx-support_half_top_side_mm;
                                                  plug1.y:=midy-support_half_top_end_mm*side*3/4;

                                                  plug2.x:=midx-support_half_top_side_mm;
                                                  plug2.y:=midy+support_half_top_end_mm*side*3/4;

                                                  do_chair_mark(plug1,plug2,491);

                                                  plug1:=plug2;
                                                  plug2.x:=midx+support_half_top_side_mm;
                                                  plug2.y:=midy+support_half_top_end_mm*side*3/4;

                                                  do_chair_mark(plug1,plug2,491);

                                                  plug1:=plug2;
                                                  plug2.x:=midx+support_half_top_side_mm;
                                                  plug2.y:=midy-support_half_top_end_mm*side*3/4;

                                                  do_chair_mark(plug1,plug2,491);

                                                  plug1:=plug2;
                                                  plug2.x:=midx-support_half_top_side_mm;
                                                  plug2.y:=midy-support_half_top_end_mm*side*3/4;

                                                  do_chair_mark(plug1,plug2,491);


                                                    //  24 more marks for clip-fit plugs -- tang recesses   (36..59  60 marks total) ...  241b

                                                  tang2_spacing:=0;     // init single set of tangs
                                                  tang2_long:=0;

                                                  tang_outlong1:=3*inscale;   // 3"   init

                                                  tang_top_width:=inscale*1.25;  // 1.1.4" at top of tang

                                                  tang_top_clear:=inscale/2;   // 1/2" side clearance at top for tang to flex

                                                  len:=ABS(p1.x-p4.x)/inscale;  // plug width convert to full size

                                                  if len<6.25
                                                     then tang_bottom_space:=inscale/2                        // 1/2" space from centre to rear of base of tang for 6" plugs S1 etc.
                                                     else tang_bottom_space:=inscale/2+(len-6.25)*inscale/2;  // widen to similar tang bottom for wider plugs.

                                                  case chair_code of   // arbitrary tang lengths ...

                                                     1,3,4,6..10: begin                          // S1, S1J  no key jaw at inner end ...
                                                                    if chair_inlong<(7*inscale)
                                                                       then tang_inlong1:=chair_inlong-2.75*inscale
                                                                       else tang_inlong1:=chair_inlong-4*inscale;
                                                                  end;

                                                              2: begin   // P slide

                                                                    tang_inlong1:=1.5*inscale;                                        // 4.5" tang
                                                                    tang2_spacing:=2*inscale;                                         // middle column
                                                                    tang2_long:=chair_inlong-tang_inlong1-tang2_spacing-2.75*inscale;
                                                                  end;

                                                               5: tang_inlong1:=chair_inlong-3*inscale;   // L1 bridge chair

                                                              11: begin   //  1P..2P

                                                                    tang_inlong1:=2*inscale;                                          // 5" tang
                                                                    tang2_spacing:=2*inscale;                                         // middle column
                                                                    tang2_long:=chair_inlong-tang_inlong1-tang2_spacing-2.75*inscale;
                                                                  end;

                                                           12,13: begin
                                                                    tang_inlong1:=chair_inlong-12*inscale;  // 3P..11P switch block, internal key jaws, pin slots
                                                                    tang2_spacing:=3.75*inscale;
                                                                    tang2_long:=5.25*inscale;
                                                                  end;

                                                           14,16: tang_inlong1:=chair_inlong-6.5*inscale;     // CCL, CCR

                                                              21: begin                                           // ZY

                                                                    if (chair_inlong+chair_outlong)>(23*inscale)  // split tangs if chair longer than 23" arbitrary
                                                                       then begin
                                                                              tang_inlong1:=(chair_inlong-chair_outlong)/2-inscale;    // 1" half middle column
                                                                              tang2_spacing:=inscale*2;                                // 2" middle column
                                                                              tang2_long:=tang_outlong1+tang_inlong1;
                                                                            end
                                                                       else tang_inlong1:=chair_inlong-6.5*inscale;   // shorter than 23" -- full length single tang
                                                                  end;

                                                          23..26: begin                                        // XA, AA, AB, BB  double keys on wing rail
                                                                    tang_outlong1:=chair_outlong-6.5*inscale;

                                                                     if (chair_inlong+chair_outlong)>(27.5*inscale)  // split long tangs if chair longer than arbitrary
                                                                         then begin
                                                                                tang_inlong1:=0-chair_outlong+12.5*inscale;               // 6" tang

                                                                                tang2_spacing:=chair_inlong+chair_outlong-25*inscale;     // middle column   2.5" minimum
                                                                                tang2_long:=tang_outlong1+tang_inlong1;
                                                                              end
                                                                         else if (chair_inlong+chair_outlong)>(24.5*inscale)  // split short tangs if chair longer than arbitrary
                                                                                 then begin
                                                                                        tang_inlong1:=0-chair_outlong+11*inscale;               // 4.5" tang

                                                                                        tang2_spacing:=chair_inlong+chair_outlong-22*inscale;   // middle column   2.5" minimum
                                                                                        tang2_long:=tang_outlong1+tang_inlong1;
                                                                                      end
                                                                                 else tang_inlong1:=chair_inlong-6.5*inscale;     // shorter chair, no split

                                                                  end;

                                                              27: begin             // CD/ED   no slots

                                                                    if (chair_inlong+chair_outlong)>(19*inscale)  // split long tangs if chair longer than 19" arbitrary
                                                                       then begin
                                                                              tang_outlong1:=chair_outlong-3.5*inscale;
                                                                              tang_inlong1:=0-chair_outlong+8.5*inscale;               // 5" tang

                                                                              tang2_spacing:=chair_inlong+chair_outlong-17*inscale;   // middle column   2" minimum
                                                                              tang2_long:=tang_outlong1+tang_inlong1;
                                                                            end
                                                                       else if (chair_inlong+chair_outlong)>(17*inscale)  // split short tangs if chair longer than 17" arbitrary
                                                                               then begin
                                                                                      tang_outlong1:=chair_outlong-3.5*inscale;
                                                                                      tang_inlong1:=0-chair_outlong+7.5*inscale;               // 4" tang

                                                                                      tang2_spacing:=chair_inlong+chair_outlong-15*inscale;   // middle column   2" minimum
                                                                                      tang2_long:=tang_outlong1+tang_inlong1;
                                                                                    end
                                                                               else begin                                        // shorter chair, no split
                                                                                      tang_inlong1:=chair_inlong-4*inscale;
                                                                                      tang_outlong1:=chair_outlong-4*inscale;
                                                                                    end;
                                                                  end;

                                                              28: begin                                           //EF
                                                                    tang_outlong1:=chair_outlong-3*inscale;
                                                                    tang_inlong1:=0-chair_outlong+8*inscale;               // 5" tang

                                                                    tang2_spacing:=chair_inlong+chair_outlong-16*inscale;  // middle column
                                                                    tang2_long:=tang_outlong1+tang_inlong1;
                                                                  end;

                                                             else tang_inlong1:=chair_inlong-6.5*inscale;  // long chair, double key jaws, pin slots  CC code 15    XN code 22

                                                  end;//case


                                                  plug1.x:=tang_pex1.x;           // 36
                                                  plug1.y:=0-tang_outlong1*side;

                                                  plug2:=plug1;                   // 37
                                                  plug2.y:=tang_inlong1*side;

                                                  tang_pex36:=plug1;
                                                  tang_pex37:=plug2;

                                                  do_chair_mark(plug1,plug2,491);  // 36..37

                                                  plug1:=plug2;                                 // 37
                                                  plug2.y:=tang_pex37.y+tang2_spacing*side;     // 38

                                                  tang_pex38:=plug2;

                                                  do_chair_mark(plug1,plug2,491);  // 37..38

                                                  plug1:=plug2;                            // 38
                                                  plug2.y:=tang_pex38.y+tang2_long*side;   // 39

                                                  tang_pex39:=plug2;

                                                  do_chair_mark(plug1,plug2,491);  // 38..39



                                                  plug1:=plug2;                   // 39
                                                  plug2.x:=tang_pex6.x;           // 40

                                                  do_chair_mark(plug1,plug2,491);  // 39..40


                                                  plug1:=plug2;                   // 40
                                                  plug2.y:=tang_pex38.y;          // 41

                                                  tang_pex40:=plug1;
                                                  tang_pex41:=plug2;

                                                  do_chair_mark(plug1,plug2,491);  // 40..41

                                                  plug1:=plug2;                    // 41
                                                  plug2.y:=tang_pex37.y;           // 42

                                                  tang_pex42:=plug2;

                                                  do_chair_mark(plug1,plug2,491);   // 41..42

                                                  plug1:=plug2;                   // 42
                                                  plug2.y:=tang_pex36.y;          // 43

                                                  tang_pex43:=plug2;

                                                  do_chair_mark(plug1,plug2,491);  // 42..43


                                                  plug1:=plug2;                    // 43
                                                  plug2.x:=tang_pex9.x;            // 44

                                                  do_chair_mark(plug1,plug2,491);  // 43..44


                                                  plug1:=plug2;                    // 44
                                                  plug2.y:=tang_pex37.y;           // 45

                                                  do_chair_mark(plug1,plug2,491);    // 44..45

                                                  plug1:=plug2;              // 45
                                                  plug2.y:=tang_pex38.y;     // 46

                                                  tang_pex46:=plug2;

                                                  do_chair_mark(plug1,plug2,491);    // 45..46

                                                  plug1:=plug2;              // 46
                                                  plug2.y:=tang_pex39.y;     // 47

                                                  do_chair_mark(plug1,plug2,491);    // 46..47


                                                  plug1:=plug2;              // 47
                                                  plug2.x:=tang_pex14.x;     // 48

                                                  do_chair_mark(plug1,plug2,491);    // 47..48


                                                  plug1:=plug2;              // 48
                                                  plug2.y:=tang_pex38.y;     // 49

                                                  do_chair_mark(plug1,plug2,491);   // 48..49

                                                  plug1:=plug2;              // 49
                                                  plug2.y:=tang_pex37.y;     // 50

                                                  tang_pex50:=plug2;

                                                  do_chair_mark(plug1,plug2,491);   // 49..50

                                                  plug1:=plug2;              // 50
                                                  plug2.y:=tang_pex36.y;     // 51

                                                  do_chair_mark(plug1,plug2,491);

                                                  plug1:=plug2;              // 51
                                                  plug2.x:=tang_pex17.x;     // 52

                                                  tang_pex52:=plug2;

                                                  do_chair_mark(plug1,plug2,491);    // 51..52




                                                  plug1:=plug2;              // 52
                                                  plug2.y:=tang_pex37.y;     // 53

                                                  tang_pex53:=plug2;

                                                  do_chair_mark(plug1,plug2,491);    // 52..53

                                                  plug1:=plug2;              // 53
                                                  plug2.y:=tang_pex38.y;     // 54

                                                  tang_pex54:=plug2;

                                                  do_chair_mark(plug1,plug2,491);    // 53..54

                                                  plug1:=plug2;              // 54
                                                  plug2.y:=tang_pex39.y;     // 55

                                                  tang_pex55:=plug2;

                                                  do_chair_mark(plug1,plug2,491);    // 54..55

                                                  plug1:=plug2;              // 55
                                                  plug2.x:=tang_pex22.x;     // 56

                                                  tang_pex56:=plug2;

                                                  do_chair_mark(plug1,plug2,491);    // 55..56

                                                  plug1:=plug2;              // 56
                                                  plug2.y:=tang_pex38.y;     // 57

                                                  tang_pex57:=plug2;

                                                  do_chair_mark(plug1,plug2,491);    // 56..57

                                                  plug1:=plug2;              // 57
                                                  plug2.y:=tang_pex37.y;     // 58

                                                  tang_pex58:=plug2;

                                                  do_chair_mark(plug1,plug2,491);    // 57..58

                                                  plug1:=plug2;              // 58
                                                  plug2.y:=tang_pex36.y;     // 59

                                                  tang_pex59:=plug2;

                                                  do_chair_mark(plug1,plug2,491);    // 58..59

                                                  plug1:=plug2;              // 59
                                                  plug2.x:=tang_pex17.x;     // 52

                                                  do_chair_mark(plug1,plug2,491);    // 59..52




                                                    // add tangs, code 486 near side ...    8 marks

                                                       // 1st tang bottom ...

                                                  tang_pex52.y:=tang_pex52.y+clip_tang_end_space*inscale*side;    // 244d
                                                  tang_pex53.y:=tang_pex53.y-clip_tang_end_space*inscale*side;    // 244d

                                                  plug1:=tang_pex52;                // 0
                                                  plug2:=tang_pex53;                // 1

                                                  do_chair_mark(plug1,plug2,486);   // 0..1

                                                  plug1:=plug2;                     // 1
                                                  plug2.x:=midx-tang_bottom_space;  // 2

                                                  do_chair_mark(plug1,plug2,486);   // 1..2

                                                  plug1:=plug2;                     // 2
                                                  plug2.y:=tang_pex52.y;            // 3

                                                  do_chair_mark(plug1,plug2,486);   // 2..3

                                                  plug1:=plug2;                     // 3
                                                  plug2.x:=tang_pex52.x;            // 0

                                                  do_chair_mark(plug1,plug2,486);


                                                       // 1st tang top ...

                                                  plug1.x:=tang_pex36.x-clip_tang_overcut*inscale;  // 4
                                                  plug1.y:=tang_pex36.y+tang_top_clear*side;

                                                  plug2:=plug1;                                // 5
                                                  plug2.y:=tang_pex37.y-tang_top_clear*side;

                                                  do_chair_mark(plug1,plug2,486);   // 4..5

                                                  plug1:=plug2;                         // 5
                                                  plug2.x:=plug1.x+tang_top_width;      // 6

                                                  do_chair_mark(plug1,plug2,486);   // 5..6

                                                  plug1:=plug2;                                  // 6
                                                  plug2.y:=tang_pex36.y+tang_top_clear*side;     // 7

                                                  do_chair_mark(plug1,plug2,486);   // 6..7

                                                  plug1:=plug2;                                  // 7
                                                  plug2.x:=tang_pex36.x-clip_tang_overcut*inscale;    // 4

                                                  do_chair_mark(plug1,plug2,486);   // 7..4



                                                       // 2nd tang bottom ...

                                                  if tang2_spacing<>0
                                                     then begin


                                                            tang_pex54.y:=tang_pex54.y+clip_tang_end_space*inscale*side;    // 244d
                                                            tang_pex55.y:=tang_pex55.y-clip_tang_end_space*inscale*side;    // 244d

                                                            plug1:=tang_pex54;                // 0
                                                            plug2:=tang_pex55;                // 1

                                                            do_chair_mark(plug1,plug2,486);   // 0..1

                                                            plug1:=plug2;                     // 1
                                                            plug2.x:=midx-tang_bottom_space;  // 2

                                                            do_chair_mark(plug1,plug2,486);   // 1..2

                                                            plug1:=plug2;                     // 2
                                                            plug2.y:=tang_pex54.y;            // 3

                                                            do_chair_mark(plug1,plug2,486);   // 2..3

                                                            plug1:=plug2;                     // 3
                                                            plug2.x:=tang_pex54.x;            // 0

                                                            do_chair_mark(plug1,plug2,486);


                                                                 // 2nd tang top ...

                                                            plug1.x:=tang_pex38.x-clip_tang_overcut*inscale;  // 4
                                                            plug1.y:=tang_pex38.y+tang_top_clear*side;

                                                            plug2:=plug1;                                // 5
                                                            plug2.y:=tang_pex39.y-tang_top_clear*side;

                                                            do_chair_mark(plug1,plug2,486);   // 4..5

                                                            plug1:=plug2;                         // 5
                                                            plug2.x:=plug1.x+tang_top_width;      // 6

                                                            do_chair_mark(plug1,plug2,486);   // 5..6

                                                            plug1:=plug2;                                  // 6
                                                            plug2.y:=tang_pex38.y+tang_top_clear*side;     // 7

                                                            do_chair_mark(plug1,plug2,486);   // 6..7

                                                            plug1:=plug2;                                  // 7
                                                            plug2.x:=tang_pex38.x-clip_tang_overcut*inscale;    // 4

                                                            do_chair_mark(plug1,plug2,486);   // 7..4


                                                          end;


                                                    // add tangs, far side ...      8 marks

                                                       // 1st tang bottom ...

                                                  tang_pex59.y:=tang_pex59.y+clip_tang_end_space*inscale*side;    // 244d
                                                  tang_pex58.y:=tang_pex58.y-clip_tang_end_space*inscale*side;    // 244d

                                                  plug1:=tang_pex59;                // 0
                                                  plug2:=tang_pex58;                // 1

                                                  do_chair_mark(plug1,plug2,486);   // 0..1

                                                  plug1:=plug2;                     // 1
                                                  plug2.x:=midx+tang_bottom_space;  // 2

                                                  do_chair_mark(plug1,plug2,486);   // 1..2

                                                  plug1:=plug2;                     // 2
                                                  plug2.y:=tang_pex59.y;            // 3

                                                  do_chair_mark(plug1,plug2,486);   // 2..3

                                                  plug1:=plug2;                     // 3
                                                  plug2.x:=tang_pex59.x;            // 0

                                                  do_chair_mark(plug1,plug2,486);


                                                       // 1st tang top ...

                                                  plug1.x:=tang_pex43.x+clip_tang_overcut*inscale;  // 4
                                                  plug1.y:=tang_pex43.y+tang_top_clear*side;

                                                  plug2:=plug1;                                // 5
                                                  plug2.y:=tang_pex42.y-tang_top_clear*side;

                                                  do_chair_mark(plug1,plug2,486);   // 4..5

                                                  plug1:=plug2;                         // 5
                                                  plug2.x:=plug1.x-tang_top_width;      // 6

                                                  do_chair_mark(plug1,plug2,486);   // 5..6

                                                  plug1:=plug2;                                  // 6
                                                  plug2.y:=tang_pex43.y+tang_top_clear*side;     // 7

                                                  do_chair_mark(plug1,plug2,486);   // 6..7

                                                  plug1:=plug2;                                  // 7
                                                  plug2.x:=tang_pex43.x+clip_tang_overcut*inscale;    // 4

                                                  do_chair_mark(plug1,plug2,486);   // 7..4



                                                       // 2nd tang bottom ...

                                                  if tang2_spacing<>0
                                                     then begin

                                                            tang_pex57.y:=tang_pex57.y+clip_tang_end_space*inscale*side;    // 244d
                                                            tang_pex56.y:=tang_pex56.y-clip_tang_end_space*inscale*side;    // 244d

                                                            plug1:=tang_pex57;                // 0
                                                            plug2:=tang_pex56;                // 1

                                                            do_chair_mark(plug1,plug2,486);   // 0..1

                                                            plug1:=plug2;                     // 1
                                                            plug2.x:=midx+tang_bottom_space;  // 2

                                                            do_chair_mark(plug1,plug2,486);   // 1..2

                                                            plug1:=plug2;                     // 2
                                                            plug2.y:=tang_pex57.y;            // 3

                                                            do_chair_mark(plug1,plug2,486);   // 2..3

                                                            plug1:=plug2;                     // 3
                                                            plug2.x:=tang_pex57.x;            // 0

                                                            do_chair_mark(plug1,plug2,486);


                                                                 // 2nd tang top ...

                                                            plug1.x:=tang_pex41.x+clip_tang_overcut*inscale;  // 4
                                                            plug1.y:=tang_pex41.y+tang_top_clear*side;

                                                            plug2:=plug1;                                // 5
                                                            plug2.y:=tang_pex40.y-tang_top_clear*side;

                                                            do_chair_mark(plug1,plug2,486);   // 4..5

                                                            plug1:=plug2;                         // 5
                                                            plug2.x:=plug1.x-tang_top_width;      // 6

                                                            do_chair_mark(plug1,plug2,486);   // 5..6

                                                            plug1:=plug2;                                  // 6
                                                            plug2.y:=tang_pex41.y+tang_top_clear*side;     // 7

                                                            do_chair_mark(plug1,plug2,486);   // 6..7

                                                            plug1:=plug2;                                  // 7
                                                            plug2.x:=tang_pex41.x+clip_tang_overcut*inscale;    // 4

                                                            do_chair_mark(plug1,plug2,486);   // 7..4

                                                          end;

                                                end;//plug

                                        
                                        if (    (dxf_form.timbers_fdm_bullet_shape.Visible=True)
                                             or (dxf_form.timbers_laser_bullet_shape.Visible=True)
                                             or (dxf_form.reset_both_bullet_shape.Visible=True)
                                             or ( (dxf_form.cot_bullet_shape.Visible=True) and (heaved_plug<>0) )
                                           )
                                       and ( (dxf_form.cot_radio.Checked=False) or (heaved_plug<>0) )
                                       and (heaved_plug<>4)
                                           then begin
                                                        // doing a socket...

                                                  sf_sides:=0;    // init...
                                                  sf_ends:=0;
                                                  sf_flap_ends:=0;
                                                  socket_undercut:=0;

                                                  flap_jut_length:=clip_socket_flap_jut_length*inscale;        // init 241c ...
                                                  flap_rear_length:=clip_socket_flap_rear_length*inscale;
                                                  flap_clear_width:=clip_socket_flap_clear_width*inscale;


                                                  if chair_code>90         // RG and FG dummy chairs (for rail grooves)  // 234a
                                                     then begin
                                                            sf_sides:=0;
                                                            sf_ends:=0;
                                                            sf_flap_ends:=0;
                                                            socket_undercut:=0;
                                                          end
                                                     else if ((dxf_form.snap_fit_radio.Checked=True) and (heaved_plug=0))
                                                          or (heaved_plug=2)       // 241b ...
                                                             then begin
                                                                    if (timb_str='A') or (timb_str='N') or (timb_str='E') or (timb_str='R')
                                                                       then sf_sides:=snap_socket_fit_sides_sl
                                                                       else sf_sides:=snap_socket_fit_sides_timb;

                                                                    sf_ends:=snap_socket_fit_ends;
                                                                    sf_flap_ends:=0;

                                                                    socket_undercut:=snap_socket_undercut;
                                                                  end
                                                             else if ((dxf_form.clip_fit_radio.Checked=True) and (heaved_plug=0))
                                                                  or (heaved_plug=1)       // 241b ...
                                                                     then begin
                                                                            if (timb_str='A') or (timb_str='N') or (timb_str='E') or (timb_str='R')
                                                                               then sf_sides:=clip_socket_fit_sides_sl
                                                                               else sf_sides:=clip_socket_fit_sides_timb;

                                                                            sf_ends:=clip_socket_fit_ends;

                                                                            if dxf_form.end_flaps_checkbox.Checked=True                // 241c
                                                                               then sf_flap_ends:=clip_socket_flap_top_length*inscale
                                                                               else sf_flap_ends:=0;

                                                                            socket_undercut:=clip_socket_undercut;
                                                                          end
                                                                     else if ((dxf_form.press_fit_radio.Checked=True) and (heaved_plug=0))
                                                                          or (heaved_plug=3)// press-fit ...
                                                                             then begin
                                                                                    if (timb_str='A') or (timb_str='N') or (timb_str='E') or (timb_str='R')    // 236d
                                                                                       then sf_sides:=press_socket_fit_sides_sl
                                                                                       else sf_sides:=press_socket_fit_sides_timb;

                                                                                    len:=ABS(p1.y-p2.y)/inscale;     // convert to full size

                                                                                    if len<10                                             // 10"    L1
                                                                                       then sf_ends:=press_short_socket_fit_ends
                                                                                       else if len<15                                     // 15"    S1
                                                                                               then sf_ends:=press_socket_fit_ends
                                                                                               else if len<20                             // 20"    P
                                                                                                       then sf_ends:=press_long_socket_fit_ends
                                                                                                       else sf_ends:=press_very_long_socket_fit_ends;  // 4P, AA, BB

                                                                                    sf_ends:=sf_ends-(dxf_form.plug_fit_trackbar.Position-100)*0.0005;    // range 0..200 =  +0.05 to -0.05 on socket (each end)

                                                                                    sf_flap_ends:=0;

                                                                                    socket_undercut:=0;
                                                                                  end;

                                                               // adjust socket walls for filament 241c ...

                                                            if (dxf_form.filament_checkbox.Checked=True) and (filament_dia_mm<>1.75)
                                                               then begin
                                                                      filament_adjust:=SQR(filament_dia_mm)/3.0625*0.4-0.4;   // assume 0.4mm line width     3.0625 = 1.75*1.75

                                                                      sf_sides:=sf_sides+filament_adjust;
                                                                      sf_ends:=sf_ends+filament_adjust;

                                                                    end;

                                                  // socket outlines for 2-D ...   code 498 actual size

                                                  sock1.x:=p1.x-sf_sides;
                                                  sock1.y:=p1.y-sf_ends*side;

                                                  sock2.x:=p2.x-sf_sides;
                                                  sock2.y:=p2.y+sf_ends*side;

                                                  do_chair_mark(sock1,sock2,498);              // near side for 2-D


                                                  sock1:=sock2;
                                                  sock2.x:=p3.x+sf_sides;
                                                  sock2.y:=p3.y+sf_ends*side;

                                                  do_chair_mark(sock1,sock2,498);              // gauge end for 2-D

                                                  sock1:=sock2;
                                                  sock2.x:=p4.x+sf_sides;
                                                  sock2.y:=p4.y-sf_ends*side;

                                                  do_chair_mark(sock1,sock2,498);              // far side for 2-D


                                                  sock1:=sock2;
                                                  sock2.x:=p1.x-sf_sides;
                                                  sock2.y:=p1.y-sf_ends*side;

                                                  do_chair_mark(sock1,sock2,498);              // outer end for 2-D



                                                  // socket undercut 241b ...

                                                  if chair_code<=90         // RG and FG dummy chairs (for rail grooves), no undercut  // 234a
                                                     then begin
                                                            undercut:=socket_undercut*inscale;        //  7/16" default width of side undercut

                                                            sock1.x:=p1.x-sf_sides-undercut;
                                                            sock1.y:=p1.y-sf_ends*side;

                                                            sock2.x:=p2.x-sf_sides-undercut;
                                                            sock2.y:=p2.y+sf_ends*side;

                                                            do_chair_mark(sock1,sock2,498);    // near side

                                                            sock1:=sock2;
                                                            sock2.x:=p3.x+sf_sides+undercut;
                                                            sock2.y:=p3.y+sf_ends*side;

                                                            do_chair_mark(sock1,sock2,498);    // gauge end


                                                            sock1:=sock2;
                                                            sock2.x:=p4.x+sf_sides+undercut;
                                                            sock2.y:=p4.y-sf_ends*side;

                                                            do_chair_mark(sock1,sock2,498);    // far side


                                                            sock1:=sock2;
                                                            sock2.x:=p1.x-sf_sides-undercut;
                                                            sock2.y:=p1.y-sf_ends*side;

                                                            do_chair_mark(sock1,sock2,498);    // outer end

                                                          end;


                                                  // socket outline for 2-D (laser-cutting)...   code 9997 kerf_adjusted

                                                  if (dxf_form.kerf_plus_undercut_checkbox.Checked=True) and (socket_undercut<>0)
                                                     then kerf_undercut:=socket_undercut*inscale+kerf_extra_undercut_mm
                                                     else kerf_undercut:=0;

                                                  sock1.x:=p1.x-(sf_sides-kerf_offset+kerf_undercut);
                                                  sock1.y:=p1.y-(sf_ends-kerf_offset)*side;

                                                  sock2.x:=p2.x-(sf_sides-kerf_offset+kerf_undercut);
                                                  sock2.y:=p2.y+(sf_ends-kerf_offset)*side;

                                                  do_chair_mark(sock1,sock2,9997);              // near side for 2-D


                                                  if (dxf_form.indented_socket_checkbox.Checked=True) and (socket_indent<>0)   // 244a
                                                     then begin
                                                            sock1:=sock2;
                                                            sock2.x:=(p2.x+p3.x)/2;
                                                            sock2.y:=(p2.y+p3.y)/2-socket_indent*inscale*side;

                                                            do_chair_mark(sock1,sock2,9997);              // gauge end for 2-D, first half
                                                          end;


                                                  sock1:=sock2;
                                                  sock2.x:=p3.x+(sf_sides-kerf_offset+kerf_undercut);
                                                  sock2.y:=p3.y+(sf_ends-kerf_offset)*side;

                                                  do_chair_mark(sock1,sock2,9997);              // gauge end for 2-D

                                                  sock1:=sock2;
                                                  sock2.x:=p4.x+(sf_sides-kerf_offset+kerf_undercut);
                                                  sock2.y:=p4.y-(sf_ends-kerf_offset)*side;

                                                  do_chair_mark(sock1,sock2,9997);              // far side for 2-D


                                                  if (dxf_form.indented_socket_checkbox.Checked=True) and (socket_indent<>0)   // 244a
                                                     then begin
                                                            sock1:=sock2;
                                                            sock2.x:=(p1.x+p4.x)/2;
                                                            sock2.y:=(p1.y+p4.y)/2+socket_indent*inscale*side;

                                                            do_chair_mark(sock1,sock2,9997);              // outer end for 2-D, first half
                                                          end;


                                                  sock1:=sock2;
                                                  sock2.x:=p1.x-(sf_sides-kerf_offset+kerf_undercut);
                                                  sock2.y:=p1.y-(sf_ends-kerf_offset)*side;

                                                  do_chair_mark(sock1,sock2,9997);              // outer end for 2-D


                                                  // repeat socket outline and add chamfer for 3-D (code 492, 12 marks) 228a , 241b ...

                                                  socket_code:=492;   // socket outline full, plus top chamfer outline  plus undercut (20 marks)

                                                    // outline 0..3  ...

                                                  sock1.x:=p1.x-sf_sides;
                                                  sock1.y:=p1.y-sf_ends*side-sf_flap_ends*side;

                                                  sock2.x:=p2.x-sf_sides;
                                                  sock2.y:=p2.y+sf_ends*side+sf_flap_ends*side;

                                                  do_chair_mark(sock1,sock2,socket_code);    // near side

                                                  sock1:=sock2;
                                                  sock2.x:=p3.x+sf_sides;
                                                  sock2.y:=p3.y+sf_ends*side+sf_flap_ends*side;

                                                  do_chair_mark(sock1,sock2,socket_code);    // gauge end

                                                  sock1:=sock2;
                                                  sock2.x:=p4.x+sf_sides;
                                                  sock2.y:=p4.y-sf_ends*side-sf_flap_ends*side;

                                                  do_chair_mark(sock1,sock2,socket_code);    // far side

                                                  sock1:=sock2;
                                                  sock2.x:=p1.x-sf_sides;
                                                  sock2.y:=p1.y-sf_ends*side-sf_flap_ends*side;

                                                  do_chair_mark(sock1,sock2,socket_code);    // outer end


                                                    // socket chamfer  4..7    228a ...

                                                  if chair_code>90         // RG and FG dummy chairs (for rail grooves), no chamfer  // 234a
                                                     then chamf:=0
                                                     else chamf:=socket_chamfer*inscale;        //  3/8" default width of socket top chamfer

                                                  sock1.x:=p1.x-sf_sides-chamf;
                                                  sock1.y:=p1.y-sf_ends*side-chamf*side-sf_flap_ends*side;

                                                  sock2.x:=p2.x-sf_sides-chamf;
                                                  sock2.y:=p2.y+sf_ends*side+chamf*side+sf_flap_ends*side;

                                                  do_chair_mark(sock1,sock2,socket_code);    // near side

                                                  sock1:=sock2;
                                                  sock2.x:=p3.x+sf_sides+chamf;
                                                  sock2.y:=p3.y+sf_ends*side+chamf*side+sf_flap_ends*side;

                                                  do_chair_mark(sock1,sock2,socket_code);    // gauge end


                                                  sock1:=sock2;
                                                  sock2.x:=p4.x+sf_sides+chamf;
                                                  sock2.y:=p4.y-sf_ends*side-chamf*side-sf_flap_ends*side;

                                                  do_chair_mark(sock1,sock2,socket_code);    // far side

                                                  sock1:=sock2;
                                                  sock2.x:=p1.x-sf_sides-chamf;
                                                  sock2.y:=p1.y-sf_ends*side-chamf*side-sf_flap_ends*side;

                                                  do_chair_mark(sock1,sock2,socket_code);    // outer end



                                                    // socket undercut 8..11  241b ...

                                                  if chair_code>90         // RG and FG dummy chairs (for rail grooves), no undercut  // 234a
                                                     then undercut:=0
                                                     else undercut:=socket_undercut*inscale;        //  7/16" default width of side undercut

                                                  sock1.x:=p1.x-sf_sides-undercut;
                                                  sock1.y:=p1.y-sf_ends*side;//-sf_flap_ends*side;

                                                  sock2.x:=p2.x-sf_sides-undercut;
                                                  sock2.y:=p2.y+sf_ends*side;//+sf_flap_ends*side;

                                                  do_chair_mark(sock1,sock2,socket_code);    // near side

                                                  sock1:=sock2;
                                                  sock2.x:=p3.x+sf_sides+undercut;
                                                  sock2.y:=p3.y+sf_ends*side;//+sf_flap_ends*side;

                                                  do_chair_mark(sock1,sock2,socket_code);    // gauge end


                                                  sock1:=sock2;
                                                  sock2.x:=p4.x+sf_sides+undercut;
                                                  sock2.y:=p4.y-sf_ends*side;//-sf_flap_ends*side;

                                                  do_chair_mark(sock1,sock2,socket_code);    // far side

                                                  sock1:=sock2;
                                                  sock2.x:=p1.x-sf_sides-undercut;
                                                  sock2.y:=p1.y-sf_ends*side;//-sf_flap_ends*side;

                                                  do_chair_mark(sock1,sock2,socket_code);    // outer end


                                                    // repeat chamfer without flap ends 12..15  241c ...

                                                  if chair_code>90         // RG and FG dummy chairs (for rail grooves), no chamfer  // 234a
                                                     then chamf:=0
                                                     else chamf:=socket_chamfer*inscale;        //  3/8" default width of socket top chamfer

                                                  sock1.x:=p1.x-sf_sides-chamf;
                                                  sock1.y:=p1.y-sf_ends*side-chamf*side;//-sf_flap_ends*side;

                                                  sock2.x:=p2.x-sf_sides-chamf;
                                                  sock2.y:=p2.y+sf_ends*side+chamf*side;//+sf_flap_ends*side;

                                                  do_chair_mark(sock1,sock2,socket_code);    // near side

                                                  sock1:=sock2;
                                                  sock2.x:=p3.x+sf_sides+chamf;
                                                  sock2.y:=p3.y+sf_ends*side+chamf*side;//+sf_flap_ends*side;

                                                  do_chair_mark(sock1,sock2,socket_code);    // gauge end

                                                  sock1:=sock2;
                                                  sock2.x:=p4.x+sf_sides+chamf;
                                                  sock2.y:=p4.y-sf_ends*side-chamf*side;//-sf_flap_ends*side;

                                                  do_chair_mark(sock1,sock2,socket_code);    // far side

                                                  sock1:=sock2;
                                                  sock2.x:=p1.x-sf_sides-chamf;
                                                  sock2.y:=p1.y-sf_ends*side-chamf*side;//-sf_flap_ends*side;

                                                  do_chair_mark(sock1,sock2,socket_code);    // outer end

                                                    // repeat outline without flap ends 16..19  241c ...

                                                  sock1.x:=p1.x-sf_sides;
                                                  sock1.y:=p1.y-sf_ends*side;//-sf_flap_ends*side;

                                                  sock2.x:=p2.x-sf_sides;
                                                  sock2.y:=p2.y+sf_ends*side;//+sf_flap_ends*side;

                                                  do_chair_mark(sock1,sock2,socket_code);    // near side

                                                  sock1:=sock2;
                                                  sock2.x:=p3.x+sf_sides;
                                                  sock2.y:=p3.y+sf_ends*side;//+sf_flap_ends*side;

                                                  do_chair_mark(sock1,sock2,socket_code);    // gauge end

                                                  sock1:=sock2;
                                                  sock2.x:=p4.x+sf_sides;
                                                  sock2.y:=p4.y-sf_ends*side;//-sf_flap_ends*side;

                                                  do_chair_mark(sock1,sock2,socket_code);    // far side

                                                  sock1:=sock2;
                                                  sock2.x:=p1.x-sf_sides;
                                                  sock2.y:=p1.y-sf_ends*side;//-sf_flap_ends*side;

                                                  do_chair_mark(sock1,sock2,socket_code);    // outer end


                                                    // do near flap bottom 20..23 ...

                                                  sock1.x:=p1.x+flap_clear_width;
                                                  sock1.y:=p1.y-flap_rear_length*side;

                                                  sock2:=sock1;
                                                  sock2.y:=p1.y+flap_jut_length*side;

                                                  do_chair_mark(sock1,sock2,socket_code);    // near side

                                                  sock1:=sock2;
                                                  sock2.x:=p4.x-flap_clear_width;

                                                  do_chair_mark(sock1,sock2,socket_code);    // jut end

                                                  sock1:=sock2;
                                                  sock2.y:=p1.y-flap_rear_length*side;

                                                  do_chair_mark(sock1,sock2,socket_code);    // far side

                                                  sock1:=sock2;
                                                  sock2.x:=p1.x+flap_clear_width;;

                                                  do_chair_mark(sock1,sock2,socket_code);    // rear end


                                                   // do far flap bottom 24..27 ...

                                                  sock1.x:=p2.x+flap_clear_width;
                                                  sock1.y:=p2.y+flap_rear_length*side;

                                                  sock2:=sock1;
                                                  sock2.y:=p2.y-flap_jut_length*side;

                                                  do_chair_mark(sock1,sock2,socket_code);    // near side

                                                  sock1:=sock2;
                                                  sock2.x:=p3.x-flap_clear_width;

                                                  do_chair_mark(sock1,sock2,socket_code);    // jut end

                                                  sock1:=sock2;
                                                  sock2.y:=p2.y+flap_rear_length*side;

                                                  do_chair_mark(sock1,sock2,socket_code);    // far side

                                                  sock1:=sock2;
                                                  sock2.x:=p2.x+flap_clear_width;;

                                                  do_chair_mark(sock1,sock2,socket_code);    // rear end

                                                end;//socket

                                      end;//with chairing_dims
                                    end;
                                    //==========================================


                  begin
                    shift_slot1:=0;             // init...
                    shift_slot2:=0-6*inscale;   // dummy second slot, plated over - arbitrary position under rail seat

                    shift_slot1_along:=0;       // 241a
                    shift_slot2_along:=0;       // 239a

                    twist_slot1:=0;
                    twist_slot2:=0;

                    jaw2_dir:=1;

                    curvi_mod:=0;
                    curvi_k:=0;
                    curvi_k_mod:=0;

                    with heaved_chairing_dims.chairing_dims do begin

                					 // SC 17-SEP-2024 556
                      {if brick_form.Showing=True   // chair labels ...  MW 03-08-2024   555a
                         then begin}
                      // sc 17-sep-2024 556
{
 chair labels:

 441..458      // as below-20 = label currently selected  556b MW

 461,471 = interchangeable chair labels
 462,472 = switch block chair labels
 463,473 = V-crossing chair labels
 464,474 = K-crossing chair labels
 ... ... other specials
 468,478 = SC customizable chair labels
}

                      label_code:=0;       // init..
                      label_code_mod:=0;

                      if (((rail_code=2) or (rail_code=4)) and (hand_i=1))  // turnout road LH TEMPLATE
                      or (((rail_code=1) or (rail_code=3)) and (hand_i=-1)) // main road    RH TEMPLATE
                         then label_code_mod:=10;

                      case chair_code of
                        // SC 17-SEP-2024 556

                        {1..3,                                                            // interchangeable S1, P, L1CC,
                        5..9,                                                            // L1, M1, S1J, S1O, S1N,
                        14..20: if brick_form.chair_label_interch_checkbox.Checked=True  // CCL/R, CC, CCR/L, SS, MS, L1CCL/R, L1CCR/L
                                   then label_code:=461+label_code_mod;

                            10: if brick_form.chair_label_sc_checkbox.Checked=True       // customizable SC
                                   then label_code:=469+label_code_mod;

                         11,12: if brick_form.chair_label_switch_block_checkbox.Checked=True   // switch block 1P..2P, 3P..11P
                                   then label_code:=462+label_code_mod;

                        21..28: if brick_form.chair_label_vxing_checkbox.Checked=True   // V-crossing ZY, XN, XA, AA, AB, BB/BC, CD/DD, EF
                                   then label_code:=463+label_code_mod;}

                        1..3,                                     // interchangeable S1, P, L1CC,
                        5..9,                                     // L1, M1, S1J, S1O, S1N,
                        14..20: label_code:=461+label_code_mod;   // CCL/R, CC, CCR/L, SS, MS, L1CCL/R, L1CCR/L
                                        
                            10: label_code:=469+label_code_mod;   // customizable SC

                         11,12: label_code:=462+label_code_mod;   // switch block 1P..2P, 3P..11P

                        21..28: label_code:=463+label_code_mod;   // V-crossing ZY, XN, XA, AA, AB, BB/BC, CD/DD, EF

                        // sc 17-sep-2024 556

{nyi
    40: 'SDP';
    41: 'SDS';
    50: 'PW';
    51: 'PWL';
    52: 'PWR';
    98: 'FG'; //dummy chair  rail foot groove
    99: 'RG'; //dummy chair  rail head groove
}

                      end;//case

                       // 556b for selected chair label..

                      if (heave_selected_rail=rail_code) and  (heave_selected_tb_str=timber_str) and (current_shove_str=timber_str)
                         then label_code:=label_code-20;  // 441..458

                      tempex.x:=xtimbcl;     // timber centre

                        // label positions...

                      case chair_code of

                           5: begin                           // L1
                                tempex.y:=0-13*inscale*side;  // init  labels in six-foot  L1  13" arbitrary
                                if (xtimbcl>toex)
                               and ((xtimbcl<(ipx-30*inscale)) or ((xtimbcl>fpx) and (xtimbcl<mvjpx)))   // labels in four-foot,  beyond switch up to mid (IP-30" arbitrary), or beyond xing up to joint
                                   then begin
                                          if rail_code=2 then tempex.y:=9*inscale;         // over-rides   L1 9" arbitrary..
                                          if rail_code=3 then tempex.y:=9*inscale*side;
                                        end;
                              end;

                              // xing labels ...

                          22: tempex.y:=16*inscale;    // XN  16" arbitrary

                          23: tempex.y:=aq2offset(xtimbcl,dummy)-g+19*inscale;   // XA  19" arbitrary

                       24,25: tempex.y:=aq2offset(xtimbcl,dummy)-g+18.5*inscale; // AA, AB  18.5" arbitrary

                          26: tempex.y:=aq2offset(xtimbcl,dummy)-g+20*inscale;   // BB/BC  20" arbitrary

                       27,28: tempex.y:=aq2offset(xtimbcl,dummy)-g+12*inscale;   // CD/DD, EF  12" arbitrary

                         else begin
                                tempex.y:=0-15*inscale*side;  // init  labels in six-foot    15" arbitrary

                                if (xtimbcl>toex)
                               and ((xtimbcl<(ipx-30*inscale)) or ((xtimbcl>fpx) and (xtimbcl<mvjpx)))   // labels in four-foot,  beyond switch up to mid (IP-30" arbitrary), or beyond xing up to joint
                                   then begin
                                          if rail_code=2 then tempex.y:=11*inscale;         // over-rides  not L1 11" arbitrary..
                                          if rail_code=3 then tempex.y:=0-11*inscale;
                                        end;
                              end;

                      end;//case

                      if label_code<>0
                         then begin
                                case rail_code of                                             // global  encode rail_code in 2 LSbits
                                     1: begin bit_settings:=bit_settings AND $FFFFFFFC; end;
                                     2: begin bit_settings:=bit_settings AND $FFFFFFFC; bit_settings:=bit_settings OR $00000001; end;
                                     3: begin bit_settings:=bit_settings AND $FFFFFFFC; bit_settings:=bit_settings OR $00000002; end;
                                     4: begin bit_settings:=bit_settings AND $FFFFFFFC; bit_settings:=bit_settings OR $00000003; end;
                                end;//case

                                do_chair_mark(tempex,tempex,label_code);   // chair label
                              end;

                            //end;   SC 17-SEP-2024 556

                      if (scale>4.05) and (dxf_form.increase_plug_size_checkbox.Checked=True)    // 244d  larger scales,  reduce overhang
                         then begin

                                if chair_code=5         // L1 chair
                                   then begin
                                          plug_spacing_sides:=0.5;       // plug from L1 sides
                                          plug_spacing_outer_end:=0.12;  // plug from L1 outer end  (allow loose jaw slot)
                                          plug_spacing_inner_end:=0.5;   // plug from L1 inner end  (max socket wall between adjacent L1 chairs)

                                          slot_shear:=(plug_extended_depth)*inscale*TAN(slot_angle);   // angled slot in L1 plugs
                                        end
                                   else begin
                                          plug_spacing_sides:=0.35;      // plug 0.35mm inside all round
                                          plug_spacing_outer_end:=0.35;
                                          plug_spacing_inner_end:=0.35;

                                          slot_shear:=0;
                                        end;

                              end
                         else begin

                                if chair_code=5          // L1 chair
                                   then begin
                                          plug_spacing_sides:=1.5*inscale;        // plug 1.5"    from L1 sides
                                          plug_spacing_outer_end:=0.25*inscale;   // plug 0.25"   from L1 outer end  (allow loose jaw slot)
                                          plug_spacing_inner_end:=1.5*inscale;    // plug 1.5"    from L1 inner end  (max socket wall between adjacent L1 chairs)

                                          slot_shear:=(plug_extended_depth)*inscale*TAN(slot_angle);   // angled slot in L1 plugs
                                        end
                                   else begin
                                          plug_spacing_sides:=inscale;      // plug 1" inside all round 233d
                                          plug_spacing_outer_end:=inscale;
                                          plug_spacing_inner_end:=inscale;

                                          slot_shear:=0;
                                        end;

                              end;

                      outer_beefing:=0;  // init..
                      inner_beefing:=0;

                      if (dxf_form._3d_fdm_radiobutton.Checked=True) and (dxf_form.fdm_beefing_checkbox.Checked=True)
                         then begin
                                outer_beefing:=fdm_outer_beefing;
                                inner_beefing:=fdm_inner_beefing;
                              end
                         else if (dxf_form._3d_resin_radiobutton.Checked=True) and (dxf_form.resin_beefing_checkbox.Checked=True)
                                 then begin
                                        outer_beefing:=resin_outer_beefing;
                                        inner_beefing:=resin_inner_beefing;
                                      end;

                      plinth_spacing_sides:=_3d_data.chair_plinth_spacing_sides*inscale;                      // plinth 1" inside
                      plinth_spacing_inner_end:=(_3d_data.chair_plinth_spacing_ends-inner_beefing)*inscale;   // 556a plinth 1" inside, less beefing
                      plinth_spacing_outer_end:=(_3d_data.chair_plinth_spacing_ends-outer_beefing)*inscale;   // 556a plinth 1" inside, less beefing

                      // radiused corners at 15deg steps from p1-p4 centres = 14 marks per chair, 2 points per mark, sequence clockwise from 9 o'clock on p1

                      arck:=Pi/12;    // 15 degs

                        // first side of chair ...

                      arc1.x:=p1.x-crad_outer;
                      arc1.y:=p1.y;

                      arc2.x:=p2.x-crad_inner;
                      arc2.y:=p2.y;

                      plug_rect_xleft:=MIN(arc1.x,arc2.x)+plug_spacing_sides;  // 233d   plug rectangle



                      plinth_rect_xleft:=MIN(arc1.x,arc2.x)+plinth_spacing_sides;  // 233d   plinth rectangle

                      do_chair_mark(arc1,arc2,chcode);  // code 484,485,493,494,497 = chair outline, radiused corners - code in first mark only

                           // rad around p2 ..

                      n:=1;

                      repeat

                        arc1.x:=p2.x-crad_inner*COS(n*arck);
                        arc1.y:=p2.y+crad_inner*SIN(n*arck)*side;

                        arc2.x:=p2.x-crad_inner*COS((n+1)*arck);
                        arc2.y:=p2.y+crad_inner*SIN((n+1)*arck)*side;

                        do_chair_mark(arc1,arc2,0);

                        n:=n+2;

                      until n>5;

                      plug_rect_yinner:=arc2.y-plug_spacing_inner_end*side;       // 233d   plug rectangle

                      plinth_rect_yinner:=arc2.y-plinth_spacing_inner_end*side;   // 556a   plinth rectangle

                            // rad around p3 ..

                      n:=0;

                      repeat

                        arc1.x:=p3.x+crad_inner*SIN(n*arck);
                        arc1.y:=p3.y+crad_inner*COS(n*arck)*side;

                        arc2.x:=p3.x+crad_inner*SIN((n+1)*arck);
                        arc2.y:=p3.y+crad_inner*COS((n+1)*arck)*side;

                        do_chair_mark(arc1,arc2,0);

                        n:=n+2;

                      until n>4;


                        // opposite side of chair ...

                      arc1.x:=p3.x+crad_inner;
                      arc1.y:=p3.y;

                      arc2.x:=p4.x+crad_outer;
                      arc2.y:=p4.y;

                      plug_rect_xright:=MAX(arc1.x,arc2.x)-plug_spacing_sides;      // 233d  plug rectangle

                      plinth_rect_xright:=MAX(arc1.x,arc2.x)-plinth_spacing_sides;  // 233d  plinth rectangle

                      do_chair_mark(arc1,arc2,0);

                           // rad around p4 ..

                      n:=1;

                      repeat

                        arc1.x:=p4.x+crad_outer*COS(n*arck);
                        arc1.y:=p4.y-crad_outer*SIN(n*arck)*side;

                        arc2.x:=p4.x+crad_outer*COS((n+1)*arck);
                        arc2.y:=p4.y-crad_outer*SIN((n+1)*arck)*side;

                        do_chair_mark(arc1,arc2,0);

                        n:=n+2;

                      until n>5;

                      plug_rect_youter:=arc1.y+plug_spacing_outer_end*side;       // 233d   plug rectangle

                      plinth_rect_youter:=arc1.y+plinth_spacing_outer_end*side;   // 556a   plinth rectangle

                            // rad around p1 ..

                      n:=0;

                      repeat

                        arc1.x:=p1.x-crad_outer*SIN(n*arck);
                        arc1.y:=p1.y-crad_outer*COS(n*arck)*side;

                        arc2.x:=p1.x-crad_outer*SIN((n+1)*arck);
                        arc2.y:=p1.y-crad_outer*COS((n+1)*arck)*side;

                        do_chair_mark(arc1,arc2,0);

                        n:=n+2;

                      until n>4;


                             //  1=S1 chair, 2=P chair, 3=  , 4= , 5=L1 bridge chair, 6=M1 bridge chair, 7=S1J joint chair
                             //  8=S1O half-bolted chair outer, 9=S1N half-bolted chair inner
                             //  10=switch block chair stock rail half

                             //  11=switch block chair switch rail half, type 1 (no jaw)     A B C D switches  1PR and 2PR
                             //  12=switch block chair switch rail half, type 2 (normal key) A B C D E F switches
                             //  13=switch block chair switch rail half, type 3 (inner key)  E F switches 1PR and 2PR

                        // plinth ...

                              // normal full chairs, plinth rectangle spaced all round

                      p1.x:=plinth_rect_xleft;
                      p1.y:=plinth_rect_youter;

                      p2.x:=p1.x;
                      p2.y:=plinth_rect_yinner;

                      p3.x:=plinth_rect_xright;
                      p3.y:=p2.y;

                      p4.x:=p3.x;
                      p4.y:=p1.y;

                      do_chair_mark(p1,p2,0);   // plinth rectangle first side
                      do_chair_mark(p3,p4,0);   // second side


                        // pin slots for loose outer jaws ... 228b  (needed in list before doing plug)

                      slot_cl:=(plinth_rect_xleft+plinth_rect_xright)/2;


                      // 1st pin slot   ...

                      case chair_code of    // angled crossing chairs    ZY, XN, XA, AA, AB, BB/BC, CD/DD, EF chairs

                          21..28: begin
                                    get_xtimbcl2;

                                    curvi_mod:=aq2offset(xtimbcl2,curvi_k)-g-(xtimbcl-fpx)*TAN(k3);    // diff offset regular to curviform/generic

                                    curvi_k_mod:=curvi_k-k3;  // diff angle regular to curviform/generic
                                  end;

                      end;//case


                      case chair_code of    // crossing chairs

                          22: begin                            // XN chair
                                xnl:=xtimbcl-wingcx_minus;     // length along knuckle to chair centre on rail 3

                                shift_slot1:=xnl*SIN(k3/2);    // XN knuckle chair   rail moving away from gauge line
                              end;

                      23..25: begin                            // XA, AA, AB chair

                                shift_slot1:=(xtimbcl-wingcx)*SIN(k3/2)*2+fw_tweak_xing;   // regular xing

                                shift_slot1:=shift_slot1+curvi_mod; //*COS(curvi_k);  // adjust slot position for curviform

                              end;

                          26: begin       // BB/BC wing flares chair           241a
                                          // TS wing

                                shift_slot1:=((xtimbcl-wingcx)*SIN(k3/2)*2+fw_tweak_xing)/COS(k3)+wing_end_flares.ts_wing_add/COS(wing_end_flares.ts_wing_k+k3);

                                shift_slot1:=shift_slot1+curvi_mod; //*COS(curvi_k);  // adjust slot position for curviform

                              end;

                         else shift_slot1:=0;     // other chairs   slot for main rail

                      end;//case

                         // unshifted slot original slot...

                      loose_pin_org1.x:=slot_cl-pin_slot_halfwide*inscale;
                      loose_pin_org1.y:=yret-(_3d_data.outer_jaw_face-pin_slot_length)*inscale*side;

                      loose_pin_org2.x:=loose_pin_org1.x;
                      loose_pin_org2.y:=yret-_3d_data.outer_jaw_face*inscale*side;

                      loose_pin_org3.x:=slot_cl+pin_slot_halfwide*inscale;
                      loose_pin_org3.y:=loose_pin_org2.y;

                      loose_pin_org4.x:=loose_pin_org3.x;
                      loose_pin_org4.y:=loose_pin_org1.y;


                         // shifted first slot...

                      loose_pin2.x:=slot_cl-pin_slot_halfwide*inscale;     // 236d
                      loose_pin2.y:=yret-_3d_data.outer_jaw_face*inscale*side;

                      loose_pin2.y:=loose_pin2.y+shift_slot1;  // 239a

                      loose_pin1.x:=loose_pin2.x;
                      loose_pin1.y:=loose_pin2.y-pin_slot_length*inscale*side;  // 236d

                      loose_pin3.x:=slot_cl+pin_slot_halfwide*inscale;           // 236d
                      loose_pin3.y:=loose_pin2.y;

                      loose_pin4.x:=loose_pin3.x;
                      loose_pin4.y:=loose_pin1.y;


                        // 2nd slots ...

                      case chair_code of

                          12: begin                                               // 3P..11P switch heel block chair
                                if side=-1          // TS chair
                                   then begin
                                          shift_slot2:=aq3offset(slot_cl,twist_slot2)-g; // TS  set 2nd slot angle 3P..11P chair
                                          twist_slot2:=0-twist_slot2;
                                        end
                                   else shift_slot2:=aq2offset(slot_cl,twist_slot2);     // MS  set 2nd slot angle 3P..11P chair

                                jaw2_dir:=1;   // second jaw facing same way as first jaw, no mirror
                              end;

                          14: begin   // check rail flare-in chair

                                check_flare_shift:=0;  // init
                                check_flare_twist:=0;  // init

                                if (side=-1) and (check_end_flare4.check_chair=1)          // rail 4 TS and flare in
                                   then begin
                                          check_flare_shift:=check_end_flare4.check_add;
                                          check_flare_twist:=0-check_end_flare4.check_k;
                                        end;

                                if (side=1) and (check_end_flare1.check_chair=1)           // rail 1 MS and flare in
                                   then begin
                                          check_flare_shift:=check_end_flare1.check_add;
                                          check_flare_twist:=0-check_end_flare1.check_k;
                                        end;

                                shift_slot2:=(fw+fw_mods)+check_flare_shift;

                                twist_slot2:=0-check_flare_twist*side;

                                jaw2_dir:=-1;    // mirror second jaw facing opposite way from first jaw
                              end;

                          15: begin                             // parallel check CC chair
                                shift_slot2:=fw+fw_mods;
                                twist_slot2:=0;

                                jaw2_dir:=-1;    // mirror second jaw facing opposite way from first jaw
                              end;

                          16: begin                                               // check rail flare-out chair

                                check_flare_shift:=0;  // init
                                check_flare_twist:=0;  // init

                                if (side=-1) and (check_end_flare4.check_chair=3)          // rail 4 TS and flare out
                                   then begin
                                          check_flare_shift:=check_end_flare4.check_add;
                                          check_flare_twist:=0-check_end_flare4.check_k;
                                        end;

                                if (side=1) and (check_end_flare1.check_chair=3)           // rail 1 MS and flare out
                                   then begin
                                          check_flare_shift:=check_end_flare1.check_add;
                                          check_flare_twist:=0-check_end_flare1.check_k;
                                        end;

                                shift_slot2:=(fw+fw_mods)+check_flare_shift;
                                twist_slot2:=check_flare_twist*side;

                                jaw2_dir:=-1;    // mirror second jaw facing opposite way from first jaw
                              end;

                          21: begin             // ZY
                                get_xtimbcl2;

                                shift_slot2:=g-aq2offset(xtimbcl2,twist_slot2);    // ZY wing front chair
                                twist_slot2:=0-twist_slot2+k3/2;

                                shift_slot2_along:=(((loose_pin1.y+loose_pin2.y)/2)-shift_slot2)*TAN(twist_slot2);

                                jaw2_dir:=-1;    // mirror second jaw facing opposite way from first jaw
                              end;

                          22: begin             // XN
                                get_xtimbcl2;

                                shift_slot2:=(g-aq2offset(xtimbcl2,dummy))/COS(k3/2);    // XN knuckle chair

                                twist_slot2:=0;  // on knuckle bend, equalized on chair base

                                jaw2_dir:=-1;    // mirror second jaw facing opposite way from first jaw

                              end;

                    23,24,25: begin                // XA gap chair,  AA nose chair,  AB splice chair
                                get_xtimbcl2;

                                shift_slot2:=(fpx-xtimbcl2)*TAN(K3); //+fw_tweak_xing;

                                twist_slot2:=k3/2;  // rail 2 along wing rail

                                jaw2_dir:=-1;    // mirror second jaw facing opposite way from first jaw

                              end;

                          26: begin                // BB/BC wing flare chair    chair not symmetrical, remove shift_slot1 from symmetry

                                shift_slot2:=0-shift_slot1+fw+fw_tweak_xing+wing_end_flares.ms_wing_add/COS(wing_end_flares.ms_wing_k);

                                twist_slot2:=k3/2+wing_end_flares.ms_wing_k;  // along MS wing rail

                                shift_slot2_along:=0;

                                jaw2_dir:=-1;    // mirror second jaw facing opposite way from first jaw

                              end;

                          27: begin                            // CD/DD vee chairs  no pin slots
                                shift_slot2:=0-6*inscale;      // under rail seat default unused plated over
                                twist_slot2:=0;

                                jaw2_dir:=-1;
                              end;

                          28: begin             // EF
                                get_xtimbcl2;

                                shift_slot2:=g-aq2offset(xtimbcl2,twist_slot2);    // EF exit chair
                                twist_slot2:=0-twist_slot2+k3/2;

                                shift_slot2_along:=0;

                                jaw2_dir:=-1;    // mirror second jaw facing opposite way from first jaw
                              end;

                         else begin
                                shift_slot2:=0-6*inscale;      // under rail seat default unused plated over
                                twist_slot2:=0;

                                jaw2_dir:=-1;
                              end;

                      end;//case

                        // rectangle position for second slot ... 237a

                      if twist_slot2=0
                         then begin
                                loose_pin9.x:=loose_pin1.x+shift_slot2_along;
                                loose_pin9.y:=loose_pin1.y*jaw2_dir+shift_slot2*side;

                                loose_pin10.x:=loose_pin2.x+shift_slot2_along;
                                loose_pin10.y:=loose_pin2.y*jaw2_dir+shift_slot2*side;

                                loose_pin11.x:=loose_pin3.x+shift_slot2_along;
                                loose_pin11.y:=loose_pin3.y*jaw2_dir+shift_slot2*side;

                                loose_pin12.x:=loose_pin4.x+shift_slot2_along;
                                loose_pin12.y:=loose_pin4.y*jaw2_dir+shift_slot2*side;
                              end
                         else begin

                                    // twisted 2nd slot

                                dotransform(twist_slot2,slot_cl,(loose_pin1.y+loose_pin2.y)/2,loose_pin1,tempex);
                                loose_pin9.x:=tempex.x+shift_slot2_along;
                                loose_pin9.y:=tempex.y*jaw2_dir+shift_slot2*side;

                                dotransform(twist_slot2,slot_cl,(loose_pin1.y+loose_pin2.y)/2,loose_pin2,tempex);
                                loose_pin10.x:=tempex.x+shift_slot2_along;
                                loose_pin10.y:=tempex.y*jaw2_dir+shift_slot2*side;


                                dotransform(twist_slot2,slot_cl,(loose_pin1.y+loose_pin2.y)/2,loose_pin3,tempex);
                                loose_pin11.x:=tempex.x+shift_slot2_along;
                                loose_pin11.y:=tempex.y*jaw2_dir+shift_slot2*side;


                                dotransform(twist_slot2,slot_cl,(loose_pin1.y+loose_pin2.y)/2,loose_pin4,tempex);
                                loose_pin12.x:=tempex.x+shift_slot2_along;
                                loose_pin12.y:=tempex.y*jaw2_dir+shift_slot2*side;

                              end;

                      if jaw2_dir=1     // no mirror, chair code 12,  switch heel
                         then begin
                                tempex:=loose_pin9;              // invert rectangle to match mirrored rectangles...
                                loose_pin9:=loose_pin10;
                                loose_pin10:=tempex;

                                tempex:=loose_pin11;
                                loose_pin11:=loose_pin12;
                                loose_pin12:=tempex;
                              end;


                       // twist first slot for X crossing chairs ...

                      case chair_code of

                       21,28: begin         // ZY wing front chair , EF exit chair

                                tempex.y:=(loose_pin1.y+loose_pin2.y)/2;  // centre of rectangle
                                twist_slot1:=0-k3/2;                      // equalized chair

                                dotransform(twist_slot1,slot_cl,tempex.y,loose_pin1,loose_pin1);
                                dotransform(twist_slot1,slot_cl,tempex.y,loose_pin2,loose_pin2);
                                dotransform(twist_slot1,slot_cl,tempex.y,loose_pin3,loose_pin3);
                                dotransform(twist_slot1,slot_cl,tempex.y,loose_pin4,loose_pin4);
                              end;

                          22: begin
                                do_nothing;  // XN chair on knuckle,  slot matches skewed chair base
                              end;


                    23,24,25: begin         // XA gap chair,  AA nose chair,  AB splice chair

                                tempex.y:=(loose_pin1.y+loose_pin2.y)/2;  // centre of rectangle
                                twist_slot1:=curvi_k_mod+k3/2;            // equalized chair

                                dotransform(twist_slot1,slot_cl,tempex.y,loose_pin1,loose_pin1);
                                dotransform(twist_slot1,slot_cl,tempex.y,loose_pin2,loose_pin2);
                                dotransform(twist_slot1,slot_cl,tempex.y,loose_pin3,loose_pin3);
                                dotransform(twist_slot1,slot_cl,tempex.y,loose_pin4,loose_pin4);
                              end;

                          26: begin         // BB/BC wing flare chair

                                tempex.y:=(loose_pin1.y+loose_pin2.y)/2;                       // centre of rectangle
                                twist_slot1:=curvi_k_mod+k3/2+wing_end_flares.ts_wing_k;       // turnout-side wing rail

                                dotransform(twist_slot1,slot_cl,tempex.y,loose_pin1,loose_pin1);
                                dotransform(twist_slot1,slot_cl,tempex.y,loose_pin2,loose_pin2);
                                dotransform(twist_slot1,slot_cl,tempex.y,loose_pin3,loose_pin3);
                                dotransform(twist_slot1,slot_cl,tempex.y,loose_pin4,loose_pin4);
                              end;

                      end;//case


                      if chair_code=28  // EF chair   interchange slots 1 and 2     relative to ends of chair base and plug
                         then begin
                                tempex:=loose_pin1; loose_pin1:=loose_pin9;  loose_pin9:=tempex;
                                tempex:=loose_pin2; loose_pin2:=loose_pin10; loose_pin10:=tempex;
                                tempex:=loose_pin3; loose_pin3:=loose_pin11; loose_pin11:=tempex;
                                tempex:=loose_pin4; loose_pin4:=loose_pin12; loose_pin12:=tempex;

                                  // and invert slot rectangles ...

                                tempex:=loose_pin1; loose_pin1:=loose_pin2; loose_pin2:=tempex;
                                tempex:=loose_pin3; loose_pin3:=loose_pin4; loose_pin4:=tempex;

                                tempex:=loose_pin9;  loose_pin9:=loose_pin10;  loose_pin10:=tempex;
                                tempex:=loose_pin11; loose_pin11:=loose_pin12; loose_pin12:=tempex;
                              end;


                        // sheared base rectangle position for angled 1st slot ... 236d   L1 bridge chairs

                      loose_pin6.x:=loose_pin2.x;
                      loose_pin6.y:=loose_pin2.y+slot_shear*side;

                      loose_pin5.x:=loose_pin1.x;
                      loose_pin5.y:=loose_pin1.y+slot_shear*side;

                      loose_pin7.x:=loose_pin3.x;
                      loose_pin7.y:=loose_pin3.y+slot_shear*side;

                      loose_pin8.x:=loose_pin4.x;
                      loose_pin8.y:=loose_pin4.y+slot_shear*side;


                       // sheared base rectangle position for second angled slot ... 237a

                        //  n.b. if slot is twisted, there will be a twisted shift in the angled slot through the plug

                      loose_pin13.x:=loose_pin9.x;
                      loose_pin13.y:=loose_pin9.y-slot_shear*side;

                      loose_pin14.x:=loose_pin10.x;
                      loose_pin14.y:=loose_pin10.y-slot_shear*side;

                      loose_pin15.x:=loose_pin11.x;
                      loose_pin15.y:=loose_pin11.y-slot_shear*side;

                      loose_pin16.x:=loose_pin12.x;
                      loose_pin16.y:=loose_pin12.y-slot_shear*side;


                      
                        // now enter the slot marks ...


                      do_chair_mark(loose_pin1,loose_pin2,0);   // pin slot rectangle first side
                      do_chair_mark(loose_pin3,loose_pin4,0);   // second side

                      do_chair_mark(loose_pin5,loose_pin6,0);   // 236d sheared pin slot rectangle first side
                      do_chair_mark(loose_pin7,loose_pin8,0);   // second side

                      do_chair_mark(loose_pin9,loose_pin10,0);    // 237a second pin slot rectangle (check rail) first side
                      do_chair_mark(loose_pin11,loose_pin12,0);   // second side

                      do_chair_mark(loose_pin13,loose_pin14,0);   // 237a second sheared pin slot rectangle (check rail) first side
                      do_chair_mark(loose_pin15,loose_pin16,0);   // second side


                        // pin slots on trackpad, not used in DXF ...

                      do_chair_mark(loose_pin1,loose_pin2,499);   // 1st pin slot rectangle near side
                      do_chair_mark(loose_pin2,loose_pin3,499);   // 1st pin slot rectangle inner end
                      do_chair_mark(loose_pin3,loose_pin4,499);   // 1st pin slot rectangle far side
                      do_chair_mark(loose_pin4,loose_pin1,499);   // 1st pin slot rectangle outer end

                      case chair_code of

                             5: begin    // L1 bridge chairs, sheared slot base rectangle
                                  do_chair_mark(loose_pin5,loose_pin6,499);   // 1st pin slot base rectangle near side
                                  do_chair_mark(loose_pin6,loose_pin7,499);   // 1st pin slot base rectangle inner end
                                  do_chair_mark(loose_pin7,loose_pin8,499);   // 1st pin slot base rectangle far side
                                  do_chair_mark(loose_pin8,loose_pin5,499);   // 1st pin slot base rectangle outer end
                                end;

                12..16,21..28: begin    // 3P..11P switch chairs, check rail chairs, crossing chairs
                                 do_chair_mark(loose_pin9,loose_pin10,499);    // 2nd pin slot rectangle near side
                                 do_chair_mark(loose_pin10,loose_pin11,499);   // 2nd pin slot rectangle inner end
                                 do_chair_mark(loose_pin11,loose_pin12,499);   // 2nd pin slot rectangle far side
                                 do_chair_mark(loose_pin12,loose_pin9,499);    // 2nd pin slot rectangle outer end
                                end;

                      end;//case


                      //----------------------

                      do_plug_socket;

                      INC(chair_count);

                    end;//with chairing_dims
                  end;
                  //////////////////////////////////////////////////////////////

                  procedure calc_fill_timber_mark(code:integer);

                  var
                    pk1,pk2,ponpad,pp1,pp2:Tpex;

                  begin
                                  // blanking of plain track 229b  (n.b. shoved timbers blanked from unshoved position) ..

                    if check_plain_track_for_blanking=True then EXIT;  // 229a  blanking plain track from CTRL-1

                    dotransform(keq+k_curtimb, xtimbcl, yeq, p1, pk1);       // timber equalising + curving line angle, transform p1 to pk1 (relative to equalising centre).
                    dotransform(keq+k_curtimb, xtimbcl, yeq, p2, pk2);       // ditto p2

                    pk1.x:=pk1.x+x_curmod;      // shift p1 to final position for the curving.
                    pk1.y:=pk1.y+y_curmod;

                    dotransform(kform,xform,yform,pk1,ponpad);  // transform to template position on pad..
                    pp1.x:=ponpad.x+xshift;
                    pp1.y:=ponpad.y+yshift;

                    pk2.x:=pk2.x+x_curmod;      // shift p2 to final position for the curving.
                    pk2.y:=pk2.y+y_curmod;

                    dotransform(kform,xform,yform,pk2,ponpad);     // transform to template position on pad..
                    pp2.x:=ponpad.x+xshift;
                    pp2.y:=ponpad.y+yshift;

                    fill_mark(convert_point(pp1),convert_point(pp2),code,timber_str);  // into marks list.
                  end;
                  //////////////////////////////////////////////////////////////

                  procedure calc_fill_shoved_corners(n:integer);

                  var
                    pk1,pk2,pk3,pk4,ponpad,pp1,pp2,pp3,pp4:Tpex;

                  begin
                    dotransform(keq+k_curtimb, xtimbcl, yeq, shoved_corners_pex.p1, pk1);       // timber equalising + curving line angle, transform p1 to pk1 (relative to equalising centre).
                    dotransform(keq+k_curtimb, xtimbcl, yeq, shoved_corners_pex.p2, pk2);       // ditto p2
                    dotransform(keq+k_curtimb, xtimbcl, yeq, shoved_corners_pex.p3, pk3);       // ditto p3
                    dotransform(keq+k_curtimb, xtimbcl, yeq, shoved_corners_pex.p4, pk4);       // ditto p4

                    pk1.x:=pk1.x+x_curmod;      // shift p1 to final position for the curving.
                    pk1.y:=pk1.y+y_curmod;

                    dotransform(kform,xform,yform,pk1,ponpad);  // transform to template position on pad..
                    pp1.x:=ponpad.x+xshift;
                    pp1.y:=ponpad.y+yshift;


                    pk2.x:=pk2.x+x_curmod;      // shift p2 to final position for the curving.
                    pk2.y:=pk2.y+y_curmod;

                    dotransform(kform,xform,yform,pk2,ponpad);     // transform to template position on pad..
                    pp2.x:=ponpad.x+xshift;
                    pp2.y:=ponpad.y+yshift;


                    pk3.x:=pk3.x+x_curmod;      // shift p3 to final position for the curving.
                    pk3.y:=pk3.y+y_curmod;

                    dotransform(kform,xform,yform,pk3,ponpad);     // transform to template position on pad..
                    pp3.x:=ponpad.x+xshift;
                    pp3.y:=ponpad.y+yshift;


                    pk4.x:=pk4.x+x_curmod;      // shift p4 to final position for the curving.
                    pk4.y:=pk4.y+y_curmod;

                    dotransform(kform,xform,yform,pk4,ponpad);     // transform to template position on pad..
                    pp4.x:=ponpad.x+xshift;
                    pp4.y:=ponpad.y+yshift;

                    with current_shoved_timbers[n].shoved_mod_infill do begin

                      with current_shoved_corners do begin

                        p1:=convert_point(pp1);
                        p2:=convert_point(pp2);
                        p3:=convert_point(pp3);
                        p4:=convert_point(pp4);

                        stored_shoved_corners.p1.X:=p1.X;
                        stored_shoved_corners.p1.Y:=p1.Y+Round(y_datum*100);

                        stored_shoved_corners.p2.X:=p2.X;
                        stored_shoved_corners.p2.Y:=p2.Y+Round(y_datum*100);

                        stored_shoved_corners.p3.X:=p3.X;
                        stored_shoved_corners.p3.Y:=p3.Y+Round(y_datum*100);

                        stored_shoved_corners.p4.X:=p4.X;
                        stored_shoved_corners.p4.Y:=p4.Y+Round(y_datum*100);

                      end;//with

                      shoved_number_str:=timber_str;   // shoved timber number

                    end;//with

                  end;
                  //////////////////////////////////////////////////////////////

                  function get_wing_rail_ends(ms_clx,ts_clx:extended):Twing_end_flares;        // 241a

                      // return offset and angle at chair centre x

                  var
                    wing_end_flares:Twing_end_flares;      // 555a


                  begin
                    with wing_end_flares do begin

                      ms_wing_chair:=0; // init ..
                      ms_wing_add:=0;
                      ms_wing_k:=0;

                      ts_wing_chair:=0; // init ..
                      ts_wing_add:=0;
                      ts_wing_k:=0;

                          // MS ...

                      if ms_clx<=ms_wingx1
                         then ms_wing_chair:=2;     // parallel part of MS wing rail      // 0=beyond wing  2=parallel  3=flare-out

                      if (ms_clx>ms_wingx1) and (ms_clx<=ms_wingx2)
                         then begin
                                ms_wing_k:=k5_mr;
                                ms_wing_add:=(ms_clx-ms_wingx1)*TAN(ms_wing_k);  // MS flare-out amount at chair (wing rail reach end)
                                ms_wing_chair:=3;
                              end;

                          // TS

                      if ts_clx<=ts_wingx1
                         then ts_wing_chair:=2;     // parallel part of TS wing rail      // 0=beyond wing  2=parallel  3=flare-out

                      if (ts_clx>ts_wingx1) and (ts_clx<=ts_wingx2)
                         then begin
                                ts_wing_k:=k5_tr;
                                ts_wing_add:=(ts_clx-ts_wingx1)*TAN(ts_wing_k);  // TS flare-out amount at chair (wing rail reach end)
                                ts_wing_chair:=3;
                              end;

                     end;//with wing_end_flares
                    RESULT:=wing_end_flares;   // 555a
                  end;
                  //////////////////////////////////////////////////////////////

                  function get_check_rail_end(rail:integer; clx:extended):Tcheck_end_flare;        // 237c

                      // return type of check chair if any, and offset at chair centre x

                  begin
                    with RESULT do begin

                      check_chair:=0; // init ..
                      check_add:=0;
                      check_k:=0;

                      if rail=1   // MS
                         then begin
                                if (clx>ms_checkx1) and (clx<=ms_checkx2)        // CCL
                                   then begin
                                          check_chair:=1;

                                          if cpi.ccl_ccr_variable_pi=True
                                             then begin
                                                    check_k:=k5_mw;
                                                    check_add:=(ms_checkx2-clx)*TAN(check_k);  // MS flare-in amount at chair (check rail working end)
                                                  end
                                             else begin
                                                    if ms_ccl_done_clx<>0      // CCL already set
                                                       then begin
                                                              if clx<ms_ccl_done_clx
                                                                 then check_chair:=0   // force this to no check chair
                                                                 else check_chair:=2;  // force this to CC
                                                            end
                                                       else begin
                                                              ms_ccl_done_clx:=clx;
                                                              check_k:=ARCTAN(1/18);
                                                              check_add:=inscale*49/64;     // MS REA 49/64" fixed amount flare-in at chair (check rail working end)
                                                            end;
                                                  end;
                                        end;

                                if (clx>ms_checkx2) and (clx<=ms_checkx3)
                                   then check_chair:=2;

                                if (clx>ms_checkx3) and (clx<=ms_checkx4)        // CCR
                                   then begin
                                          check_chair:=3;

                                          if cpi.ccl_ccr_variable_pi=True
                                             then begin
                                                    check_k:=k5_me;
                                                    check_add:=(clx-ms_checkx3)*TAN(check_k);  // MS flare-out amount at chair (check rail extension end)
                                                  end
                                             else begin
                                                    if ms_ccr_done_clx<>0      // CCR already set
                                                       then begin
                                                              if clx>ms_ccr_done_clx
                                                                 then check_chair:=0   // force to no check chair
                                                                 else check_chair:=2;  // force to CC
                                                            end
                                                       else begin
                                                              ms_ccr_done_clx:=clx;
                                                              check_k:=ARCTAN(1/18);
                                                              check_add:=inscale*49/64;     // MS REA 49/64" fixed amount flare-out at chair (check rail extensiom end)
                                                            end;
                                                  end;

                                        end;
                              end;


                      if rail=4          // TS
                         then begin
                                if (clx>ts_checkx1) and (clx<=ts_checkx2)        // CCR
                                   then begin
                                          check_chair:=1;

                                          if cpi.ccl_ccr_variable_pi=True
                                             then begin
                                                    check_k:=k5_tw;
                                                    check_add:=(ts_checkx2-clx)*TAN(check_k);  // TS flare-in amount at chair (check rail working end)
                                                  end
                                             else begin
                                                    if ts_ccr_done_clx<>0      // CCR already set
                                                       then begin
                                                              if clx<ts_ccr_done_clx
                                                                 then check_chair:=0   // force this to no check chair
                                                                 else check_chair:=2;  // force this to CC
                                                            end
                                                       else begin
                                                              ts_ccr_done_clx:=clx;
                                                              check_k:=ARCTAN(1/18);
                                                              check_add:=inscale*49/64;     // TS REA 49/64" fixed amount flare-in at chair (check rail working end)
                                                            end;
                                                  end;
                                        end;

                                if (clx>ts_checkx2) and (clx<=ts_checkx3)
                                   then check_chair:=2;

                                if (clx>ts_checkx3) and (clx<=ts_checkx4)        // CCL
                                   then begin
                                          check_chair:=3;

                                          if cpi.ccl_ccr_variable_pi=True
                                             then begin
                                                    check_k:=k5_te;
                                                    check_add:=(clx-ts_checkx3)*TAN(check_k);  // TS flare-out amount at chair (check rail extension end)
                                                  end
                                             else begin
                                                    if ts_ccl_done_clx<>0      // CCL already set
                                                       then begin
                                                              if clx>ts_ccl_done_clx
                                                                 then check_chair:=0   // force to no check chair
                                                                 else check_chair:=2;  // force to CC
                                                            end
                                                       else begin
                                                              ts_ccl_done_clx:=clx;
                                                              check_k:=ARCTAN(1/18);
                                                              check_add:=inscale*49/64;     // TS REA 49/64" fixed amount flare-out at chair (check rail extensiom end)
                                                            end;
                                                  end;

                                        end;
                              end;


                     end;//with RESULT
                  end;

                  //////////////////////////////////////////////////////////////

                  procedure calculate_chairs_on_rails(tb_str:string; no_vee_outers:boolean);       // no_vee_outers for CD chairs

                   // calculate chair dims on all 4 rails for this timber ...

                  type
                    Tbbin=record
                            x1,x2,y1,y2:extended;
                          end;

                  var
                    nn,changed_ch:integer;

                    xcl_mod,chair_mod,chair_midx:extended;

                    chair_midx_flare:extended;

                    aa,bb,kk:extended;

                    bbo,bbk,bbx,aqk:extended;

                    save_yret:extended;

                    aq_y:extended;     // for current rail
                    aq_k:extended;     // an angle
                    aq_f:extended;     // check rail flare

                    bbin,bb1,bb2,bb3,bb4,bb5,bb6,bb7,bb8,bb9,bb10,bb11,bb12,bb13,bb14,bb15:Tbbin;

                    chi:integer;      // chair_list_index
                    chi_mod:integer;  // chair_list_index modifier  for 2nd or more rails in chair

                    jaw_skew:extended;  // skew angle for interchangeable loose jaws

                    heaved_plug:integer;     // 556a..
                    jaws_wanted:boolean;

                            //==================================================

                            procedure get_chair_options;   // 238a

                            begin
                              case dxf_code of           // find chair options from chair code      chi is list index       chi_mod 0 or 1 for second rail in chair

                              { S1 }  1: chi:=0;

                               { P }  2: chi:=3;

                              { L1 }  5: chi:=2;

                             { S1J }  7: chi:=1;

                              { SC } 10: chi:=31;    // 244a

                          { 1P..2P } 11: chi:=4+chi_mod;

                         { 3P..11P } 12: chi:=6+chi_mod;

          { CCLR working end flare } 14: chi:=10+chi_mod;

                              { CC } 15: chi:=8+chi_mod;

        { CCLR extension end flare } 16: chi:=10+chi_mod;      // extension end same as working end

                              { ZY } 21: chi:=13+chi_mod;      // in front of knuckle

                              { XN } 22: chi:=15+chi_mod;      // at knuckle

                              { XA } 23: chi:=17+chi_mod;      // in gap

                              { AA } 24: chi:=19+chi_mod;      // nose AA chair

                              { AB } 25: chi:=21+chi_mod;      // splice AB chair

                           { BB/BC } 26: chi:=23+chi_mod;      // wing flare BB/BC

                           { CD/DD } 27: chi:=25+chi_mod;      // vee rails (prototype inside keyed)

                              { EF } 28: chi:=27+chi_mod;      // vee rails exit



                                    else chi:=0;

                              end;//case
                            end;
                            //==================================================

                            function get_current_jaw_options:Tjaw_options;         // 244a

                            var
                              nn:integer;

                            begin
                              SetLength(RESULT,Length(current_jaw_options_set));

                              RESULT:=Copy(current_jaw_options_set);

                                // any heaving over-rides?

                              nn:=find_shove(tb_str,False);
                              if (nn>=0) and (nn<Length(current_shoved_timbers))     // valid existing shoved slot
                                 then begin
                                        with current_shoved_timbers[nn].heave_rail_chairs[rail_code] do begin

                                          if hv_jaws=True
                                             then begin
                                                    get_chair_options;  // get chi  list index

                                                                               // [0] main running rail...

                                                    RESULT[chi][0]:=hv_jaw_options[0][0];     // include inner jaw?
                                                    RESULT[chi][1]:=hv_jaw_options[0][1];     // include outer jaw?

                                                    RESULT[chi][2]:=hv_jaw_options[0][2];     // outer jaw is loose?

                                                    RESULT[chi][3]:=hv_jaw_options[0][3];     // export loose jaw only?
                                                    RESULT[chi][4]:=hv_jaw_options[0][4];     // export rail seat?

                                                    RESULT[chi][5]:=hv_jaw_options[0][5];     // include key on outer jaw?

                                                                //  hv_jaw_options[1..3][ ] other rails in chair nyi 244a
                                                  end;

                                        end;//with
                                      end;
                            end;

                            //==================================================

                            procedure add_jaw(jaw_str:string; inner_jaw:boolean; jaw_bb:Tbbin);

                              // 237c

                            var
                              n:integer;

                              pt1,pt2,p1,p2,pk1,pk2,pc1,pc2,ponpad,pp1,pp2:Tpex;

                              half_insert_span,cot_shift:extended;

                              z_off:extended;  // Z offset for loose jaws

                              jaw_is_loose:boolean;  // 244e

                              current_jaw_options:Tjaw_options;              // 244a  after any heaving applied



                                                            //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

                                                            procedure add_jmod;

                                                            var
                                                              i:integer;

                                                            begin
                                                              SetLength(current_jmods,Length(current_jmods)+1);  // create a new entry in current jmods list

                                                              i:=HIGH(current_jmods);   // index to it

                                                              with current_jmods[i] do begin

                                                                jm_str:=jaw_str;   // jaw ident name (DXF block mame) '...'

                                                                jm_h:=hand_i;

                                                                jm_aq_y:=aq_y;         // for current rail
                                                                jm_aq_k:=aq_k;         // angle
                                                                jm_aq_f:=aq_f;         // check rail flare

                                                              end;//with

                                                            end;
                                                            //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

                            begin
                              current_jaw_options:=Copy(get_current_jaw_options);       // 244a

                              get_chair_options;  // get chi list index      // 239a

                              if (inner_jaw=True) and (current_jaw_options[chi][0]=False) then EXIT;  // inner jaws not wanted

                              if (inner_jaw=True) and (current_jaw_options[chi][2]=True) and (current_jaw_options[chi][3]=True) then EXIT;  // only loose outer jaws wanted

                              if (inner_jaw=False) and (current_jaw_options[chi][1]=False) then EXIT;  // outer jaws not wanted

                                // if exporting loose jaws only, drop Z onto raft..

                              if (inner_jaw=False) and (current_jaw_options[chi][2]=True) and (current_jaw_options[chi][3]=True) // loose, export it

                              and (POS('VN',jaw_str)<>1)  and  (POS('WN',jaw_str)<>1)   // don't drop DX insert on DD chairs  - insert includes seat and support

                                 then begin
                                        if (scale>4.05) //and (dxf_form.thinify_timbers_checkbox.Checked=False)
                                           then z_off:=pyramid_height_pin-pyramid_height_high
                                           else z_off:=pyramid_height_pin-pyramid_height_low;
                                      end
                                 else z_off:=0;

                              jaw_is_loose:=(inner_jaw=False) and (current_jaw_options[chi][2]=True);   // outer jaw is loose

                              if jaw_is_loose=True // (inner_jaw=False) and (current_jaw_options[chi][2]=True)  // outer jaw is loose
                                 then begin
                                              // change to jaw block version with pin ...

                                        case chi of

                                             0: jaw_str:='S1OUTJAL';      // S1

                                             1: jaw_str:='S1OUTJAL';      // S1J joint

                                             2: jaw_str:='L1OUTJAL';      // L1 bridge

                                             3: jaw_str:='PJAWL';          // P slide

                                             4: jaw_str:='SCOUTJAL';      // 1P..2P stock rail

                                             5: do_nothing;               // jaws_loose_pi[5] switch rail customized

                                             6: jaw_str:='SCOUTJAL';      // 3P..11P stock rail

                                             7: do_nothing;               // jaws_loose_pi[7] switch rail customized

                                             8: jaw_str:='SCOUTJAL';      // CC check  running rail

                                             9: jaw_str:='CCOUTJAL';      // CC check  check rail

                                            10: jaw_str:='SCOUTJAL';      // CCL/CCR end check  running rail

                                            11: do_nothing;               // jaws_loose_pi[11] check rail end customized

                                            13: do_nothing;               // ZY rail 3   V-crossing jaws customized ...

                                            14: do_nothing;               // ZY rail 2

                                            15: jaw_str:='SCOUTJAL';      // XN rail 3   knuckle chair

                                            16: jaw_str:='SCOUTJAL';      // XN rail 2   knuckle chair

                                        17..28: do_nothing;               // all V-crossing jaws customized ...


                                              // more nyi ...



                                           else jaw_str:='S1OUTJAL';      // S1

                                        end;//case

                                      end;

                              if POS('...',jaw_str)<>0
                                 then begin
                                        add_jmod;       // customized jaw with/without loose pin

                                        if (POS('WI',jaw_str)=1) or (POS('VI',jaw_str)=1)   // XN knuckle chair inner jaws
                                           then begin
                                                  half_insert_span:=ABS(jaw_bb.x2-jaw_bb.x1)/2;    // skewing of interchangeable jaws, e.g. for XN chair

                                                  pt1.x:=jaw_bb.x1;
                                                  pt1.y:=jaw_bb.y1-half_insert_span*SIN(jaw_skew); // 239a  skew jaw from rail if any ...

                                                  pt2.x:=jaw_bb.x2;
                                                  pt2.y:=jaw_bb.y2+half_insert_span*SIN(jaw_skew);

                                                end
                                           else begin

                                                  pt1.x:=jaw_bb.x1;
                                                  pt1.y:=jaw_bb.y1;

                                                  pt2.x:=jaw_bb.x2;
                                                  pt2.y:=jaw_bb.y2;
                                                end;
                                      end
                                 else begin
                                        half_insert_span:=ABS(jaw_bb.x2-jaw_bb.x1)/2;    // skewing of interchangeable jaws, e.g. for XN chair

                                        pt1.x:=jaw_bb.x1;
                                        pt1.y:=jaw_bb.y1-half_insert_span*SIN(jaw_skew); // 239a  skew jaw from rail if any ...

                                        pt2.x:=jaw_bb.x2;
                                        pt2.y:=jaw_bb.y2+half_insert_span*SIN(jaw_skew);
                                      end;

                              if (dxf_form.cot_radio.Checked=True) and  (jaw_is_loose=True)   // 556 MW
                                 then begin
                                        if chi_mod=1
                                           then cot_shift:=0-g/3         // arbitrary
                                           else cot_shift:=g/3;

                                        pt1.x:=pt1.x+15*inscale;   // shift into clear space..
                                        pt1.y:=pt1.y+cot_shift;

                                        pt2.x:=pt2.x+15*inscale;
                                        pt2.y:=pt2.y+cot_shift;
                                      end;

                              dotransform(chair_k,xtimbcl,0,pt1,pc1);       // pt1 rotate to required angle
                              dotransform(chair_k,xtimbcl,0,pt2,pc2);       // ditto pt2

                              pc1.y:=pc1.y+chair_y;   // to actual rail position
                              pc2.y:=pc2.y+chair_y;

                              dotransform(keq+k_curtimb, xtimbcl, yeq, pc1, pk1);       // timber equalising + curving line angle, transform p1 to pk1 (relative to equalising centre).
                              dotransform(keq+k_curtimb, xtimbcl, yeq, pc2, pk2);       // ditto p2

                              pk1.x:=pk1.x+x_curmod;      // shift p1 to final position for the curving.
                              pk1.y:=pk1.y+y_curmod;

                              dotransform(kform,xform,yform,pk1,ponpad);  // transform to template position on pad..
                              pp1.x:=ponpad.x+xshift;
                              pp1.y:=ponpad.y+yshift+(y_datum*hand_i);

                              pk2.x:=pk2.x+x_curmod;      // shift p2 to final position for the curving.
                              pk2.y:=pk2.y+y_curmod;

                              dotransform(kform,xform,yform,pk2,ponpad);     // transform to template position on pad..
                              pp2.x:=ponpad.x+xshift;
                              pp2.y:=ponpad.y+yshift+(y_datum*hand_i);    // bgnd templates include their y_datum


                              SetLength(current_jaws,Length(current_jaws)+1);  // create a new entry in current jaws list

                              n:=HIGH(current_jaws);   // index to it

                              with current_jaws[n] do begin

                                chcode:=dxf_code;        // type of chair         0=invalid entry
                                rail:=rail_code;         // which rail it is on
                                right_hand:=(hand_i=-1);
                                inner:=inner_jaw;
                                loose_outer:=jaw_is_loose;  // 244e

                                t_str:=tb_str;   // which timber number

                                j_str:=jaw_str;  // jaw ident name (DXF block mame) ...

                                 // bb insertion locs ..

                                x1:=pp1.x;
                                x2:=pp2.x;

                                y1:=pp1.y*hand_i;
                                y2:=pp2.y*hand_i;

                                z_mod:=z_off;

                              end;//with
                            end;
                            //==================================================

                            procedure add_seat(seat_str:string; seat_bb:Tbbin; seat_angle:extended);

                              // 237c

                            var
                              n:integer;
                              g_mod:extended;

                              pt1,pt2,p1,p2,pk1,pk2,pc1,pc2,ponpad,pp1,pp2:Tpex;

                              half_insert_span:extended;

                              z_off:extended;

                              current_jaw_options:Tjaw_options;              // 244a  after any heaving applied

                            begin

                              if cpi.rail_seats_pi=False then EXIT;    // 238a   seats not wanted

                              current_jaw_options:=Copy(get_current_jaw_options);       // 244a

                              get_chair_options;  // get chi list index    238a

                              z_off:=0;  // init

                              if seat_str='DXSEAT'
                                 then begin
                                        if current_jaw_options[chi][1]=False then EXIT;   // DX insert, no jaws to put seat under

                                        if (current_jaw_options[chi][2]=True) and (current_jaw_options[chi][3]=True)   // loose insert, add support
                                           then seat_str:='DXSEATL';
                                      end
                                 else begin
                                        if current_jaw_options[chi][4]=False then EXIT;                                          // seat not wanted under this rail
                                        if (current_jaw_options[chi][2]=True) and (current_jaw_options[chi][3]=True) then EXIT;  // only loose outer jaws wanted,  so no chair base to receive seat
                                      end;

                              half_insert_span:=ABS(seat_bb.x2-seat_bb.x1)/2;

                              pt1.x:=seat_bb.x1;
                              pt1.y:=seat_bb.y1-half_insert_span*SIN(seat_angle); // 239a  skew seat from rail if any     //+g_mod/2;

                              pt2.x:=seat_bb.x2;
                              pt2.y:=seat_bb.y2+half_insert_span*SIN(seat_angle); // 239a  skew seat from rail if any      //+g_mod/2;

                              dotransform(chair_k,xtimbcl,0,pt1,pc1);       // pt1 rotate to required angle
                              dotransform(chair_k,xtimbcl,0,pt2,pc2);       // ditto pt2

                              pc1.y:=pc1.y+chair_y;   // to actual rail position
                              pc2.y:=pc2.y+chair_y;

                              dotransform(keq+k_curtimb, xtimbcl, yeq, pc1, pk1);       // timber equalising + curving line angle, transform p1 to pk1 (relative to equalising centre).
                              dotransform(keq+k_curtimb, xtimbcl, yeq, pc2, pk2);       // ditto p2

                              pk1.x:=pk1.x+x_curmod;      // shift p1 to final position for the curving.
                              pk1.y:=pk1.y+y_curmod;

                              dotransform(kform,xform,yform,pk1,ponpad);  // transform to template position on pad..
                              pp1.x:=ponpad.x+xshift;
                              pp1.y:=ponpad.y+yshift+(y_datum*hand_i);

                              pk2.x:=pk2.x+x_curmod;      // shift p2 to final position for the curving.
                              pk2.y:=pk2.y+y_curmod;

                              dotransform(kform,xform,yform,pk2,ponpad);     // transform to template position on pad..
                              pp2.x:=ponpad.x+xshift;
                              pp2.y:=ponpad.y+yshift+(y_datum*hand_i);    // bgnd templates include their y_datum


                              SetLength(current_seats,Length(current_seats)+1);  // create a new entry in current seats list

                              n:=HIGH(current_seats);   // index to it

                              with current_seats[n] do begin

                                chcode:=dxf_code;        // type of chair         0=invalid entry
                                rail:=rail_code;         // which rail it is on
                                right_hand:=(hand_i=-1);

                                t_str:=tb_str;    // which timber number

                                s_str:=seat_str;  // seat ident name (DXF block mame) ...

                                x1:=pp1.x;
                                x2:=pp2.x;

                                y1:=pp1.y*hand_i;
                                y2:=pp2.y*hand_i;

                              end;//with
                            end;
                            //==================================================

                            procedure add_key(key_str:string; key_bb:Tbbin; key_angle,skew:extended);

                              // 237c

                            var
                              n:integer;
                              dir,offset,cot_shift:extended;

                              pt1,pt2,p1,p2,pk1,pk2,pc1,pc2,ponpad,pp1,pp2:Tpex;

                              half_insert_span:extended;

                              z_off:extended;  // Z offset for loose jaws

                              jaw_is_loose:boolean;  // 244e

                              current_jaw_options:Tjaw_options;              // 244a  after any heaving applied

                            begin

                              if cpi.keys_on_outer_jaws_pi=False then EXIT;   // 238a  keys not wanted

                              if key_wanted[rail_code]=False then EXIT;  // 556 MW  key omitted in chair heaving

                              current_jaw_options:=Copy(get_current_jaw_options);   // 244a

                              get_chair_options;  // get chi list index    238a


                              if current_jaw_options[chi][1]=False then EXIT;  // outer jaws not wanted, so no key either

                              if current_jaw_options[chi][5]=False then EXIT;  // key not wanted on outer jaw  244a

                                      // if exporting loose jaws only, drop Z onto raft..

                              if (current_jaw_options[chi][2]=True) and (current_jaw_options[chi][3]=True)   // loose, export it

                                 then begin
                                        if (scale>4.05) //and (dxf_form.thinify_timbers_checkbox.Checked=False)
                                           then z_off:=pyramid_height_pin-pyramid_height_high
                                           else z_off:=pyramid_height_pin-pyramid_height_low;
                                      end
                                 else z_off:=0;


                              jaw_is_loose:=(current_jaw_options[chi][2]=True);   // outer jaw is loose

                              if (jaw_is_loose=True) {(current_jaw_options[chi][2]=True)} and (dxf_code<>22) then key_str:=key_str+'L';  // loose jaw version of key wanted (no middle pad) except for XN knuckle chair

                              half_insert_span:=ABS(key_bb.x2-key_bb.x1)/2;

                              pt1.x:=key_bb.x1;
                              pt1.y:=key_bb.y1-half_insert_span*SIN(skew); // 239a  skew key from rail if any

                              pt2.x:=key_bb.x2;
                              pt2.y:=key_bb.y2+half_insert_span*SIN(skew); // 239a  skew key from rail if any


                              if key_direction=0   // 233a  random direction or towards joint
                                 then begin
                                        if (key_towards<>0) and (keys_towards_joint=True)  // over-ride for plain track  single line
                                           then dir:=key_towards
                                           else begin
                                                  if Random>0.5
                                                     then dir:=1.0
                                                     else dir:=0-1.0;
                                                end;
                                      end
                                 else dir:=key_direction;   // +/-1.0   forced all  double track

                              if joint_timber<>0
                                 then dir:=joint_timber;    // 233d over-ride key direction for joint timbers

                              if dxf_form._3d_fdm_radiobutton.Checked=True    // 556 MW
                                 then offset:=dir*Random*inscale/2            // max offset for FDM-printed chairs 1/2"
                                 else begin
                                        if chair_key_max_offset<0.25   // central key wanted
                                           then offset:=0
                                           else offset:=dir*((chair_key_max_offset-0.25)*Random+0.25)*inscale;    // offset range 1/4"..max"    555a was 1/2" MW
                                      end;

                              pt1.x:=pt1.x-offset;     // modify insertion point for offset along the rail...
                              pt2.x:=pt2.x-offset;

                              pt1.y:=pt1.y-offset*SIN(key_angle);
                              pt2.y:=pt2.y-offset*SIN(key_angle);

                              if (dxf_form.cot_radio.Checked=True) and  (jaw_is_loose=True)   // 556 MW
                                 then begin
                                        if chi_mod=1
                                           then cot_shift:=0-g/3      // arbitrary
                                           else cot_shift:=g/3;

                                        pt1.x:=pt1.x+15*inscale;   // shift into clear space to follow loose jaw..
                                        pt1.y:=pt1.y+cot_shift;

                                        pt2.x:=pt2.x+15*inscale;
                                        pt2.y:=pt2.y+cot_shift;
                                      end;

                              dotransform(chair_k,xtimbcl,0,pt1,pc1);       // pt1 rotate to required angle
                              dotransform(chair_k,xtimbcl,0,pt2,pc2);       // ditto pt2

                              pc1.y:=pc1.y+chair_y;   // to actual rail position
                              pc2.y:=pc2.y+chair_y;

                              dotransform(keq+k_curtimb, xtimbcl, yeq, pc1, pk1);       // timber equalising + curving line angle, transform p1 to pk1 (relative to equalising centre).
                              dotransform(keq+k_curtimb, xtimbcl, yeq, pc2, pk2);       // ditto p2

                              pk1.x:=pk1.x+x_curmod;      // shift p1 to final position for the curving.
                              pk1.y:=pk1.y+y_curmod;

                              dotransform(kform,xform,yform,pk1,ponpad);  // transform to template position on pad..
                              pp1.x:=ponpad.x+xshift;
                              pp1.y:=ponpad.y+yshift+(y_datum*hand_i);

                              pk2.x:=pk2.x+x_curmod;      // shift p2 to final position for the curving.
                              pk2.y:=pk2.y+y_curmod;

                              dotransform(kform,xform,yform,pk2,ponpad);     // transform to template position on pad..
                              pp2.x:=ponpad.x+xshift;
                              pp2.y:=ponpad.y+yshift+(y_datum*hand_i);    // bgnd templates include their y_datum


                              SetLength(current_keys,Length(current_keys)+1);  // create a new entry in current keys list

                              n:=HIGH(current_keys);   // index to it

                              with current_keys[n] do begin

                                chcode:=dxf_code;           // type of chair         0=invalid entry
                                rail:=rail_code;            // which rail it is on
                                right_hand:=(hand_i=-1);
                                loose_outer:=jaw_is_loose;  // 244e

                                t_str:=tb_str;    // which timber number

                                k_str:=key_str;  // seat ident name (DXF block mame) ...

                                x1:=pp1.x;
                                x2:=pp2.x;

                                y1:=pp1.y*hand_i;
                                y2:=pp2.y*hand_i;

                                z_mod_key:=z_off;

                              end;//with
                            end;
                            //==================================================

                            function get_heaved_chair(hc_code:integer):Tchairing_dims;

                            begin
                              case hc_code of

                                    1: RESULT:=S1_chairing_dims;      // chair_str:='S1';
                                    2: RESULT:=P_chairing_dims;       // chair_str:='P';

                                    3: RESULT:=null_chair;  // chair_str:='L1CC';
                                    4: RESULT:=null_chair;  // chair_str:='none';

                                    5: RESULT:=L1_chairing_dims;      // chair_str:='L1';

                                    6: RESULT:=null_chair;  // chair_str:='M1';

                                    7: RESULT:=S1J_chairing_dims;     // chair_str:='S1J';

                                    8: RESULT:=null_chair;  // chair_str:='S1O';
                                    9: RESULT:=null_chair;  // chair_str:='S1N';

                                    10: RESULT:=SC_chairing_dims;     // chair_str:='SC';

                                    11: RESULT:=PLR1_chairing_dims;   // chair_str:='1P..2P';
                                    12: RESULT:=PLR3_chairing_dims;   // chair_str:='3P..11P';

                                    13: RESULT:=null_chair;  // chair_str:='none';

                                    14: RESULT:=CCW_chairing_dims;    // chair_str:='CCL/R';
                                    15: RESULT:=CC_chairing_dims;     // chair_str:='CC';
                                    16: RESULT:=CCE_chairing_dims;    // chair_str:='CCR/L';

                                    17: RESULT:=null_chair;  // chair_str:='SS';
                                    18: RESULT:=null_chair;  // chair_str:='MS';
                                    19: RESULT:=null_chair;  // chair_str:='L1CCL/R';
                                    20: RESULT:=null_chair;  // chair_str:='L1CCR/L';

                                    21: RESULT:=ZY_chairing_dims;     // chair_str:='ZY';
                                    22: RESULT:=XN_chairing_dims;     // chair_str:='XN';
                                    23: RESULT:=XA_chairing_dims;     // chair_str:='XA';
                                    24: RESULT:=AA_chairing_dims;     // chair_str:='AA';
                                    25: RESULT:=AB_chairing_dims;     // chair_str:='AB';
                                    26: RESULT:=BB_chairing_dims;     // chair_str:='BB/BC';
                                    27: RESULT:=CD_chairing_dims;     // chair_str:='CD/DD';
                                    28: RESULT:=EF_chairing_dims;     // chair_str:='EF';

                                29..39: RESULT:=null_chair;  // chair_str:='none';
                                    40: RESULT:=null_chair;  // chair_str:='SDP';
                                    41: RESULT:=null_chair;  // chair_str:='SDS';
                                42..49: RESULT:=null_chair;  // chair_str:='none';
                                    50: RESULT:=null_chair;  // chair_str:='PW';
                                    51: RESULT:=null_chair;  // chair_str:='PWL';
                                    52: RESULT:=null_chair;  // chair_str:='PWR';
                                53..97: RESULT:=null_chair;  // chair_str:='none';

                                    98: RESULT:=FG_chairing_dims;     // chair_str:='FG'; //dummy chair  rail foot groove
                                    99: RESULT:=RG_chairing_dims;     // chair_str:='RG'; //dummy chair  rail head groove

                                  else RESULT:=null_chair;  // chair_str:='none';

                              end;//case

{

                                    S1_chairing_dims:Tchairing_dims;
                                    S1J_chairing_dims:Tchairing_dims;
                                    L1_chairing_dims:Tchairing_dims;
                                    P_chairing_dims:Tchairing_dims;

                                    SC_chairing_dims:Tchairing_dims;

                                    PLR1_chairing_dims:Tchairing_dims;   // first 2 switch block slide  A..D switches
                                    PLR3_chairing_dims:Tchairing_dims;   // remaining switch block heel chairs 3P..11P

                                    PLR1X_chairing_dims:Tchairing_dims;  // first 2 switch block slide  E and F switches (inside keyed)

                                    CC_chairing_dims:Tchairing_dims;     // check chair parallel part
                                    CCW_chairing_dims:Tchairing_dims;    // check end chair WORKING near end
                                    CCE_chairing_dims:Tchairing_dims;    // check end chair EXTENSION far end

                                    ZY_chairing_dims:Tchairing_dims;     // chair code 21    V-crossing chairs...
                                    XN_chairing_dims:Tchairing_dims;     // chair code 22
                                    XA_chairing_dims:Tchairing_dims;     // chair code 23
                                    AA_chairing_dims:Tchairing_dims;     // chair code 24
                                    AB_chairing_dims:Tchairing_dims;     // chair code 25
                                    BB_chairing_dims:Tchairing_dims;     // chair code 26
                                    CD_chairing_dims:Tchairing_dims;     // chair code 27
                                    EF_chairing_dims:Tchairing_dims;     // chair code 28

                                    FG_chairing_dims:Tchairing_dims;
                                    RG_chairing_dims:Tchairing_dims;
}

                            end;
                            //==================================================

                            procedure do_bit_settings_plug(plug_fit:integer);      // 556a

                             // 0=no change 1=force clip-fit  2=force snap-fit  3=force press-fit  4= force COT (no plug)

                             // encode in 3 bits  MSB ..4,3,2.. LSB

                            begin
                              case plug_fit of
                                   0: begin bit_settings:=bit_settings AND $FFFFFFE3; end;
                                   1: begin bit_settings:=bit_settings AND $FFFFFFE3; bit_settings:=bit_settings OR $00000004; end;
                                   2: begin bit_settings:=bit_settings AND $FFFFFFE3; bit_settings:=bit_settings OR $00000008; end;
                                   3: begin bit_settings:=bit_settings AND $FFFFFFE3; bit_settings:=bit_settings OR $0000000C; end;
                                   4: begin bit_settings:=bit_settings AND $FFFFFFE3; bit_settings:=bit_settings OR $00000010; end;
                              end;//case
                            end;
                            //==================================================



                  begin
                    xcl_mod:=0; // init   235a

                    check_flare_shift:=0;    // init  237c  for check rail flares
                    check_flare_twist:=0;

                    curvi_mod:=0;     // init 239a
                    curvi_k:=0;
                    curvi_k_mod:=0;

                    bb:=4*inscale;   // extent for DXF angle calc  237c

                    save_yret:=yret;  // init 237a

                    with _2d_data do begin

                          // main-road stock-rail...

                          // search RAIL1

                      if {(heave_chairs_form.Visible=True) and} (timber_str=current_shove_str)
                         then heave_chairs_form.rail1_groupbox.Visible:=(chairing_dims1.chair_code<>0);  // no chair exists

                      if (main_road_stock_rail_flag=True) and (chair1_wanted=True) and (chairing_dims1.chair_code<>0)     // 237a

                         then begin
                                rail_code:=1;

                                yret:=save_yret;  //-g_tweak;    // 237a  adjust chairs and socket positions for gauge tweaks

                                xcl_mod:=inscale*keq*6/Pi;  // 1" shift forward at 30 degs (by trial and error) on skewed timbers (zero on square-on timbers)

                                eql:=yeq;       // to rail from equalizing rotation point

                                try
                                  chair_y:=yeq-eql/COS(keq);                        // distance to chair along timber
                                  chair_midx_flare:=xtimbcl+xcl_mod+yeq*TAN(keq);   // for check rail chairs
                                except
                                  chair_y:=yeq;
                                  chair_midx_flare:=xtimbcl+xcl_mod;
                                end;//try

                                check_end_flare1:=get_check_rail_end(rail_code,chair_midx_flare);    // 237c       // get amount of flare at timber

                                check_end_flare:=check_end_flare1;

                                case check_end_flare.check_chair of

                                     1: begin
                                          now_chairing_dims1:=CCW_chairing_dims;     // flare-in

                                          check_flare_shift:=check_end_flare.check_add;
                                          check_flare_twist:=0-check_end_flare.check_k;

                                          with now_chairing_dims1 do chair_inlong:=chair_outlong+(fw+fw_mods)+check_flare_shift+ABS(chair_halfwide*SIN(check_end_flare.check_k));   // increase for effect of skewed jaw
                                        end;

                                     2: begin
                                          now_chairing_dims1:=CC_chairing_dims;       // parallel

                                          check_flare_shift:=0;
                                          check_flare_twist:=0;
                                        end;

                                     3: begin
                                          now_chairing_dims1:=CCE_chairing_dims;    // flare_out

                                          check_flare_shift:=check_end_flare.check_add;
                                          check_flare_twist:=check_end_flare.check_k;

                                          with now_chairing_dims1 do chair_inlong:=chair_outlong+(fw+fw_mods)+check_flare_shift+ABS(chair_halfwide*SIN(check_end_flare.check_k));   // increase for effect of skewed jaw
                                        end;

                                   else begin
                                          now_chairing_dims1:=chairing_dims1;  // not a check chair

                                          check_flare_shift:=0;
                                          check_flare_twist:=0;
                                        end;

                                end;//case


                                chair_k:=0-keq;    // chair angle relative to timber


                                if (heave_chairs_form.Visible=True) and (timber_str=current_shove_str)
                                   then begin
                                          heave_chairs_form.normal_ch_label1.Caption:='normal  chair :';
                                          heave_chairs_form.chair1_type_label.Caption:=get_cclr_chair_str(1,now_chairing_dims1.chair_code);
                                        end;

                                heaved_chairing_dims1.chairing_dims:=now_chairing_dims1;
                                heaved_chairing_dims1.heaved_valid:=False;                 // init

                                do_bit_settings_plug(0);      // 556a   init

                                nn:=find_shove(timber_str,False);
                                if (nn>=0) and (nn<Length(current_shoved_timbers))     // valid existing shoved slot
                                   then begin
                                          with heaved_chairing_dims1 do begin

                                            heave_rail_chair:=current_shoved_timbers[nn].heave_rail_chairs[1];   // 556..
                                            heaved_valid:=True;

                                            do_bit_settings_plug(heave_rail_chair.hv_plug);      // 556a

                                            if heave_rail_chair.hv_ch<>0                                      // changed chair?
                                               then chairing_dims:=get_heaved_chair(heave_rail_chair.hv_ch);

                                            if heave_rail_chair.hv_ch=10    // SC chair
                                               then begin
                                                      with heave_rail_chair do begin

                                                        if hv_sc_outlong<>0 then chairing_dims.chair_outlong:=hv_sc_outlong*inscale;        // custom SC chair dims
                                                        if hv_sc_inlong<>0 then chairing_dims.chair_inlong:=hv_sc_inlong*inscale;           // custom SC chair dims
                                                        if hv_sc_halfwide<>0 then chairing_dims.chair_halfwide:=hv_sc_halfwide*inscale;     // custom SC chair dims

                                                      end;//with
                                                    end;
                                          end;//with
                                        end;

                                with heaved_chairing_dims1.chairing_dims do begin   // chair on rail 1

                                  dxf_code:=chair_code;  //  237a  dxf_code global

                                  chair_mod:=(chair_outlong+chair_inlong)/2-chair_inlong;   // chair_mod = chair middle from gauge-face   (no effect on track gauge)

                                  p1.x:=xtimbcl+xcl_mod-chair_halfwide+crad_outer;
                                  p1.y:=yret-chair_outlong+crad_outer;

                                  p2.x:=xtimbcl+xcl_mod-chair_halfwide+crad_inner;
                                  p2.y:=yret+chair_inlong-crad_inner;

                                  p3.x:=xtimbcl+xcl_mod+chair_halfwide-crad_inner;
                                  p3.y:=p2.y;

                                  p4.x:=xtimbcl+xcl_mod+chair_halfwide-crad_outer;
                                  p4.y:=p1.y;

                                  chair_midx:=(p2.x+p3.x)/2;        // 237c  x to chair middle at rail  for chair base and 2D


                                  b1.x:=xtimbcl+xcl_mod+inbolt_1x;               // bolt centres ...
                                  b1.y:=yret+chair_inlong-bolts_from_end;

                                  b2.x:=xtimbcl+xcl_mod+inbolt_2x;
                                  b2.y:=yret+chair_inlong-bolts_from_end;

                                  b3.x:=xtimbcl+xcl_mod+outbolt_1x;
                                  b3.y:=yret-chair_outlong+bolts_from_end;

                                  b4.x:=xtimbcl+xcl_mod+outbolt_2x;
                                  b4.y:=yret-chair_outlong+bolts_from_end;

                                end;//with heaved_chairing_dims1

                                    // put 2D chair outlines in file ...      // main-road stock-rail

                                if ((timb_str='K') or (timb_str='D')) and (cpi.temp_wrong_k_xing_pi=True)     // 244a temporary for customizing
                                   then begin
                                         chair_twist.x:=chair_midx;       // chair rotation centre
                                         chair_twist.y:=0;                // from gauge-face
                                         chair_twist.k:=hdk/2;            // equalized
                                        end
                                   else begin
                                          chair_twist.x:=0;
                                          chair_twist.y:=0;
                                          chair_twist.k:=0;
                                        end;


                                case dxf_code of
                                        2: calc_fill_chair_outline(chair_twist,heaved_chairing_dims1,497,1);   // P chair list code sets plinth depth..
                                   11..16: calc_fill_chair_outline(chair_twist,heaved_chairing_dims1,485,1);   // switch block chairs  CC chairs check rail
                                        5: calc_fill_chair_outline(chair_twist,heaved_chairing_dims1,493,1);   // L1 chair
                                      else calc_fill_chair_outline(chair_twist,heaved_chairing_dims1,494,1);   // main-road stock-rail  +  plain track MS  S1 chair,  S1J chair
                                end;//case

                                calc_fill_bolts(chair_twist,dxf_code);

                                    // add chair jaws required ... 237c

                                    // main-road stock-rail...  rail 1

                                if heaved_chairing_dims1.heaved_valid=True
                                   then heaved_plug:=heaved_chairing_dims1.heave_rail_chair.hv_plug  // 1=force clip-fit  2=force snap-fit  3=force press-fit  4= force COT (no plug)
                                   else heaved_plug:=0;

                                if dxf_form.cot_bullet_shape.Visible=True                          // COT track
                                   then jaws_wanted:=((heaved_plug=0) or (heaved_plug=4))
                                   else if dxf_form.cot_radio.Checked=True
                                           then jaws_wanted:=(heaved_plug>0) and (heaved_plug<4)
                                           else jaws_wanted:=True;                                 // normal plug track

                                if (exp_chairing=True) and (dxf_form.chairs_combo.ItemIndex<>0) and (jaws_wanted=True)
                                   then begin

                                          aq_y:=aq2offset(chair_midx,aq_k); // for second rail
                                          aq_f:=0;                          // modified later, check flare

                                          bbin.x1:=chair_midx-bb;  // near DXF location mark..
                                          bbin.x2:=chair_midx+bb;  // far

                                          bbin.y1:=yret;  // MS stock rail, no angle..
                                          bbin.y2:=yret;

                                          // for double chairs ...

                                          bbo:=0;   // init...
                                          bbk:=0;
                                          bbx:=0;

                                          case dxf_code of     // seat and key for check rail flared ends...

                                            11..13: bbo:=aq2offset(chair_midx,bbk);  // for switch rail  -  from stock rail

                                                14: begin   // flare-in
                                                      bbo:=check_end_flare.check_add;    // add amount of flare-in at timber
                                                      bbk:=0-check_end_flare.check_k;    // for seat and key insertion

                                                      bbo:=bbo+check_end_flare.check_add*SIN(0-bbk)/2;      // adjust position for skewing    /2 arbitrary
                                                      bbx:=0-check_end_flare.check_add*COS(0-bbk)/2;

                                                      aq_f:=check_end_flare.check_add;   // for chair jaw customizing..
                                                      aq_k:=check_end_flare.check_k;

                                                      bbo:=bbo+fw_mods;  // 239a
                                                    end;

                                                15: bbo:=fw_mods;  // 239a  CC check rail


                                                16: begin     // flare-out
                                                      bbo:=check_end_flare.check_add;  // add amount of flare-out at timber
                                                      bbk:=check_end_flare.check_k;    // for seat and key insertion

                                                      bbo:=bbo+check_end_flare.check_add*SIN(bbk)/2;      // adjust position for skewing    /2 arbitrary
                                                      bbx:=check_end_flare.check_add*COS(bbk)/2;

                                                      aq_f:=check_end_flare.check_add;   // for chair jaw customizing..
                                                      aq_k:=check_end_flare.check_k;

                                                      bbo:=bbo+fw_mods;  // 239a
                                                    end;

                                          end;//case

                                          bb2.x1:=bbin.x1+bbx;
                                          bb2.x2:=bbin.x2+bbx;

                                          bb2.y1:=bbo-bb*TAN(bbk);
                                          bb2.y2:=bbo+bb*TAN(bbk);


                                          chi_mod:=0;  // init
                                          jaw_skew:=0; // init

                                          case dxf_code of

                                          { S1 }  1: begin
                                                       add_jaw('S1OUTJAW', False, bbin);
                                                       add_jaw('S1INJAW',  True,  bbin);
                                                       add_seat('S1SEAT',         bbin,0);
                                                       add_key('KEY',             bbin,0,0);
                                                     end;

                                           { P }  2: begin
                                                       add_jaw('PJAW', False, bbin);
                                                       add_seat('PSEAT',      bbin,0);
                                                     end;

                                          { L1 }  5: begin
                                                       add_jaw('L1OUTJAW', False, bbin);
                                                       add_jaw('L1INJAW',  True,  bbin);
                                                       add_seat('L1SEAT',         bbin,0);
                                                       add_key('KEY',             bbin,0,0);
                                                     end;

                                         { S1J }  7: begin
                                                       add_jaw('S1OUTJAW', False, bbin);
                                                       add_jaw('S1INJAW',  True,  bbin);
                                                       add_seat('S1JSEAT',        bbin,0);
                                                       add_key('KEY',             bbin,0,0);
                                                     end;

                                          { SC } 10: begin                                    // fictional 4-screw
                                                       add_jaw('SCOUTJAW', False, bbin);
                                                       add_jaw('S1INJAW',  True,  bbin);
                                                       add_seat('SCCCSEAT',       bbin,0);
                                                       add_key('KEY',             bbin,0,0);
                                                     end;

                                { 1P..2P }       11: begin
                                                       add_jaw('SCOUTJAW', False, bbin);
                                                       add_jaw('SCINJAW',  True,  bbin);
                                                       add_seat('SCCCSEAT',       bbin,0);
                                                       add_key('KEY',             bbin,0,0);

                                                       chi_mod:=1;
                                                       add_jaw('MM...'+tb_str, False, bb2);
                                                       add_seat('PLR1SEAT',           bb2,0);
                                                     end;

                               { 3P..11P }       12: begin
                                                       add_jaw('SCOUTJAW', False, bbin);
                                                       add_jaw('SCINJAW',  True,  bbin);
                                                       add_seat('SCCCSEAT',       bbin,0);
                                                       add_key('KEY',             bbin,0,0);

                                                       chi_mod:=1;
                                                       add_jaw('MN...'+tb_str, False, bb2);
                                                       add_jaw('MH...'+tb_str, True,  bb2);
                                                       add_seat('PLR3SEAT',           bb2,0);
                                                       add_key('KEY',                 bb2,bbk,0);
                                                     end;

                { CCLR working end flare }       14: begin
                                                       add_jaw('SCOUTJAW', False, bbin);
                                                       add_jaw('CCFIJAW',  True,  bbin);
                                                       add_seat('SCCCSEAT',       bbin,0);
                                                       add_key('KEY',             bbin,0,0);

                                                       chi_mod:=1;
                                                       add_jaw('MA...'+tb_str, True,  bbin);
                                                       add_jaw('MB...'+tb_str, False, bbin);
                                                       add_seat('CCSEATCK',           bb2,0);
                                                       add_key('CCKEY',               bb2,bbk,0);
                                                     end;

                                    { CC }       15: begin
                                                       add_jaw('SCOUTJAW', False, bbin);
                                                       add_jaw('CCFIJAW',  True,  bbin);
                                                       add_seat('SCCCSEAT',       bbin,0);
                                                       add_key('KEY',             bbin,0,0);

                                                       chi_mod:=1;
                                                       add_jaw('FC...'+tb_str, True,  bbin);
                                                       add_jaw('CCOUTJAW', False, bbin);
                                                       add_seat('CCSEATCK',       bb2,0);
                                                       add_key('CCKEY',           bb2,0,0);
                                                     end;

              { CCLR extension end flare }       16: begin
                                                       add_jaw('SCOUTJAW', False, bbin);
                                                       add_jaw('CCFIJAW',  True,  bbin);
                                                       add_seat('SCCCSEAT',       bbin,0);
                                                       add_key('KEY',             bbin,0,0);

                                                       chi_mod:=1;
                                                       add_jaw('MC...'+tb_str, True,  bbin);
                                                       add_jaw('MD...'+tb_str, False, bbin);
                                                       add_seat('CCSEATCK',           bb2,0);
                                                       add_key('CCKEY',               bb2,bbk,0);
                                                     end;




                                          end;//case

                                        end;//if exp chairing

                                //end;//with chairing_dims

                              end;// MS stock rail

                              //pad_form.Panel1.Caption:='ms rail  '+current_shove_str;

                      //-----------

                             // main-road crossing-rail...

                             // search RAIL3

                      if {(heave_chairs_form.Visible=True) and} (timber_str=current_shove_str)
                         then heave_chairs_form.rail3_groupbox.Visible:=(chairing_dims3.chair_code<>0);  // no chair exists

                      if plain_track=True
                         then this_chair_wanted:=(turnout_road_stock_rail_flag=True) and (chair3_wanted=True)       // plain track TS rail
                         else this_chair_wanted:=(main_road_crossing_rail_flag=True) and (chair3_wanted=True);      // closure rail/wing

                      if (this_chair_wanted=True) and (chairing_dims3.chair_code<>0)     // 237a
                         then begin
                                rail_code:=3;

                                yret:=save_yret;  //+g_tweak;    // 237a  adjust chairs and socket positions for gauge tweaks

                                xcl_mod:=0;    // 241a not needed on this rail

                                eql:=yeq-g;                   // to rail from equalizing rotation point
                                try
                                  chair_y:=yeq-eql/COS(keq);  // distance to chair along timber
                                except
                                  chair_y:=yeq;
                                end;//try

                                check_end_flare3:=get_check_rail_end(rail_code,0);    // 237c  // no check rail on this rail

                                check_end_flare:=check_end_flare3;

                                chair_k:=0-keq;    // chair angle relative to timber


                                if (heave_chairs_form.Visible=True) and (timber_str=current_shove_str)
                                   then begin
                                          heave_chairs_form.normal_ch_label3.Caption:='normal  chair :';
                                          heave_chairs_form.chair3_type_label.Caption:=get_cclr_chair_str(3,chairing_dims3.chair_code);
                                        end;

                                heaved_chairing_dims3.chairing_dims:=chairing_dims3;
                                heaved_chairing_dims3.heaved_valid:=False;                 // init

                                do_bit_settings_plug(0);      // 556a   init

                                nn:=find_shove(timber_str,False);
                                if (nn>=0) and (nn<Length(current_shoved_timbers))     // valid existing shoved slot
                                   then begin
                                          with heaved_chairing_dims3 do begin

                                            heave_rail_chair:=current_shoved_timbers[nn].heave_rail_chairs[3];   // 556..
                                            heaved_valid:=True;

                                            do_bit_settings_plug(heave_rail_chair.hv_plug);      // 556a

                                            if heave_rail_chair.hv_ch<>0                                      // changed chair?
                                               then chairing_dims:=get_heaved_chair(heave_rail_chair.hv_ch);

                                            if heave_rail_chair.hv_ch=10    // SC chair
                                               then begin
                                                      with heave_rail_chair do begin

                                                        if hv_sc_outlong<>0 then chairing_dims.chair_outlong:=hv_sc_outlong*inscale;        // custom SC chair dims
                                                        if hv_sc_inlong<>0 then chairing_dims.chair_inlong:=hv_sc_inlong*inscale;           // custom SC chair dims
                                                        if hv_sc_halfwide<>0 then chairing_dims.chair_halfwide:=hv_sc_halfwide*inscale;     // custom SC chair dims

                                                      end;//with
                                                    end;
                                          end;//with
                                        end;

                                with heaved_chairing_dims3.chairing_dims do begin   // chair on rail 3

                                  dxf_code:=chair_code;  //  237a  dxf_code global - in list for DXF exports

                                  chair_mod:=(chair_outlong+chair_inlong)/2-chair_inlong;   // chair_mod = chair middle from gauge-face   (no effect on track gauge)


                                  p1.x:=xtimbcl+xcl_mod-chair_halfwide+crad_outer;
                                  p1.y:=yret+chair_outlong-crad_outer;

                                  p2.x:=xtimbcl+xcl_mod-chair_halfwide+crad_inner;
                                  p2.y:=yret-chair_inlong+crad_inner;

                                  p3.x:=xtimbcl+xcl_mod+chair_halfwide-crad_inner;
                                  p3.y:=p2.y;

                                  p4.x:=xtimbcl+xcl_mod+chair_halfwide-crad_outer;
                                  p4.y:=p1.y;

                                  chair_midx:=(p2.x+p3.x)/2;        // 237c   x to chair middle at rail  for chair base and 2D


                                  b1.x:=xtimbcl+xcl_mod+inbolt_1x;               // bolt centres ...
                                  b1.y:=yret-chair_inlong+bolts_from_end;

                                  b2.x:=xtimbcl+xcl_mod+inbolt_2x;
                                  b2.y:=yret-chair_inlong+bolts_from_end;

                                  b3.x:=xtimbcl+xcl_mod+outbolt_1x;
                                  b3.y:=yret+chair_outlong-bolts_from_end;

                                  b4.x:=xtimbcl+xcl_mod+outbolt_2x;
                                  b4.y:=yret+chair_outlong-bolts_from_end;

                                end;//with

                                    // put 2D chair outlines in file ...

                                if (dxf_code>20) and (dxf_code<29)   //  21..28   ZY, XN, XA, AA, AB, BB, CD, EF crossing chairs
                                   then begin
                                          chair_twist.x:=chair_midx;       // chair rotation centre
                                          chair_twist.y:=0;                // from gauge-face
                                          chair_twist.k:=k3/2;             // equalized
                                        end
                                   else begin
                                          chair_twist.x:=0;
                                          chair_twist.y:=0;
                                          chair_twist.k:=0;
                                        end;

                                case dxf_code of
                                    5: calc_fill_chair_outline(chair_twist,heaved_chairing_dims3,493,-1);   // L1 chair
                            21..26,28: calc_fill_chair_outline(chair_twist,heaved_chairing_dims3,484,-1);   // ZY, ZN, XA, AA, AB, BB, EF crossing chairs
                                  else calc_fill_chair_outline(chair_twist,heaved_chairing_dims3,494,-1);   // main-road crossing-rail S1, S1J and CD/DD  (chair dode 27)
                                end;//case

                                  // (above set curvi_mod, curvi_k_mod for crossing chairs)

                                calc_fill_bolts(chair_twist,dxf_code);

                                    // add chair jaws required ... 237c

                                    // main-road crossing-rail...   rail 3

                                if heaved_chairing_dims3.heaved_valid=True
                                   then heaved_plug:=heaved_chairing_dims3.heave_rail_chair.hv_plug  // 1=force clip-fit  2=force snap-fit  3=force press-fit  4= force COT (no plug)
                                   else heaved_plug:=0;

                                if dxf_form.cot_bullet_shape.Visible=True                          // COT track
                                   then jaws_wanted:=((heaved_plug=0) or (heaved_plug=4))
                                   else if dxf_form.cot_radio.Checked=True
                                           then jaws_wanted:=(heaved_plug>0) and (heaved_plug<4)
                                           else jaws_wanted:=True;                                 // normal plug track

                                if (exp_chairing=True) and (dxf_form.chairs_combo.ItemIndex<>0) and (jaws_wanted=True)
                                   then begin
                                          bbin.x1:=chair_midx-bb;  // near
                                          bbin.x2:=chair_midx+bb;  // far

                                          bbin.y1:=yret;  // rail 3, main-road crossing rail rail
                                          bbin.y2:=yret;

                                            // for double chairs ...

                                          bb1.x1:=bbin.x1+fw*SIN(k3/2);     // for AA nose inner - align with wing inner
                                          bb1.x2:=bbin.x2+fw*SIN(k3/2);

                                          bb1.y1:=bbin.y1;
                                          bb1.y2:=bbin.y2;



                                          get_xtimbcl2;

                                          bbo:=g-aq2offset(xtimbcl2,bbk);     // wing front ZY

                                          bb2.x1:=bbin.x1+bbo*SIN(k3/2);
                                          bb2.x2:=bbin.x2+bbo*SIN(k3/2);

                                          bb2.y1:=bbin.y1-bbo-bb*TAN(bbk);
                                          bb2.y2:=bbin.y2-bbo+bb*TAN(bbk);


                                          bb9.x1:=bb2.x1+fw*SIN(k3/2);     // for AA nose inner - align with wing inner
                                          bb9.x2:=bb2.x2+fw*SIN(k3/2);

                                          bb9.y1:=bb2.y1; //+curvi_mod*COS(curvi_k);
                                          bb9.y2:=bb2.y2; //+curvi_mod*COS(curvi_k);



                                            // for XN knuckle chair rail 3  main rail...

                                          bbo:=xnl_3*SIN(k3/2);   // rail 3

                                          bb3.x1:=bbin.x1-bbo*SIN(k3/2);
                                          bb3.x2:=bbin.x2-bbo*SIN(k3/2);

                                          bb3.y1:=bbin.y1+bbo-bb*TAN(bbk);
                                          bb3.y2:=bbin.y2+bbo+bb*TAN(bbk);

                                          try
                                            bbo:=knuck_rad-SQRT(SQR(knuck_rad)-SQR(xnl_3))-inscale/8;    // follow knuckle bend radius   1/8" arbitrary freedom
                                          except
                                            bbo:=0;
                                          end;//try

                                          bb5.x1:=bbin.x1-bbo*SIN(k3/2);
                                          bb5.x2:=bbin.x2-bbo*SIN(k3/2);

                                          bb5.y1:=bbin.y1+bbo;
                                          bb5.y2:=bbin.y2+bbo;


                                            // for XN knuckle chair rail 2  turnout rail...

                                          bbo:=g-aq2offset(xtimbcl2,bbk)+xnl_2*SIN(k3/2);   // rail 2

                                          bb4.x1:=bbin.x1+bbo*SIN(k3/2);
                                          bb4.x2:=bbin.x2+bbo*SIN(k3/2);

                                          bb4.y1:=bbin.y1-bbo-bb*TAN(bbk);
                                          bb4.y2:=bbin.y2-bbo+bb*TAN(bbk);

                                          try
                                            bbo:=(g-aq2offset(xtimbcl2,bbk))+(knuck_rad-SQRT(SQR(knuck_rad)-SQR(xnl_3))-inscale/8);    // follow knuckle bend radius   1/8" arbitrary freedom
                                          except
                                            bbo:=g-aq2offset(xtimbcl2,bbk);
                                          end;//try

                                          bb6.x1:=bbin.x1+bbo*SIN(k3/2);
                                          bb6.x2:=bbin.x2+bbo*SIN(k3/2);

                                          bb6.y1:=bbin.y1-bbo-bb*TAN(bbk);
                                          bb6.y2:=bbin.y2-bbo+bb*TAN(bbk);


                                            // for XA, AA chairs ...

                                               // rail 3 main rail along wing     assume regular crossing

                                          bbo:=(xtimbcl-wingcx)*SIN(K3/2)*2+fw_tweak_xing;   // 241a

                                          bbo:=bbo+curvi_mod*COS(curvi_k);  // 239b for curviform xing

                                          bb7.x1:=bbin.x1-bbo*TAN(k3/2+curvi_k_mod);                           //-bbo*COS(k3)*SIN(k3/2);
                                          bb7.x2:=bbin.x2-bbo*TAN(k3/2+curvi_k_mod);                           //-bbo*COS(k3)*SIN(k3/2);

                                          bb7.y1:=bbin.y1+bbo-bb*TAN(k3+curvi_k_mod);      // 239b for curviform xing
                                          bb7.y2:=bbin.y2+bbo+bb*TAN(k3+curvi_k_mod);


                                          bb8.x1:=bbin.x1+(fw+fw_tweak_xing)*TAN(k3/2);     // rail 2 wing rail
                                          bb8.x2:=bbin.x2+(fw+fw_tweak_xing)*TAN(k3/2);

                                          bb8.y1:=bbin.y1-(fw+fw_tweak_xing);
                                          bb8.y2:=bbin.y2-(fw+fw_tweak_xing);


                                            // for BB/BC wing flares      241a


                                          bbo:=((xtimbcl-wingcx)*SIN(k3/2)*2+fw_tweak_xing)/COS(k3)+wing_end_flares.ts_wing_add/COS(wing_end_flares.ts_wing_k+k3);

                                          bbo:=bbo+curvi_mod*COS(curvi_k);  // 239b for curviform xing

                                          bbo:=bbo*COS(k3/2); // along chair centre-line

                                          bb10.x1:=bbin.x1-bbo*TAN(k3/2+wing_end_flares.ts_wing_k+curvi_k_mod);
                                          bb10.x2:=bbin.x2-bbo*TAN(k3/2+wing_end_flares.ts_wing_k+curvi_k_mod);

                                          bb10.x1:=bb10.x1+rail_section_data_mm.rail_head_width_mm*SIN(wing_end_flares.ts_wing_k+k3/2);
                                          bb10.x2:=bb10.x2+rail_section_data_mm.rail_head_width_mm*SIN(wing_end_flares.ts_wing_k+k3/2);


                                          bb10.y1:=bbin.y1+bbo-bb*TAN(k3+wing_end_flares.ts_wing_k+curvi_k_mod);
                                          bb10.y2:=bbin.y2+bbo+bb*TAN(k3+wing_end_flares.ts_wing_k+curvi_k_mod);



                                          bb11.x1:=bb10.x1+inscale/3;   // /k3n;  // arbitrary for BB/BC  TS jaw
                                          bb11.x2:=bb10.x2+inscale/3;   // /k3n;

                                          bb11.y1:=bb10.y1;
                                          bb11.y2:=bb10.y2;



                                          bb12.x1:=bb9.x1+inscale*TAN(k3+wing_end_flares.ts_wing_k);  // arbitrary 1" for BB/BC
                                          bb12.x2:=bb9.x2+inscale*TAN(k3+wing_end_flares.ts_wing_k);

                                          bb12.y1:=bb9.y1;
                                          bb12.y2:=bb9.y2;


                                          bb13.x1:=bbin.x1+(fw+fw_tweak_xing)*TAN(k3/2);    // MS wing rail
                                          bb13.x2:=bbin.x2+(fw+fw_tweak_xing)*TAN(k3/2);

                                          try
                                            bb13.y1:=bbin.y1-(fw+fw_tweak_xing+wing_end_flares.ms_wing_add)+bb*TAN(wing_end_flares.ms_wing_k+curvi_k_mod);
                                          except
                                            bb13.y1:=bbin.y1;

                                          end;//try

                                          try
                                            bb13.y2:=bbin.y2-(fw+fw_tweak_xing+wing_end_flares.ms_wing_add)-bb*TAN(wing_end_flares.ms_wing_k+curvi_k_mod);
                                          except
                                            bb13.y2:=bbin.y2;
                                          end;

                                          bb14.x1:=bb13.x1+inscale/k3n;  // arbitrary for BB/BC  MS jaw
                                          bb14.x2:=bb13.x2+inscale/k3n;

                                          bb14.y1:=bb13.y1;
                                          bb14.y2:=bb13.y2;


                                          chi_mod:=0;  // init
                                          jaw_skew:=0; // init

                                          case dxf_code of

                                          { S1 }  1: begin
                                                       add_jaw('S1OUTJAW', False, bbin);
                                                       add_jaw('S1INJAW',  True,  bbin);
                                                       add_seat('S1SEAT',         bbin,0);
                                                       add_key('KEY',             bbin,0,0);
                                                     end;

                                          { L1 }  5: begin
                                                       add_jaw('L1OUTJAW', False, bbin);
                                                       add_jaw('L1INJAW',  True,  bbin);
                                                       add_seat('L1SEAT',         bbin,0);
                                                       add_key('KEY',             bbin,0,0);
                                                     end;

                                         { S1J }  7: begin                                    // on exit track
                                                       add_jaw('S1OUTJAW', False, bbin);
                                                       add_jaw('S1INJAW',  True,  bbin);
                                                       add_seat('S1JSEAT',        bbin,0);
                                                       add_key('KEY',             bbin,0,0);
                                                     end;

                                          { SC } 10: begin                                    // fictional 4-screw
                                                       add_jaw('SCOUTJAW', False, bbin);
                                                       add_jaw('S1INJAW',  True,  bbin);
                                                       add_seat('SCCCSEAT',       bbin,0);
                                                       add_key('KEY',             bbin,0,0);
                                                     end;

                                          { ZY } 21: begin                    // ZY wing front
                                                       aq_y:=0;               // on main rail
                                                       aq_k:=k3/2;            // main rail, jaw shear, chair base equalized
                                                       aq_f:=rail_sep/2;      // between rails

                                                       add_jaw('WA...'+tb_str, False, bbin);
                                                       add_jaw('WH...'+tb_str,  True, bbin);
                                                       add_seat('ZYSEAT',             bbin,0);
                                                       add_key('KEY',                 bbin,0,0);


                                                       chi_mod:=1;
                                                       rail_code:=2;                          // on turnout crossing rail

                                                       add_jaw('VA...'+tb_str, False, bb2);
                                                       add_jaw('VH...'+tb_str,  True, bb2);
                                                       add_seat('ZYSEAT',             bb2,0);
                                                       add_key('KEY',                 bb2,bbk,0);
                                                     end;

                                          { XN } 22: begin                    // XN knuckle
                                                       aq_y:=0;               // on main rail
                                                       aq_k:=0;               // main rail, no jaw shear, chair base equalized

                                                       aq_f:=(rail_sep/2)+(knuck_rad-SQRT(SQR(knuck_rad)-SQR(xnl_3)));  // between rails   follow knuckle bend radius


                                                       jaw_skew:=0-k3/2;
                                                       add_jaw('SCOUTJAW',    False, bb3);   // interchangeable with SCOUTJAW for loose jaws

                                                       jaw_skew:=k3/2;
                                                       add_jaw('WI...'+tb_str, True, bb5);

                                                       add_seat('XNSEAT',            bb3,0-k3/2);
                                                       add_key('KEY',                bb3,0,0-k3/2);


                                                       chi_mod:=1;
                                                       rail_code:=2;                          // on turnout crossing rail

                                                       jaw_skew:=0-k3/2;
                                                       add_jaw('SCOUTJAW',    False, bb4);   // interchangeable with SCOUTJAW for loose jaws

                                                       jaw_skew:=0-k3/2;
                                                       add_jaw('VI...'+tb_str, True, bb6);

                                                       add_seat('XNSEAT',            bb4,0-k3/2);
                                                       add_key('KEY',                bb4,bbk,0-k3/2);
                                                     end;


                                          { XA } 23: begin                    // XA crossing gap
                                                       aq_y:=0;               // on main rail
                                                       aq_k:=0-k3/2;          // main rail, jaw_shear, chair base equalized

                                                       aq_f:=(xtimbcl-wingcx)*SIN(K3)+fw_tweak_xing+(fpx-xtimbcl)*SIN(K3/2);  // inner jaw trim to middle of chair


                                                       jaw_skew:=0;
                                                       add_jaw('WC...'+tb_str,False, bb7);

                                                       jaw_skew:=0;
                                                       add_jaw('WJ...'+tb_str, True, bb7);

                                                       add_seat('XASEAT',            bb7,0);
                                                       add_key('KEY',                bb7,k3,0);


                                                       chi_mod:=1;
                                                       rail_code:=2;                          // on turnout crossing rail

                                                       jaw_skew:=0-k3;
                                                       add_jaw('VC...'+tb_str,False, bb8);

                                                       jaw_skew:=0-k3;
                                                       add_jaw('VJ...'+tb_str, True, bb8);

                                                       add_seat('XASEAT',            bb8,0);
                                                       add_key('KEY',                bb8,0,0);

                                                     end;

                                          { AA } 24: begin                         // AA nose            239b
                                                       aq_y:=0;                    // on main rail
                                                       aq_k:=0-k3/2;               // main rail, jaw_shear, chair base equalized
                                                       aq_f:=(fw+fw_tweak_xing)/2;

                                                       jaw_skew:=0;
                                                       add_jaw('WD...'+tb_str,False, bb7);

                                                       add_seat('AASEAT',            bb7,0);
                                                       add_key('KEY',                bb7,k3,0);


                                                       aq_k:=0;          // main rail, jaw_shear, chair base equalized
                                                       jaw_skew:=0;
                                                       add_jaw('WP...'+tb_str, True, bb1);


                                                       aq_k:=0-k3/2;
                                                       aq_f:=(fw+fw_tweak_xing)/2+curvi_mod*COS(curvi_k);
                                                       jaw_skew:=0;
                                                       add_jaw('WK...'+tb_str, True, bb7);



                                                       chi_mod:=1;
                                                       rail_code:=2;                   // on turnout crossing rail

                                                       aq_k:=0-k3/2;                   // jaw_shear, chair base equalized
                                                       aq_f:=(fw+fw_tweak_xing)/2;

                                                       jaw_skew:=0-k3;
                                                       add_jaw('VD...'+tb_str,False, bb8);

                                                       jaw_skew:=0-k3;
                                                       add_jaw('VK...'+tb_str, True, bb8);

                                                       add_seat('AASEAT',            bb8,0);
                                                       add_key('KEY',                bb8,0,0);


                                                       aq_k:=0;          // main rail, jaw_shear, chair base equalized
                                                       jaw_skew:=0;
                                                       add_jaw('VP...'+tb_str, True, bb9);


                                                       if cpi.nose_clamp_pi=True    // 241a
                                                          then begin

                                                                 chi_mod:=0;
                                                                 rail_code:=3;                   // on main crossing rail

                                                                 xtimbcl:=xtimbcl+15*inscale;

                                                                    // no timber, so need to calc these...

                                                                 yeq:=aq3offset(xtimbcl,dummy_k)/2;
                                                                 docurving(False,True,xtimbcl,yeq,x_curtimb,y_curtimb,k_curtimb,dummy);    // calc curving
                                                                 x_curmod:=x_curtimb-xtimbcl;
                                                                 y_curmod:=y_curtimb-yeq;


                                                                  bbin.x1:=chair_midx-bb+15*inscale;  // near
                                                                  bbin.x2:=chair_midx+bb+15*inscale;  // far

                                                                  bbin.y1:=yret;  // rail 3, main-road crossing rail rail
                                                                  bbin.y2:=yret;

                                                                  bb1.x1:=bbin.x1+fw*SIN(k3/2);     // for nose inner - align with wing inner
                                                                  bb1.x2:=bbin.x2+fw*SIN(k3/2);

                                                                  bb1.y1:=bbin.y1;
                                                                  bb1.y2:=bbin.y2;


                                                                  get_xtimbcl2;

                                                                  bbo:=g-aq2offset(xtimbcl2,bbk);     // wing front

                                                                  bb2.x1:=bbin.x1+bbo*SIN(k3/2);
                                                                  bb2.x2:=bbin.x2+bbo*SIN(k3/2);

                                                                  bb2.y1:=bbin.y1-bbo-bb*TAN(bbk);
                                                                  bb2.y2:=bbin.y2-bbo+bb*TAN(bbk);


                                                                  bb9.x1:=bb2.x1+fw*SIN(k3/2);     // for AA nose inner - align with wing inner
                                                                  bb9.x2:=bb2.x2+fw*SIN(k3/2);

                                                                  bb9.y1:=bb2.y1; //+curvi_mod*COS(curvi_k);
                                                                  bb9.y2:=bb2.y2; //+curvi_mod*COS(curvi_k);



                                                                  bbo:=(xtimbcl-wingcx)*SIN(K3)+fw_tweak_xing;

                                                                  bbo:=bbo+curvi_mod*COS(curvi_k);  // 239b for curviform xing

                                                                  bb7.x1:=bbin.x1-bbo*TAN(k3/2+curvi_k_mod);                           //-bbo*COS(k3)*SIN(k3/2);
                                                                  bb7.x2:=bbin.x2-bbo*TAN(k3/2+curvi_k_mod);                           //-bbo*COS(k3)*SIN(k3/2);

                                                                  bb7.y1:=bbin.y1+bbo-bb*TAN(k3+curvi_k_mod);      // 239b for curviform xing
                                                                  bb7.y2:=bbin.y2+bbo+bb*TAN(k3+curvi_k_mod);


                                                                  bb8.x1:=bbin.x1+(fw+fw_tweak_xing)*TAN(k3/2);     // rail 2 wing rail
                                                                  bb8.x2:=bbin.x2+(fw+fw_tweak_xing)*TAN(k3/2);

                                                                  bb8.y1:=bbin.y1-(fw+fw_tweak_xing);
                                                                  bb8.y2:=bbin.y2-(fw+fw_tweak_xing);


                                                                 aq_y:=0;                    // on main rail
                                                                 aq_k:=0-k3/2;               // main rail
                                                                 aq_f:=(fw+fw_tweak_xing)/2;

                                                                 jaw_skew:=0;
                                                                 add_seat('NCSEAT',            bb7,0);         // nose clamp

                                                                 aq_k:=0;
                                                                 add_jaw('NP...'+tb_str, True, bb1);

                                                                 aq_k:=0-k3/2;
                                                                 aq_f:=(fw+fw_tweak_xing)/2+curvi_mod*COS(curvi_k);
                                                                 jaw_skew:=0;
                                                                 add_jaw('NK...'+tb_str, True, bb7);



                                                                 chi_mod:=1;
                                                                 rail_code:=2;                   // on turnout crossing rail

                                                                 aq_k:=0-k3/2;                   // jaw_shear, chair base equalized
                                                                 aq_f:=(fw+fw_tweak_xing)/2;

                                                                 jaw_skew:=0-k3;
                                                                 add_jaw('OK...'+tb_str, True, bb8);

                                                                 add_seat('NCSEAT',            bb8,0);          // nose clamp

                                                                 aq_k:=0;          // main rail, jaw_shear, chair base equalized
                                                                 jaw_skew:=0;
                                                                 add_jaw('OP...'+tb_str, True, bb9);

                                                                 xtimbcl:=xtimbcl-15*inscale;    // reset...
                                                                 get_xtimbcl2;

                                                                 yeq:=aq3offset(xtimbcl,dummy_k)/2;
                                                                 docurving(False,True,xtimbcl,yeq,x_curtimb,y_curtimb,k_curtimb,dummy);    // restore curving calcs
                                                                 x_curmod:=x_curtimb-xtimbcl;
                                                                 y_curmod:=y_curtimb-yeq;

                                                               end;

                                                       if cpi.xing_filler_pi=True       // add 3D xing gap spacer
                                                          then begin

                                                                 chi_mod:=0;
                                                                 rail_code:=3;                   // on main crossing rail

                                                                 xtimbcl:=xtimbcl-15*inscale;

                                                                    // no timber here, so need to calc these...

                                                                 yeq:=aq3offset(xtimbcl,dummy_k)/2;
                                                                 docurving(False,True,xtimbcl,yeq,x_curtimb,y_curtimb,k_curtimb,dummy);    // calc curving
                                                                 x_curmod:=x_curtimb-xtimbcl;
                                                                 y_curmod:=y_curtimb-yeq;

                                                                 get_xtimbcl2;

                                                                 curvi_mod:=aq2offset(xtimbcl2,curvi_k)-g-(xtimbcl-fpx)*TAN(k3);    // diff offset regular to curviform/generic

                                                                 curvi_k_mod:=curvi_k-k3;  // diff angle regular to curviform/generic
                                                                 

                                                                 bbin.x1:=chair_midx-bb-15*inscale;  // near
                                                                 bbin.x2:=chair_midx+bb-15*inscale;  // far

                                                                 bbin.y1:=yret;  // rail 3, main-road crossing rail rail
                                                                 bbin.y2:=yret;

                                                                 bbo:=(xtimbcl-wingcx)*SIN(K3/2)*2+fw_tweak_xing;   // 241a

                                                                 bbo:=bbo+curvi_mod*COS(curvi_k);  // 239b for curviform xing

                                                                 bb7.x1:=bbin.x1-bbo*TAN(k3/2+curvi_k_mod);
                                                                 bb7.x2:=bbin.x2-bbo*TAN(k3/2+curvi_k_mod);

                                                                 bb7.y1:=bbin.y1+bbo-bb*TAN(k3+curvi_k_mod);      // 239b for curviform xing
                                                                 bb7.y2:=bbin.y2+bbo+bb*TAN(k3+curvi_k_mod);


                                                                 bb8.x1:=bbin.x1+(fw+fw_tweak_xing)*TAN(k3/2);     // rail 2 wing rail
                                                                 bb8.x2:=bbin.x2+(fw+fw_tweak_xing)*TAN(k3/2);

                                                                 bb8.y1:=bbin.y1-(fw+fw_tweak_xing);
                                                                 bb8.y2:=bbin.y2-(fw+fw_tweak_xing);

                                                                 
                                                                 aq_k:=0-k3/2;
                                                                 aq_f:=(fw+fw_tweak_xing)+(bn_wide-8/k3n/2)*inscale+curvi_mod*COS(curvi_k);  // overlap to middle  8" nose to spacer
                                                                 jaw_skew:=0;
                                                                 add_jaw('XK...'+tb_str, True, bb7);

                                                                 chi_mod:=1;
                                                                 rail_code:=2;                   // on turnout crossing rail

                                                                 aq_k:=0-k3/2;                                         // jaw_shear, chair base equalized
                                                                 aq_f:=(fw+fw_tweak_xing)+(bn_wide-8/k3n/2)*inscale;   // overlap to middle   8" nose to spacer
                                                                 jaw_skew:=0-k3;
                                                                 add_jaw('YK...'+tb_str, True, bb8);

                                                                 add_seat('NXSEAT',            bb8,0);

                                                                 xtimbcl:=xtimbcl+15*inscale;    // reset calcs...

                                                                 get_xtimbcl2;

                                                                 yeq:=aq3offset(xtimbcl,dummy_k)/2;
                                                                 docurving(False,True,xtimbcl,yeq,x_curtimb,y_curtimb,k_curtimb,dummy);    // restore curving calcs
                                                                 x_curmod:=x_curtimb-xtimbcl;
                                                                 y_curmod:=y_curtimb-yeq;
                                                               end;

                                                     end;


                                          { AB } 25: begin                    // AB splice            239b

                                                       aq_y:=0;                    // on main rail
                                                       aq_k:=0-k3/2;               // main rail, jaw_shear, chair base equalized
                                                       aq_f:=(fw+fw_tweak_xing)/2;

                                                       jaw_skew:=0;
                                                       add_jaw('WE...'+tb_str,False, bb7);

                                                       add_seat('ABSEAT',            bb7,0);
                                                       add_key('KEY',                bb7,k3,0);


                                                       aq_k:=0;          // main rail, jaw_shear, chair base equalized
                                                       jaw_skew:=0;
                                                       add_jaw('WQ...'+tb_str, True, bb1);


                                                       aq_k:=0-k3/2;
                                                       aq_f:=(fw+fw_tweak_xing)/2+curvi_mod*COS(curvi_k);
                                                       jaw_skew:=0;
                                                       add_jaw('WL...'+tb_str, True, bb7);



                                                       chi_mod:=1;
                                                       rail_code:=2;                   // on turnout crossing rail

                                                       aq_k:=0-k3/2;                   // jaw_shear, chair base equalized
                                                       aq_f:=(fw+fw_tweak_xing)/2;

                                                       jaw_skew:=0-k3;
                                                       add_jaw('VE...'+tb_str,False, bb8);

                                                       jaw_skew:=0-k3;
                                                       add_jaw('VL...'+tb_str, True, bb8);

                                                       add_seat('ABSEAT',            bb8,0);
                                                       add_key('KEY',                bb8,0,0);


                                                       aq_k:=0;          // main rail, jaw_shear, chair base equalized
                                                       jaw_skew:=0;
                                                       add_jaw('VQ...'+tb_str, True, bb9);

                                                     end;





                                       { BB/BC } 26: begin                    // BB/BC wing flares  241a

                                                       aq_y:=0;                                                  // on main rail
                                                       aq_k:=0-k3/2-wing_end_flares.ts_wing_k;                   // main rail, jaw_shear, chair base equalized
                                                       aq_f:=(fw+fw_tweak_xing)/2+wing_end_flares.ts_wing_add;   // TS wing rail

                                                       jaw_skew:=0;
                                                       add_jaw('WF...'+tb_str,False, bb11);

                                                       if k3n<10.01
                                                          then add_seat('ABSEAT',            bb10,0)
                                                          else add_seat('BCSEAT',            bb10,0);

                                                       add_key('KEY',                bb10,k3+wing_end_flares.ts_wing_k,0);


                                                       aq_k:=0;          // main point rail
                                                       aq_f:=(fw+fw_tweak_xing)/2+wing_end_flares.ms_wing_add;
                                                       jaw_skew:=0;
                                                       add_jaw('WR...'+tb_str, True, bb1);



                                                       aq_k:=0-k3/2;
                                                       aq_f:=(fw+fw_tweak_xing)/2+curvi_mod*COS(curvi_k)+wing_end_flares.ts_wing_add;
                                                       jaw_skew:=0;
                                                       add_jaw('WM...'+tb_str, True, bb11);



                                                       chi_mod:=1;
                                                       rail_code:=2;                   // on turnout crossing rail

                                                       aq_k:=0-k3/2;                   // jaw_shear, chair base equalized
                                                       aq_f:=(fw+fw_tweak_xing)/2+wing_end_flares.ms_wing_add;

                                                       jaw_skew:=0-k3;
                                                       add_jaw('VM...'+tb_str, True, bb13);

                                                       if k3n<10.01
                                                          then add_seat('ABSEAT',            bb13,0)
                                                          else add_seat('BCSEAT',            bb13,0);
                                                          
                                                       add_key('KEY',                bb13,0,0);


                                                       aq_k:=0-k3/2-wing_end_flares.ms_wing_k;
                                                       jaw_skew:=0-k3;
                                                       add_jaw('VF...'+tb_str,False, bb14);



                                                       

                                                       aq_k:=0;                                                 // main rail, jaw_shear, chair base equalized
                                                       aq_f:=(fw+fw_tweak_xing)/2+wing_end_flares.ts_wing_add;
                                                       jaw_skew:=0;
                                                       add_jaw('VR...'+tb_str, True, bb12);






                                                     end;


                                       { CD/DD } 27: begin                    // CD/DD vee rails beyond wing
                                                       aq_y:=0;               // on main rail
                                                       aq_k:=k3/2;            // main rail, jaw shear, chair base equalized
                                                       jaw_skew:=0;

                                                       aq_f:=rail_sep/2-rail_section_data_mm.rail_head_width_mm+inscale/4;       // 1/4" overlap merge

                                                       add_jaw('WG...'+tb_str,  True, bbin);

                                                       if no_vee_outers=True                     // CD, no middle DX insert
                                                          then add_seat('ZYSEAT',     bbin,0)
                                                          else begin
                                                                 add_jaw('WN...'+tb_str, False, bbin);    // DX insert on DD chair
                                                                 add_seat('DDSEAT',     bbin,0);
                                                                 add_seat('DXSEAT',     bbin,0);
                                                               end;


                                                       chi_mod:=0;                       // no separate chair options, as main rail
                                                       rail_code:=2;                     // on turnout crossing rail

                                                       add_jaw('VG...'+tb_str,  True, bb2);

                                                       if no_vee_outers=True
                                                          then add_seat('ZYSEAT',     bb2,0)    // CD, no middle DX insert
                                                          else begin
                                                                 add_jaw('VN...'+tb_str, False, bb2);  // DX insert on DD chair
                                                                 add_seat('DDSEAT',     bb2,0);
                                                                 add_seat('DXSEAT',     bb2,0);
                                                               end;

                                                     end;











                                          { EF } 28: begin                    // EF exit
                                                       aq_y:=0;               // on main rail
                                                       aq_k:=k3/2;            // main rail, jaw shear, chair base equalized

                                                       aq_f:=(xtimbcl-fpx)*SIN(k3/2);   // trimmed in DXF to leave flexing space

                                                       add_jaw('WO...'+tb_str, False, bbin);
                                                       add_jaw('WZ...'+tb_str,  True, bbin);
                                                       add_seat('ZYSEAT',             bbin,0);
                                                       add_key('KEY',                 bbin,0,0);


                                                       chi_mod:=1;
                                                       rail_code:=2;                          // on turnout crossing rail

                                                       add_jaw('VO...'+tb_str, False, bb2);
                                                       add_jaw('VZ...'+tb_str,  True, bb2);
                                                       add_seat('ZYSEAT',             bb2,0);
                                                       add_key('KEY',                 bb2,bbk,0);
                                                     end;


                                          end;//case

                                        end;//if exp chairing

                                //end;// with chairing dims

                              end;// main-road crossing rail

                      //----------

                             // turnout-road crossing-rail...

                             // search RAIL2

                      if {(heave_chairs_form.Visible=True) and} (timber_str=current_shove_str)
                         then heave_chairs_form.rail2_groupbox.Visible:=(chairing_dims2.chair_code<>0);  // no chair exists

                      this_chair_wanted:=(turnout_road_crossing_rail_flag=True) and (chair2_wanted=True);    // closure rail/wing

                      if (this_chair_wanted=True) and (chairing_dims2.chair_code<>0)     // 237a
                         then begin
                                rail_code:=2;

                                yret:=save_yret;  //-g_tweak;    // 237a  adjust chairs and socket positions for gauge tweaks

                                xcl_mod:=0;    // not needed on this rail

                                             // turnout-road crossing-rail chairs...

                                eql:=yeq-aq2offset(xtimbcl,chair_k);   // init to rail from equalizing rotation point

                                     // find intersection of timber centre with rail ...

                                try
                                  eql:=yeq-aq2offset(xtimbcl+eql*TAN(keq),chair_k);   // repeat for greater accuracy...

                                  eql:=yeq-aq2offset(xtimbcl+eql*TAN(keq),chair_k);   // and again
                                  eql:=yeq-aq2offset(xtimbcl+eql*TAN(keq),chair_k);   // and again
                                  eql:=yeq-aq2offset(xtimbcl+eql*TAN(keq),chair_k);   // and again
                                  eql:=yeq-aq2offset(xtimbcl+eql*TAN(keq),chair_k);   // and again

                                  chair_y:=yeq-eql/COS(keq);  // distance to chair along timber
                                except
                                   chair_y:=yeq;
                                end;//try

                                check_end_flare2:=get_check_rail_end(rail_code,0);    // 237c  // no check rail on this rail

                                check_end_flare:=check_end_flare2;

                                chair_k:=chair_k-keq;    // chair angle relative to timber

                                //normal_code2:=chairing_dims2.chair_code;      // 244a for normal chair list

                                if (heave_chairs_form.Visible=True) and (timber_str=current_shove_str)
                                   then begin
                                          heave_chairs_form.normal_ch_label2.Caption:='normal  chair :';
                                          heave_chairs_form.chair2_type_label.Caption:=get_cclr_chair_str(2,chairing_dims2.chair_code);
                                        end;

                                heaved_chairing_dims2.chairing_dims:=chairing_dims2;
                                heaved_chairing_dims2.heaved_valid:=False;                 // init

                                do_bit_settings_plug(0);      // 556a   init

                                nn:=find_shove(timber_str,False);
                                if (nn>=0) and (nn<Length(current_shoved_timbers))     // valid existing shoved slot
                                   then begin
                                          with heaved_chairing_dims2 do begin

                                            heave_rail_chair:=current_shoved_timbers[nn].heave_rail_chairs[2];   // 556..
                                            heaved_valid:=True;

                                            do_bit_settings_plug(heave_rail_chair.hv_plug);      // 556a

                                            if heave_rail_chair.hv_ch<>0                                      // changed chair?
                                               then chairing_dims:=get_heaved_chair(heave_rail_chair.hv_ch);

                                            if heave_rail_chair.hv_ch=10    // SC chair
                                               then begin
                                                      with heave_rail_chair do begin

                                                        if hv_sc_outlong<>0 then chairing_dims.chair_outlong:=hv_sc_outlong*inscale;        // custom SC chair dims
                                                        if hv_sc_inlong<>0 then chairing_dims.chair_inlong:=hv_sc_inlong*inscale;           // custom SC chair dims
                                                        if hv_sc_halfwide<>0 then chairing_dims.chair_halfwide:=hv_sc_halfwide*inscale;     // custom SC chair dims

                                                      end;//with
                                                    end;
                                          end;//with
                                        end;

                                with heaved_chairing_dims2.chairing_dims do begin   // chair on rail 2

                                  dxf_code:=chair_code;  //  237a  dxf_code global - in list for DXF exports

                                  chair_mod:=(chair_outlong+chair_inlong)/2-chair_inlong;   // chair_mod = chair middle from gauge-face   (no effect on track gauge)

                                  p1.x:=xtimbcl-xcl_mod-chair_halfwide-chair_mod*SIN(chair_k)+crad_outer;   // create chair rectangle at y=0 ...
                                  p1.y:=yret-chair_outlong+crad_outer;

                                  p2.x:=xtimbcl-xcl_mod-chair_halfwide-chair_mod*SIN(chair_k)+crad_inner;
                                  p2.y:=yret+chair_inlong-crad_inner;

                                  p3.x:=xtimbcl-xcl_mod+chair_halfwide-chair_mod*SIN(chair_k)-crad_inner;
                                  p3.y:=p2.y;

                                  p4.x:=xtimbcl-xcl_mod+chair_halfwide-chair_mod*SIN(chair_k)-crad_outer;
                                  p4.y:=p1.y;

                                  chair_midx:=(p2.x+p3.x)/2;        // 237c   x to chair middle at rail  for chair base and 2D


                                  b1.x:=xtimbcl-xcl_mod+inbolt_1x-chair_mod*SIN(chair_k);               // bolt centres ...
                                  b1.y:=yret+chair_inlong-bolts_from_end;

                                  b2.x:=xtimbcl-xcl_mod+inbolt_2x-chair_mod*SIN(chair_k);
                                  b2.y:=yret+chair_inlong-bolts_from_end;

                                  b3.x:=xtimbcl-xcl_mod+outbolt_1x-chair_mod*SIN(chair_k);
                                  b3.y:=yret-chair_outlong+bolts_from_end;

                                  b4.x:=xtimbcl-xcl_mod+outbolt_2x-chair_mod*SIN(chair_k);
                                  b4.y:=yret-chair_outlong+bolts_from_end;

                                end;//with

                                    // put 2D chair outlines in file ...


                                chair_twist.x:=0;
                                chair_twist.y:=0;
                                chair_twist.k:=0;

                                case dxf_code of
                                    5: calc_fill_chair_outline(chair_twist,heaved_chairing_dims2,493,1);     // L1 chair
                                  else calc_fill_chair_outline(chair_twist,heaved_chairing_dims2,494,1);     // turnout-road crossing-rail
                                end;//case

                                calc_fill_bolts(chair_twist,dxf_code);

                                    // add chair jaws ...

                                    // turnout-road crossing-rail...   rail 2

                                if heaved_chairing_dims2.heaved_valid=True
                                   then heaved_plug:=heaved_chairing_dims2.heave_rail_chair.hv_plug  // 1=force clip-fit  2=force snap-fit  3=force press-fit  4= force COT (no plug)
                                   else heaved_plug:=0;

                                if dxf_form.cot_bullet_shape.Visible=True                          // COT track
                                   then jaws_wanted:=((heaved_plug=0) or (heaved_plug=4))
                                   else if dxf_form.cot_radio.Checked=True
                                           then jaws_wanted:=(heaved_plug>0) and (heaved_plug<4)
                                           else jaws_wanted:=True;                                 // normal plug track

                                if (exp_chairing=True) and (dxf_form.chairs_combo.ItemIndex<>0) and (jaws_wanted=True)
                                   then begin
                                          aq_y:=aq2offset(chair_midx,aq_k); // for rail 2
                                          aq_f:=0;                          // modified later, wing rail flare

                                          bbin.x1:=chair_midx-bb;  // near DXF location mark..
                                          bbin.x2:=chair_midx+bb;  // far

                                          bbin.y1:=yret;  // rail 2
                                          bbin.y2:=yret;

                                          chi_mod:=0;  // init
                                          jaw_skew:=0; // init

                                          case dxf_code of

                                          { S1 }  1: begin
                                                       add_jaw('S1OUTJAW', False, bbin);
                                                       add_jaw('S1INJAW',  True,  bbin);
                                                       add_seat('S1SEAT',         bbin,0);
                                                       add_key('KEY',             bbin,0,0);
                                                     end;

                                          { L1 }  5: begin
                                                       add_jaw('L1OUTJAW', False, bbin);
                                                       add_jaw('L1INJAW',  True,  bbin);
                                                       add_seat('L1SEAT',         bbin,0);
                                                       add_key('KEY',             bbin,0,0);
                                                     end;

                                         { S1J }  7: begin                                    // on exit track
                                                       add_jaw('S1OUTJAW', False, bbin);
                                                       add_jaw('S1INJAW',  True,  bbin);
                                                       add_seat('S1JSEAT',        bbin,0);
                                                       add_key('KEY',             bbin,0,0);
                                                     end;

                                          { SC } 10: begin                                    // fictional 4-screw
                                                       add_jaw('SCOUTJAW', False, bbin);
                                                       add_jaw('S1INJAW',  True,  bbin);
                                                       add_seat('SCCCSEAT',       bbin,0);
                                                       add_key('KEY',             bbin,0,0);
                                                     end;


                                          end;//case

                                        end;//if exp chairing



                                //end;//with chairing dims

                              end;// rail

                      //--------------

                             // turnout-road stock-rail...

                             // search RAIL4

                      if {(heave_chairs_form.Visible=True) and} (timber_str=current_shove_str)
                         then heave_chairs_form.rail4_groupbox.Visible:=(chairing_dims4.chair_code<>0);    // no chair exists

                      this_chair_wanted:=chair4_wanted;

                      if (turnout_road_stock_rail_flag=True) and (this_chair_wanted=True) and (chairing_dims4.chair_code<>0)     // 237a
                         then begin
                                rail_code:=4;

                                yret:=save_yret;  //+g_tweak;    // 237a  adjust chairs and socket positions for gauge tweaks

                                xcl_mod:=0;    // not needed on this rail

                                eql:=yeq-aq3offset(xtimbcl,chair_k);   // init   to rail from equalizing rotation point

                                   // find intersection of timber centre with rail ...

                                try
                                  eql:=yeq-aq3offset(xtimbcl+eql*TAN(keq),chair_k);   // repeat

                                  eql:=yeq-aq3offset(xtimbcl+eql*TAN(keq),chair_k);   // and again
                                  eql:=yeq-aq3offset(xtimbcl+eql*TAN(keq),chair_k);   // and again
                                  eql:=yeq-aq3offset(xtimbcl+eql*TAN(keq),chair_k);   // and again
                                  eql:=yeq-aq3offset(xtimbcl+eql*TAN(keq),chair_k);   // and again

                                  chair_y:=yeq-eql/COS(keq);  // distance to chair along timber

                                  chair_midx_flare:=xtimbcl+xcl_mod+yeq*TAN(keq);   // for check rail chairs
                                except
                                  chair_y:=yeq;
                                  chair_midx_flare:=xtimbcl+xcl_mod;
                                end;//try

                                check_end_flare4:=get_check_rail_end(rail_code,chair_midx_flare);    // 237c

                                check_end_flare:=check_end_flare4;

                                case check_end_flare.check_chair of

                                     1: begin
                                          now_chairing_dims4:=CCW_chairing_dims;     // flare-in

                                          with now_chairing_dims4 do chair_inlong:=chair_outlong+(fw+fw_mods)+check_end_flare.check_add+ABS(chair_halfwide*SIN(check_end_flare.check_k));   // increase for effect of skewed jaw

                                        end;

                                     2: now_chairing_dims4:=CC_chairing_dims;       // parallel

                                     3: begin
                                          now_chairing_dims4:=CCE_chairing_dims;    // flare_out

                                          with now_chairing_dims4 do chair_inlong:=chair_outlong+(fw+fw_mods)+check_end_flare.check_add+ABS(chair_halfwide*SIN(check_end_flare.check_k));   // increase for effect of skewed jaw
                                        end;

                                   else now_chairing_dims4:=chairing_dims4;  // not a check chair

                                end;//case

                                chair_k:=chair_k-keq;    // chair angle relative to timber

                                if (heave_chairs_form.Visible=True) and (timber_str=current_shove_str)
                                   then begin
                                          heave_chairs_form.normal_ch_label4.Caption:='normal  chair :';
                                          heave_chairs_form.chair4_type_label.Caption:=get_cclr_chair_str(4,now_chairing_dims4.chair_code);
                                        end;

                                heaved_chairing_dims4.chairing_dims:=now_chairing_dims4;
                                heaved_chairing_dims4.heaved_valid:=False;                 // init

                                do_bit_settings_plug(0);      // 556a   init

                                nn:=find_shove(timber_str,False);
                                if (nn>=0) and (nn<Length(current_shoved_timbers))     // valid existing shoved slot
                                   then begin
                                          with heaved_chairing_dims4 do begin

                                            heave_rail_chair:=current_shoved_timbers[nn].heave_rail_chairs[4];   // 556..
                                            heaved_valid:=True;

                                            do_bit_settings_plug(heave_rail_chair.hv_plug);      // 556a

                                            if heave_rail_chair.hv_ch<>0                                      // changed chair?
                                               then chairing_dims:=get_heaved_chair(heave_rail_chair.hv_ch);

                                            if heave_rail_chair.hv_ch=10    // SC chair
                                               then begin
                                                      with heave_rail_chair do begin

                                                        if hv_sc_outlong<>0 then chairing_dims.chair_outlong:=hv_sc_outlong*inscale;        // custom SC chair dims
                                                        if hv_sc_inlong<>0 then chairing_dims.chair_inlong:=hv_sc_inlong*inscale;           // custom SC chair dims
                                                        if hv_sc_halfwide<>0 then chairing_dims.chair_halfwide:=hv_sc_halfwide*inscale;     // custom SC chair dims

                                                      end;//with
                                                    end;
                                          end;//with
                                        end;

                                with heaved_chairing_dims4.chairing_dims do begin   // chair on rail 4

                                  dxf_code:=chair_code;  //  237a  dxf_code global - in list for DXF exports

                                  chair_mod:=(chair_outlong+chair_inlong)/2-chair_inlong;   // chair_mod = chair middle from gauge-face   (no effect on track gauge)

                                  p1.x:=xtimbcl-xcl_mod-chair_halfwide+chair_mod*SIN(chair_k)+crad_outer;   // create chair rectangle at y=0 ...
                                  p1.y:=yret+chair_outlong-crad_outer;

                                  p2.x:=xtimbcl-xcl_mod-chair_halfwide+chair_mod*SIN(chair_k)+crad_inner;
                                  p2.y:=yret-chair_inlong+crad_inner;

                                  p3.x:=xtimbcl-xcl_mod+chair_halfwide+chair_mod*SIN(chair_k)-crad_inner;
                                  p3.y:=p2.y;

                                  p4.x:=xtimbcl-xcl_mod+chair_halfwide+chair_mod*SIN(chair_k)-crad_outer;
                                  p4.y:=p1.y;

                                  chair_midx:=(p2.x+p3.x)/2;        // 237c


                                  b1.x:=xtimbcl-xcl_mod+inbolt_1x+chair_mod*SIN(chair_k);               // bolt centres ...
                                  b1.y:=yret-chair_inlong+bolts_from_end;

                                  b2.x:=xtimbcl-xcl_mod+inbolt_2x+chair_mod*SIN(chair_k);
                                  b2.y:=yret-chair_inlong+bolts_from_end;

                                  b3.x:=xtimbcl-xcl_mod+outbolt_1x+chair_mod*SIN(chair_k);
                                  b3.y:=yret+chair_outlong-bolts_from_end;

                                  b4.x:=xtimbcl-xcl_mod+outbolt_2x+chair_mod*SIN(chair_k);
                                  b4.y:=yret+chair_outlong-bolts_from_end;

                                end;//with

                                    // put 2D chair outlines in file ...         // turnout-road stock-rail

                                 if ((timb_str='K') or (timb_str='D')) and (cpi.temp_wrong_k_xing_pi=True)     // 244a temporary for customizing
                                    then begin
                                          chair_twist.x:=chair_midx;       // chair rotation centre
                                          chair_twist.y:=0;                // from gauge-face
                                          chair_twist.k:=0-hdk/2;          // equalized
                                         end
                                    else begin
                                           chair_twist.x:=0;
                                           chair_twist.y:=0;
                                           chair_twist.k:=0;
                                         end;

                                case dxf_code of
                                    2: calc_fill_chair_outline(chair_twist,heaved_chairing_dims4,497,-1);   // P chair
                               11..16: calc_fill_chair_outline(chair_twist,heaved_chairing_dims4,485,-1);   // switch block   CC chairs  turnout-road check-rail
                                    5: calc_fill_chair_outline(chair_twist,heaved_chairing_dims4,493,-1);   // L1 chair
                                  else calc_fill_chair_outline(chair_twist,heaved_chairing_dims4,494,-1);   // turnout-road stock-rail
                                end;//case

                                calc_fill_bolts(chair_twist,dxf_code);

                                    // add chair jaws required ... 237c

                                    // // turnout-road stock-rail... rail 4

                                if heaved_chairing_dims4.heaved_valid=True
                                   then heaved_plug:=heaved_chairing_dims4.heave_rail_chair.hv_plug  // 1=force clip-fit  2=force snap-fit  3=force press-fit  4= force COT (no plug)
                                   else heaved_plug:=0;

                                if dxf_form.cot_bullet_shape.Visible=True                          // COT track
                                   then jaws_wanted:=((heaved_plug=0) or (heaved_plug=4))
                                   else if dxf_form.cot_radio.Checked=True
                                           then jaws_wanted:=(heaved_plug>0) and (heaved_plug<4)
                                           else jaws_wanted:=True;                                 // normal plug track

                                if (exp_chairing=True) and (dxf_form.chairs_combo.ItemIndex<>0) and (jaws_wanted=True)
                                   then begin
                                          aq_y:=aq3offset(chair_midx,aq_k); // for second rail
                                          aq_f:=0;                          // init, modified later, check flare

                                          bbin.x1:=chair_midx-bb;  // near DXF location mark..
                                          bbin.x2:=chair_midx+bb;  // far

                                          bbin.y1:=yret;  // turnout-road stock-rail
                                          bbin.y2:=yret;

                                          // for double chairs ...

                                          bbo:=0;   // init...
                                          bbk:=0;
                                          bbx:=0;

                                          case dxf_code of

                                            11..13: bbo:=0-(aq3offset(chair_midx,bbk)-g);  // for switch rail  -  from stock rail

                                                14: begin   // flare-in
                                                      bbo:=0-check_end_flare.check_add;  // add amount of flare-in at timber
                                                      bbk:=0-check_end_flare.check_k;    // for seat and key insertion

                                                      aq_f:=check_end_flare.check_add;   // for chair jaw customizing..
                                                      aq_k:=0-check_end_flare.check_k;

                                                      bbo:=bbo-fw_mods;  // 239a
                                                    end;

                                                15: bbo:=0-fw_mods;  // 239a  CC check rail


                                                16: begin     // flare-out
                                                      bbo:=0-check_end_flare.check_add;  // add amount of flare-out at timber
                                                      bbk:=check_end_flare.check_k;      // for set and key insertion

                                                      aq_f:=check_end_flare.check_add;   // for chair jaw customizing..
                                                      aq_k:=0-check_end_flare.check_k;

                                                      bbo:=bbo-fw_mods;  // 239a
                                                    end;

                                          end;//case

                                          bb2.x1:=bbin.x1+bbx;
                                          bb2.x2:=bbin.x2+bbx;

                                          bb2.y1:=bbo+bb*TAN(bbk);
                                          bb2.y2:=bbo-bb*TAN(bbk);


                                          chi_mod:=0;  // init
                                          jaw_skew:=0; // init

                                          case dxf_code of

                                          { S1 }  1: begin
                                                       add_jaw('S1OUTJAW', False, bbin);
                                                       add_jaw('S1INJAW',  True,  bbin);
                                                       add_seat('S1SEAT',         bbin,0);
                                                       add_key('KEY',             bbin,0,0);
                                                     end;

                                           { P }  2: begin
                                                       add_jaw('PJAW', False, bbin);
                                                       add_seat('PSEAT',      bbin,0);
                                                     end;

                                          { L1 }  5: begin
                                                       add_jaw('L1OUTJAW', False, bbin);
                                                       add_jaw('L1INJAW',  True,  bbin);
                                                       add_seat('L1SEAT',         bbin,0);
                                                       add_key('KEY',             bbin,0,0);
                                                     end;

                                         { S1J }  7: begin
                                                       add_jaw('S1OUTJAW', False, bbin);
                                                       add_jaw('S1INJAW',  True,  bbin);
                                                       add_seat('S1JSEAT',        bbin,0);
                                                       add_key('KEY',             bbin,0,0);
                                                     end;

                                          { SC } 10: begin                                    // fictional 4-screw
                                                       add_jaw('SCOUTJAW', False, bbin);
                                                       add_jaw('S1INJAW',  True,  bbin);
                                                       add_seat('SCCCSEAT',       bbin,0);
                                                       add_key('KEY',             bbin,0,0);
                                                     end;

                                { 1P..2P }       11: begin
                                                       add_jaw('SCOUTJAW', False, bbin);
                                                       add_jaw('SCINJAW',  True,  bbin);
                                                       add_seat('SCCCSEAT',       bbin,0);
                                                       add_key('KEY',             bbin,0,0);

                                                       chi_mod:=1;
                                                       add_jaw('TM...'+tb_str, False, bb2);
                                                       add_seat('PLR1SEAT',           bb2,0);
                                                     end;

                               { 3P..11P }       12: begin
                                                       add_jaw('SCOUTJAW', False, bbin);
                                                       add_jaw('SCINJAW',  True,  bbin);
                                                       add_seat('SCCCSEAT',       bbin,0);
                                                       add_key('KEY',             bbin,0,0);

                                                       chi_mod:=1;
                                                       add_jaw('TN...'+tb_str, False, bb2);
                                                       add_jaw('TH...'+tb_str, True,  bb2);
                                                       add_seat('PLR3SEAT',           bb2,0);
                                                       add_key('KEY',                 bb2,0-bbk,0);
                                                     end;

                { CCLR working end flare }       14: begin
                                                       add_jaw('SCOUTJAW', False, bbin);
                                                       add_jaw('CCFIJAW',  True,  bbin);
                                                       add_seat('SCCCSEAT',       bbin,0);
                                                       add_key('KEY',             bbin,0,0);

                                                       chi_mod:=1;
                                                       add_jaw('TA...'+tb_str, True,  bbin);
                                                       add_jaw('TB...'+tb_str, False, bbin);
                                                       add_seat('CCSEATCK',           bb2,0);
                                                       add_key('CCKEY',               bb2,0-bbk,0);
                                                     end;

                                    { CC }       15: begin
                                                       add_jaw('SCOUTJAW', False, bbin);
                                                       add_jaw('CCFIJAW',  True,  bbin);
                                                       add_seat('SCCCSEAT',       bbin,0);
                                                       add_key('KEY',             bbin,0,0);

                                                       chi_mod:=1;
                                                       add_jaw('FC...'+tb_str, True,  bbin);
                                                       add_jaw('CCOUTJAW', False, bbin);
                                                       add_seat('CCSEATCK',       bb2,0);
                                                       add_key('CCKEY',           bb2,0,0);
                                                     end;

              { CCLR extension end flare }       16: begin
                                                       add_jaw('SCOUTJAW', False, bbin);
                                                       add_jaw('CCFIJAW',  True,  bbin);
                                                       add_seat('SCCCSEAT',       bbin,0);
                                                       add_key('KEY',             bbin,0,0);

                                                       chi_mod:=1;
                                                       add_jaw('TC...'+tb_str, True,  bbin);
                                                       add_jaw('TD...'+tb_str, False, bbin);
                                                       add_seat('CCSEATCK',           bb2,0);
                                                       add_key('CCKEY',               bb2,0-bbk,0);
                                                     end;

                                          end;//case

                                        end;//if exp chairing

                              end;// rail

                    end;//with _2d_data

                    yret:=save_yret;    // restore

                  end;//calculate_chairs_on_rails
                  //////////////////////////////////////////////////////////////


begin
  if check_plain_track_for_blanking=True then EXIT;  // 241f bug-fix      // timber is blanked out, prevent chairing

  if dxf_form.clip_fit_radio.Checked=True                         // 241b
     then begin
            plug_extended_depth:=clip_plug_extended_depth;
            plug_taper_depth:=clip_plug_taper_depth;
            plug_inset_depth:=clip_plug_inset_depth;
            plug_total_depth:=clip_plug_total_depth;
          end
     else if dxf_form.snap_fit_radio.Checked=True
             then begin
                    plug_extended_depth:=snap_plug_extended_depth;
                    plug_taper_depth:=snap_plug_taper_depth;
                    plug_inset_depth:=snap_plug_inset_depth;
                    plug_total_depth:=snap_plug_total_depth;
                  end
             else begin
                    plug_extended_depth:=press_plug_extended_depth;
                    plug_taper_depth:=press_plug_taper_depth;
                    plug_inset_depth:=press_plug_inset_depth;
                    plug_total_depth:=press_plug_total_depth;
                  end;



  shove_this:=0;     // default init - draw in normal timber colours.
  throw:=0;          // default init.
  crab:=0;

  half_kerfed_width:=0;  // keep compiler happy..
  half_nib_width:=0;

  rail_sep:=0;

  cd_yes:=False;

  near_snib_link_pex1.x:=0;        // keep compiler happy...
  near_snib_link_pex1.y:=0;
  far_snib_link_pex1.x:=0;
  far_snib_link_pex1.y:=0;

  near_snib_link_pex2.x:=0;
  near_snib_link_pex2.y:=0;
  far_snib_link_pex2.x:=0;
  far_snib_link_pex2.y:=0;


  if (plain_track=True) or (timb_str='A') or (timb_str='E') or (timb_str='R')    // 244a
     then begin
            ms_snib2_extent:=snib2_extent_p*inscale;
            ts_snib2_extent:=snib2_extent_p*inscale;

            ms_snib2_angle:=3;   // divisor..
            ts_snib2_angle:=3;
          end
     else begin
            if timbers_equalized=True
               then begin
                      ms_snib2_extent:=snib2_extent_e*inscale;
                      ts_snib2_extent:=snib2_extent_e*inscale;

                      ms_snib2_angle:=2.5;
                      ts_snib2_angle:=2.5;
                    end
               else begin
                      ms_snib2_extent:=snib2_extent_p*inscale;    
                      ts_snib2_extent:=snib2_extent_t*inscale;    // square-on turnout

                      ms_snib2_angle:=3;
                      ts_snib2_angle:=2;
                    end;
          end;


  if dxf_form.snib_space_checkbox.Checked=True
     then begin
            tsn_snib_space:=tsn_snib_space_mm;
            tsf_snib_space:=tsf_snib_space_mm;
            msn_snib_space:=msn_snib_space_mm;
            msf_snib_space:=msf_snib_space_mm;
          end
     else begin
            tsn_snib_space:=0;
            tsf_snib_space:=0;
            msn_snib_space:=0;
            msf_snib_space:=0;
          end;

  if exp_chairing=True                                      // 239a
     then fw_mods:=fw_correction_check //+fw_modify_for_gw
     else fw_mods:=0;


  if dxf_form.extra_switch_clearance_checkbox.Checked=True      // 235b
     then begin
            if (cpi.fw_pi/inscale)>3.5  // 00-BF flangeway
               then extra_opening:=2
               else extra_opening:=1;
          end
     else extra_opening:=0;

  if k3n>10                // 232a  number of crossing chairs spanned by wing rail reach length...
     then end_of_wing:=3   // A B C   over 1:10   size 2 wing rail
     else end_of_wing:=2;  // A B     up to 1:10  size 1 wing rail

  kerf_offset:=cutter_kerf_mm/2;            // 228b

  save_keq:=keq;                            // save the global.
  timber_str:=timb_str+IntToStr(tbn);       // timber number string.

  if (heave_chairs_form.Visible=True) and (timber_str=current_shove_str)   // init   for chair heaving ...
     then begin
            with heave_chairs_form do begin
              normal_ch_label4.Caption:='';
              chair4_type_label.Caption:='';

              normal_ch_label3.Caption:='';
              chair3_type_label.Caption:='';

              normal_ch_label2.Caption:='';
              chair2_type_label.Caption:='';

              normal_ch_label1.Caption:='';
              chair1_type_label.Caption:='';
            end;//with
          end;

                    // see if this one gets shoved or omitted...

  shove_colours_wanted:=-1;  // 226a  init

  tn_snibs_wanted:=True;      // 244a init ...
  tf_snibs_wanted:=True;
  mn_snibs_wanted:=True;
  mf_snibs_wanted:=True;

  near_snib_link_wanted:=False;
  far_snib_link_wanted:=False;

  tn_web_wanted:=True;      // 555a init ...
  tf_web_wanted:=True;
  mn_web_wanted:=True;
  mf_web_wanted:=True;

  n_flange_wanted:=True;     // 229b init ...
  f_flange_wanted:=True;
  ms_flange_wanted:=True;
  ts_flange_wanted:=True;

  chair1_wanted:=True;     // 233 init ...
  chair2_wanted:=True;
  chair3_wanted:=True;
  chair4_wanted:=True;

  for rnum:=1 to 4 do key_wanted[rnum]:=True;  // init   556 MW ...

  outline_wanted:=True;

  for n:=0 to Length(current_shoved_timbers)-1 do begin
    with current_shoved_timbers[n].shove_data do begin
      if (timber_str=current_shoved_timbers[n].sv_str) and (sv_code<>0)              // this timber number is in shove list.
         then begin
                if (shove_timber_form.Showing=True) and (shove_timber_form.show_all_blue_checkbox.Checked=True) then shove_this:=90;  // draw highlighted blue if required (may be overidden later for red if currently selected).

                if sv_code=-1      // this value in the list is a flag, he wants this timber omitted
                   then EXIT
                   else begin
                          if ((sv_option_bits AND $40)<>0) and ( (sv_use_tcol=True) or (sv_use_ocol=True) )     // 226a  shove_data.sv_col_has_been_set=True
                         and (modified_timbering_infill=True)                                                 // generator switch
                             then shove_colours_wanted:=n;

                          xns:=xns+sv_x-sv_w;         // he wants it shoved along and/or widened/narrowed.
                          xfs:=xfs+sv_x+sv_w;

                          if xfs<(xns-inscale/2)      // if shoving produces a negative width (by more than 1/2" scale), average the sides to the centre-line.
                             then begin
                                    xfs:=(xfs+xns)/2;
                                    xns:=xfs;
                                    sv_w:=0;          // reset normal to prevent further narrowing.
                                  end;

                          keq:=keq+sv_k;              // and/or twisted.

                          yns:=yns+sv_o;              // and/or shoved across.
                          ynsred:=ynsred+sv_o;

                          yfs:=yfs+sv_o+sv_l;
                          yfsred:=yfsred+sv_o+sv_l;

                          throw:=sv_o;   // for read-out panel..
                          crab:=sv_c;

                          n_flange_wanted:=((sv_option_bits AND $01)=0);     // 229b ...
                          f_flange_wanted:=((sv_option_bits AND $02)=0);
                          ms_flange_wanted:=((sv_option_bits AND $04)=0);
                          ts_flange_wanted:=((sv_option_bits AND $08)=0);

                          outline_wanted:=((sv_option_bits AND $80)=0);    // 232a msb   not hidden


                          with current_shoved_timbers[n] do begin      // 242a

                            chair1_wanted:= NOT heave_rail_chairs[1].hv_omit;
                            chair2_wanted:= NOT heave_rail_chairs[2].hv_omit;
                            chair3_wanted:= NOT heave_rail_chairs[3].hv_omit;
                            chair4_wanted:= NOT heave_rail_chairs[4].hv_omit;

                            for rnum:=1 to 4 do key_wanted[rnum]:= NOT heave_rail_chairs[rnum].hv_omit_key;     // 556 MW ...

                          end;//with

                          with current_shoved_timbers[n].snibs_data do begin      // 244a

                            tn_snibs_wanted:=(omit_tsn_snibs=False);
                            tf_snibs_wanted:=(omit_tsf_snibs=False);
                            mn_snibs_wanted:=(omit_msn_snibs=False);
                            mf_snibs_wanted:=(omit_msf_snibs=False);

                            near_snib_link_wanted:=add_near_link;
                            far_snib_link_wanted:=add_far_link;

                          end;//with

                          with current_shoved_timbers[n].webs_data do begin      //555a  MW 17-AUG-2024

                            tn_web_wanted:=(omit_tsn_web=False);
                            tf_web_wanted:=(omit_tsf_web=False);
                            mn_web_wanted:=(omit_msn_web=False);
                            mf_web_wanted:=(omit_msf_web=False);

                          end;//with
                        end;
                BREAK;
              end;
    end;//with
  end;//for

  xtimbcl:=(xns+xfs)/2;  // timber centre.

  if (xfs>startx) and (outline_wanted=True)               // 226a
     then begin
            if (timb_str='J') or (timb_str='S') or (timb_str='T') or (timb_str='X') or (timb_str='K') or (timb_str='D')   // 226a
               then begin
                      total_template_timber_length:=total_template_timber_length+ABS(yfs-yns);
                      INC(template_number_of_timbers);

                      pre_str:='t';    // 227a
                    end
               else begin
                      total_template_sleeper_length:=total_template_sleeper_length+ABS(yfs-yns);
                      INC(template_number_of_sleepers);

                      pre_str:='z';    // 227a
                    end;

                 // 227a timber lengths ...

            list_str:=IntToStr(Round(ABS(yfs-yns)/inscale));

            while Length(list_str)<3 do list_str:=' '+list_str;     // for sorting

            list_str:=pre_str+list_str;

            n:=timb_len_list.IndexOf(list_str);

            if n<0
               then begin
                      n:=timb_len_list.AddObject(list_str,Ttimber_count_object.Create);

                      Ttimber_count_object(timb_len_list.Objects[n]).numco:=1;        // init

                    end
               else with Ttimber_count_object(timb_len_list.Objects[n]) do INC(numco);
          end;

  if retcurve=True    // sleepering the return curve..
     then begin
            yret:=aq25offset(xtimbcl,kret)-g/2;
          end
     else begin
            yret:=0;
            kret:=0;
          end;

  keq:=keq+kret;       // add any twist.

         // see if this one is to be shown selected for shoving...

  if (timber_str=current_shove_str) and (shove_timber_form.Showing=True)
     then begin

            shovetimb_wide:=xfs-xns;
            shovetimb_len:=yfs-yns;
            shovetimb_throw:=throw;
            shovetimb_crab:=crab;

            if show_shove_fs=True
               then begin
                      shove_timber_form.width_panel.Caption:=' width :  '+round_str(shovetimb_wide/inscale,2)+' ins';
                      shove_timber_form.length_panel.Caption:=' length :  '+round_str(shovetimb_len/inscale,2)+' ins';
                      shove_timber_form.throw_panel.Caption:=' throw :  '+round_str(shovetimb_throw/inscale,2)+' ins';
                      shove_timber_form.crab_panel.Caption:=' crab :  '+round_str(shovetimb_crab/inscale,2)+' ins';
                    end
               else begin
                      shove_timber_form.width_panel.Caption:=' width :  '+round_str(shovetimb_wide,2)+' mm';
                      shove_timber_form.length_panel.Caption:=' length :  '+round_str(shovetimb_len,2)+' mm';
                      shove_timber_form.throw_panel.Caption:=' throw :  '+round_str(shovetimb_throw,2)+' mm';
                      shove_timber_form.crab_panel.Caption:=' crab :  '+round_str(shovetimb_crab,2)+' mm';
                    end;

            if show_origin_k=True
               then begin
                      docurving(False,True,{xtb}xtimbcl,g/2,dummy1,dummy2,temp_k,dummy3); // curving angle to this xtb.
                      shovetimb_keq:=(keq+temp_k)*hand_i;                                 // twist angle from template origin.

                      if shovetimb_keq<0 then deg_str:=' degs '        // no room for "degrees" in full if negative.
                                         else deg_str:=' degrees ';

                      shove_timber_form.twist_panel.Caption:=' twist :  '+round_str(shovetimb_keq*180/Pi,2)+deg_str+'÷';
                    end
               else begin
                      shovetimb_keq:=keq*hand_i;
                      if shovetimb_keq<0 then deg_str:=' degs '        // no room for "degrees" in full if negative.
                                         else deg_str:=' degrees ';

                      shove_timber_form.twist_panel.Caption:=' twist :  '+round_str(shovetimb_keq*180/Pi,2)+deg_str+'¬';
                    end;

            shove_this:=30;                 // draw highlighted in red.

          end;

      // now ready to make timbering marks...

      // mods for parallel edges - 0.76.a  16-10-01...

  docurving(False,True,{xeq}xtimbcl,yeq{ytimbcl},x_curtimb,y_curtimb,k_curtimb,dummy);    // calc curving for the timber rotation point.

  x_curmod:=x_curtimb-{xeq}xtimbcl;   // shift required to rotation point, to suit the curving..

  y_curmod:=y_curtimb-yeq{ytimbcl};

  try
              // first any blanking? (not bonus timbers)...

    if (timb_str<>'B') and (plain_track=False) and (xfs<startx) then EXIT;  //ignore whole timber if heel side is blanked.

    if outline_wanted=True   // 232a
       then begin

                    // 228b  start with the infill corners for DXF, and 2-D cutter kerf

                    // first do any timber infill, so chairing, outlines and reduced ends can overwrite, possibly in a different colour..


              if (timber_str='S1') and (exp_chairing=True) and (dxf_form.soleplates_combo.ItemIndex<>0)     // reduce full-depth timber to outline of soleplate  236b
                 then begin
                        sp_side:=(ABS(xfs-xns)-soleplate_width*inscale)/2;                            // at each side of soleplate

                        sp_end_near:=(ABS(yns)-(_2d_data.P_chair_outlong+soleplate_rib)*inscale);     // at near end of soleplate
                        sp_end_far:=(ABS(yfs)-(g+(_2d_data.P_chair_outlong+soleplate_rib)*inscale));  // at far end of soleplate

                                // for 2-D ...      244a

                        p1.x:=xns+crab+sp_side;
                        p1.y:=yns+yret+sp_end_near;
                        p2.x:=p1.x;
                        p2.y:=yfs+yret-sp_end_far;

                        enter_mark(True,p1,p2,101,'');      // near side of soleplate       101 is also switch drive mark

                        p1:=p2;
                        p2.x:=xfs+crab-sp_side;

                        enter_mark(True,p1,p2,101,'');      // far end of soleplate

                        p1:=p2;
                        p2.y:=yns+yret+sp_end_near;

                        enter_mark(True,p1,p2,101,'');      // far side of soleplate

                        p1:=p2;
                        p2.x:=xns+crab+sp_side;

                        enter_mark(True,p1,p2,101,'');      // near end of soleplate

                      end
                 else begin
                        sp_side:=0;
                        sp_end_near:=0;
                        sp_end_far:=0;
                      end;


              if (exp_chairing=True) and (dxf_form.switch_drive_checkbox.Checked=True)     // 241c  add ribs for switch-drive slider
                 then begin
                        if (timber_str=slider1_str)   // rib on far side ...
                        or (timber_str=slider2_str)
                        or (timber_str=slider3_str)
                        or (timber_str=slider4_str)
                        or (timber_str=slider5_str)
                           then begin
                                  p1.x:=xfs+crab;
                                  p1.y:=yns+yret;                                 // near end of rib
                                  p2.x:=p1.x+switch_slider_rib_width_mm; //*inscale;
                                  p2.y:=p1.y;

                                  if timbering_infill=True then calc_fill_timber_mark(487);

                                  p1.x:=p2.x;
                                  p1.y:=yfs+yret;                                 // far end of rib
                                  p2.x:=xfs+crab;
                                  p2.y:=yfs+yret;

                                  if timbering_infill=True then calc_fill_timber_mark(0);
                                end;


                        if (timber_str=get_next_timber_along(slider1_str))        // rib on near side
                        or (timber_str=get_next_timber_along(slider2_str))
                        or (timber_str=get_next_timber_along(slider3_str))
                        or (timber_str=get_next_timber_along(slider4_str))
                        or (timber_str=get_next_timber_along(slider5_str))
                           then begin
                                  p1.x:=xns+crab;
                                  p1.y:=yns+yret;                                 // near end of rib
                                  p2.x:=p1.x-switch_slider_rib_width_mm; // *inscale;
                                  p2.y:=p1.y;

                                  if timbering_infill=True then calc_fill_timber_mark(487);

                                  p1.x:=p2.x;
                                  p1.y:=yfs+yret;                                 // far end of rib
                                  p2.x:=xns+crab;
                                  p2.y:=yfs+yret;

                                  if timbering_infill=True then calc_fill_timber_mark(0);
                                end;

                      end;

              p1.x:=xns+crab+sp_side;
              p1.y:=yns+yret+sp_end_near;
              p2.x:=p1.x;                   //xns+crab;
              p2.y:=yfs+yret-sp_end_far;

              if timbering_infill=True then calc_fill_timber_mark(203+shove_this);        // generator switch

              p1.x:=xfs+crab-sp_side;
              p1.y:=yfs+yret-sp_end_far;
              p2.x:=p1.x;                  //xfs+crab;
              p2.y:=yns+yret+sp_end_near;

              if timbering_infill=True then calc_fill_timber_mark(0);   // generator switch

                // 226a   store shoved infill rectangles ...

              if (timbering_infill=True) and (modified_timbering_infill=True) and (shove_colours_wanted>-1)   // generator switch, and index to shove list
                 then begin
                        shoved_corners_pex.p1.x:=xns+crab; //+inscale;
                        shoved_corners_pex.p1.y:=yns+yret; //+inscale;
                        shoved_corners_pex.p2.x:=xns+crab; //+inscale;
                        shoved_corners_pex.p2.y:=yfs+yret; //-inscale;

                        shoved_corners_pex.p3.x:=xfs+crab; //-inscale;
                        shoved_corners_pex.p3.y:=yfs+yret; //-inscale;
                        shoved_corners_pex.p4.x:=xfs+crab; //-inscale;
                        shoved_corners_pex.p4.y:=yns+yret; //+inscale;

                        calc_fill_shoved_corners(shove_colours_wanted);

                        timber_fill_overdraw_generated:=True;  // 227d
                      end;
                      //end;

                // then timber outlines..

              if (sp_side<>0) and (sp_end_near<>0) and (sp_end_far<>0) // doing soleplate
                 then tbl_mod:=0                                       // cancel any timber extensions
                 else tbl_mod:=tbl;                                    // timber extensions if any

              p1.x:=xns-tbl_mod+crab;
              p1.y:=yns+yret;
              p2.x:=xfs+tbl_mod+crab;
              p2.y:=yns+yret;
              calc_fill_timber_mark(3+shove_this);  // mark timber near end. ( 3 = timber outline.)
                                                    // (33 = highlighted timber outline).

              if full_length=True
                 then begin
                        p1.x:=xns-tbl_mod+crab;
                        p1.y:=yfs+yret;
                        p2.x:=xfs+tbl_mod+crab;
                        p2.y:=yfs+yret;
                        calc_fill_timber_mark(3+shove_this);      // mark timber far end
                      end;


              p1.x:=xns+crab;
              p1.y:=yns-tbl_mod+yret;
              p2.x:=xns+crab;
              p2.y:=yfs+tbl_mod+yret;
              calc_fill_timber_mark(3+shove_this);       // timber toe side.

              p1.x:=xfs+crab;
              p1.y:=yns-tbl_mod+yret;
              p2.x:=xfs+crab;
              p2.y:=yfs+tbl_mod+yret;
              calc_fill_timber_mark(3+shove_this);       // timber heel side.

                //-----------

              if (dxf_form.unchaired_kerf_checkbox.Checked=True) or (exp_chairing=True)    // 241e      do kerf lines
                 then begin

                          // do timber outlines adjusted for DXF 2-D cutter kerf ...  228b

                        if dxf_form.nibs_checkbox.Checked=True       // 237b ...
                           then begin
                                  half_kerfed_width:=(xfs-xns)/2+kerf_offset;
                                  half_nib_width:=nib_width/2;


                                  p1.x:=xns+crab-kerf_offset;
                                  p1.y:=yns+yret-kerf_offset;
                                  p2.x:=p1.x+half_kerfed_width-half_nib_width;
                                  p2.y:=p1.y;

                                  calc_fill_timber_mark(9996);  // ms near end 1


                                  p1:=p2;
                                  p2.y:=p1.y-nib_length;

                                  calc_fill_timber_mark(9996);  // ms near end 2


                                  p1:=p2;
                                  p2.x:=p1.x-snib1_extent*inscale;    // at 45 degs
                                  p2.y:=p1.y-snib1_extent*inscale;

                                  if (cpi.ms_snibs_pi=True) and (mn_snibs_wanted=True) then calc_fill_timber_mark(9996);  // ms near end 3


                                  p1.x:=p2.x-msn_snib_space;
                                  p1.y:=p2.y-msn_snib_space;
                                  p2.x:=p1.x-ms_snib2_extent;
                                  p2.y:=p1.y-ms_snib2_extent/ms_snib2_angle;

                                  near_snib_link_pex1:=p2;  // 244a

                                  if (cpi.ms_snibs_pi=True) and (mn_snibs_wanted=True) then calc_fill_timber_mark(9996);  // ms near end 4



                                  p1.x:=xfs+crab+kerf_offset;
                                  p1.y:=yns+yret-kerf_offset;

                                  p2.x:=p1.x-half_kerfed_width+half_nib_width;
                                  p2.y:=p1.y;

                                  calc_fill_timber_mark(9996);  // ms far end 1


                                  p1:=p2;
                                  p2.y:=p1.y-nib_length;

                                  calc_fill_timber_mark(9996);  // ms far end 2


                                  p1:=p2;
                                  p2.x:=p1.x+snib1_extent*inscale;    // at 45 degs
                                  p2.y:=p1.y-snib1_extent*inscale;

                                  if (cpi.ms_snibs_pi=True) and (mf_snibs_wanted=True) then calc_fill_timber_mark(9996);  // ms far end 3


                                  p1.x:=p2.x+msf_snib_space;
                                  p1.y:=p2.y-msf_snib_space;
                                  p2.x:=p1.x+ms_snib2_extent;
                                  p2.y:=p1.y-ms_snib2_extent/ms_snib2_angle;

                                  far_snib_link_pex1:=p2;  // 244a

                                  if (cpi.ms_snibs_pi=True) and (mf_snibs_wanted=True) then calc_fill_timber_mark(9996);  // ms far end 4

                                end
                           else begin
                                  p1.x:=xns+crab-kerf_offset;
                                  p1.y:=yns+yret-kerf_offset;
                                  p2.x:=xfs+crab+kerf_offset;
                                  p2.y:=yns+yret-kerf_offset;
                                  calc_fill_timber_mark(9996);  // mark adjusted near end
                                end;

                        if full_length=True
                           then begin

                                  if dxf_form.nibs_checkbox.Checked=True    // 237b ...
                                     then begin
                                            p1.x:=xns+crab-kerf_offset;
                                            p1.y:=yfs+yret+kerf_offset;

                                            p2.x:=p1.x+half_kerfed_width-half_nib_width;
                                            p2.y:=p1.y;

                                            calc_fill_timber_mark(9996);  // ts near end 1


                                            p1:=p2;
                                            p2.y:=p1.y+nib_length;

                                            calc_fill_timber_mark(9996);  // ts near end 2


                                            p1:=p2;
                                            p2.x:=p1.x-snib1_extent*inscale;    // at 45 degs
                                            p2.y:=p1.y+snib1_extent*inscale;

                                            if (cpi.ts_snibs_pi=True) and (tn_snibs_wanted=True) then calc_fill_timber_mark(9996);  // ts near end 3


                                            p1.x:=p2.x-tsn_snib_space;
                                            p1.y:=p2.y+tsn_snib_space;
                                            p2.x:=p1.x-ts_snib2_extent;
                                            p2.y:=p1.y+ts_snib2_extent/ts_snib2_angle;

                                            near_snib_link_pex2:=p2;  // 244a

                                            if (cpi.ts_snibs_pi=True) and (tn_snibs_wanted=True) then calc_fill_timber_mark(9996);  // ts near end 4




                                            p1.x:=xfs+crab+kerf_offset;
                                            p1.y:=yfs+yret+kerf_offset;

                                            p2.x:=p1.x-half_kerfed_width+half_nib_width;
                                            p2.y:=p1.y;

                                            calc_fill_timber_mark(9996);  // ts far end 1


                                            p1:=p2;
                                            p2.y:=p1.y+nib_length;

                                            calc_fill_timber_mark(9996);  // ts far end 2


                                            p1:=p2;
                                            p2.x:=p1.x+snib1_extent*inscale;    // at 45 degs
                                            p2.y:=p1.y+snib1_extent*inscale;

                                            if (cpi.ts_snibs_pi=True) and (tf_snibs_wanted=True) then calc_fill_timber_mark(9996);  // ts far end 3


                                            p1.x:=p2.x+tsf_snib_space;
                                            p1.y:=p2.y+tsf_snib_space;
                                            p2.x:=p1.x+ts_snib2_extent;
                                            p2.y:=p1.y+ts_snib2_extent/ts_snib2_angle;

                                            far_snib_link_pex2:=p2;  // 244a

                                            if (cpi.ts_snibs_pi=True) and (tf_snibs_wanted=True) then calc_fill_timber_mark(9996);  // ts far end 4

                                          end
                                     else begin
                                            p1.x:=xns+crab-kerf_offset;
                                            p1.y:=yfs+yret+kerf_offset;
                                            p2.x:=xfs+crab+kerf_offset;
                                            p2.y:=yfs+yret+kerf_offset;
                                            calc_fill_timber_mark(9996);      // mark adjusted far end

                                         end;


                                  if near_snib_link_wanted=True
                                     then begin
                                            p1:=near_snib_link_pex1;
                                            p2:=near_snib_link_pex2;
                                            calc_fill_timber_mark(9996);  // near snib link
                                          end;

                                  if far_snib_link_wanted=True
                                     then begin
                                            p1:=far_snib_link_pex1;
                                            p2:=far_snib_link_pex2;
                                            calc_fill_timber_mark(9996);  // far snib link
                                          end;

                                end;

                        p1.x:=xns+crab-kerf_offset;
                        p1.y:=yns+yret-kerf_offset;
                        p2.x:=xns+crab-kerf_offset;
                        p2.y:=yfs+yret+kerf_offset;
                        calc_fill_timber_mark(9996);       // adjusted toe side.

                        p1.x:=xfs+crab+kerf_offset;
                        p1.y:=yns+yret-kerf_offset;
                        p2.x:=xfs+crab+kerf_offset;
                        p2.y:=yfs+yret+kerf_offset;
                        calc_fill_timber_mark(9996);       // adjusted heel side.

                      end;


              if exp_chairing=True
                 then begin

                          // do timber flanges

                        xnso:=xns-flange_offset*inscale+flange_integrity_overlap*inscale;     // add the offset if any, for using flanges as spacing a framework...
                        xfso:=xfs+flange_offset*inscale-flange_integrity_overlap*inscale;
                        ynso:=yns-flange_offset*inscale+flange_integrity_overlap*inscale;
                        yfso:=yfs+flange_offset*inscale-flange_integrity_overlap*inscale;

                        if (dxf_form.flanges_combo.ItemIndex<>0) or ((dxf_form.cot_radio.Checked=True) and (dxf_form.cot_flanges_checkbox.Checked=True))
                           then begin

                                  // add timber outer side flanges (8 marks each side)

                                  // near side:

                                  if n_flange_wanted=True    // 229b
                                     then begin

                                            p1.x:=xnso+crab;
                                            p1.y:=ynso+yret;

                                            p2.x:=p1.x-side_flange_width_mm; // *inscale;
                                            p2.y:=p1.y;

                                            calc_fill_timber_mark(490);

                                            p1:=p2;
                                            p2.x:=p1.x;
                                            p2.y:=yfso+yret;

                                            calc_fill_timber_mark(490);

                                            p1:=p2;
                                            p2.x:=xnso+crab;
                                            p2.y:=p1.y;

                                            calc_fill_timber_mark(490);

                                            p1:=p2;
                                            p2.x:=p1.x;
                                            p2.y:=ynso+yret;

                                            calc_fill_timber_mark(490);


                                               // dropper ridge ...     241f

                                            p1.x:=xns-dropper_ridge_spacing_top_mm+crab;
                                            p1.y:=ynso+yret;

                                            p2.x:=xns-dropper_ridge_spacing_bottom_mm+crab;
                                            p2.y:=p1.y;

                                            calc_fill_timber_mark(479);

                                            p1:=p2;
                                            p2.x:=p1.x;
                                            p2.y:=yfso+yret;

                                            calc_fill_timber_mark(479);

                                            p1:=p2;
                                            p2.x:=xns-dropper_ridge_spacing_top_mm+crab;
                                            p2.y:=p1.y;

                                            calc_fill_timber_mark(479);

                                            p1:=p2;
                                            p2.x:=p1.x;
                                            p2.y:=ynso+yret;

                                            calc_fill_timber_mark(479);

                                          end;

                                    // far side:

                                  if f_flange_wanted=True
                                     then begin

                                            p1.x:=xfso+crab;
                                            p1.y:=ynso+yret;

                                            p2.x:=p1.x+side_flange_width_mm; // *inscale;
                                            p2.y:=p1.y;

                                            calc_fill_timber_mark(490);

                                            p1:=p2;
                                            p2.x:=p1.x;
                                            p2.y:=yfso+yret;

                                            calc_fill_timber_mark(490);

                                            p1:=p2;
                                            p2.x:=xfso+crab;
                                            p2.y:=p1.y;

                                            calc_fill_timber_mark(490);

                                            p1:=p2;
                                            p2.x:=p1.x;
                                            p2.y:=ynso+yret;

                                            calc_fill_timber_mark(490);


                                               // far dropper ridge...    241f

                                            p1.x:=xfs+dropper_ridge_spacing_top_mm+crab;
                                            p1.y:=ynso+yret;

                                            p2.x:=xfs+dropper_ridge_spacing_bottom_mm+crab;
                                            p2.y:=p1.y;

                                            calc_fill_timber_mark(479);

                                            p1:=p2;
                                            p2.x:=p1.x;
                                            p2.y:=yfso+yret;

                                            calc_fill_timber_mark(479);

                                            p1:=p2;
                                            p2.x:=xfs+dropper_ridge_spacing_top_mm+crab;
                                            p2.y:=p1.y;

                                            calc_fill_timber_mark(479);

                                            p1:=p2;
                                            p2.x:=p1.x;
                                            p2.y:=ynso+yret;

                                            calc_fill_timber_mark(479);

                                          end;
                                end;

                        if ( (dxf_form.flanges_combo.ItemIndex<>0) or ((dxf_form.cot_radio.Checked=True) and (dxf_form.cot_flanges_checkbox.Checked=True)) )
                        and (dxf_form.end_flanges_checkbox.Checked=True)
                           then begin

                                  // add timber outer end flanges (8 marks each end)

                                     // near end...

                                  if ms_flange_wanted=True
                                     then begin

                                            p1.x:=xnso+crab-side_flange_width_mm; // *inscale;    244d
                                            p1.y:=ynso+yret+flange_integrity_overlap*inscale;    // doubled overlap to ensure integrity

                                            p2.x:=p1.x;
                                            p2.y:=p1.y-(end_flange_width+flange_integrity_overlap*2)*inscale;

                                            calc_fill_timber_mark(490);

                                            p1:=p2;
                                            p2.x:=xfso+crab+side_flange_width_mm; // *inscale;  244d
                                            p2.y:=p1.y;

                                            calc_fill_timber_mark(490);

                                            p1:=p2;
                                            p2.x:=p1.x;
                                            p2.y:=ynso+yret+flange_integrity_overlap*inscale;    // doubled overlap to ensure integrity

                                            calc_fill_timber_mark(490);

                                            p1:=p2;
                                            p2.x:=xnso+crab-side_flange_width_mm; // *inscale;
                                            p2.y:=p1.y;

                                            calc_fill_timber_mark(490);


                                              // ridge not wanted at ends...

                                            p1.x:=0;
                                            p1.y:=0;

                                            p2.x:=0;
                                            p2.y:=0;

                                            calc_fill_timber_mark(490);
                                            calc_fill_timber_mark(490);
                                            calc_fill_timber_mark(490);
                                            calc_fill_timber_mark(490);

                                          end;

                                    // far end...

                                  if ts_flange_wanted=True
                                     then begin

                                            p1.x:=xnso+crab-side_flange_width_mm; // *inscale;    244d
                                            p1.y:=yfso+yret-flange_integrity_overlap*inscale;    // doubled overlap to ensure integrity

                                            p2.x:=p1.x;
                                            p2.y:=p1.y+(end_flange_width+flange_integrity_overlap*2)*inscale;     // neater outer edge

                                            calc_fill_timber_mark(490);

                                            p1:=p2;
                                            p2.x:=xfso+crab+side_flange_width_mm; // *inscale;
                                            p2.y:=p1.y;

                                            calc_fill_timber_mark(490);

                                            p1:=p2;
                                            p2.x:=p1.x;
                                            p2.y:=yfso+yret-flange_integrity_overlap*inscale;    // doubled overlap to ensure integrity

                                            calc_fill_timber_mark(490);

                                            p1:=p2;
                                            p2.x:=xnso+crab-side_flange_width_mm; // *inscale;
                                            p2.y:=p1.y;

                                            calc_fill_timber_mark(490);


                                              // ridge not wanted at ends...

                                            p1.x:=0;
                                            p1.y:=0;

                                            p2.x:=0;
                                            p2.y:=0;

                                            calc_fill_timber_mark(490);
                                            calc_fill_timber_mark(490);
                                            calc_fill_timber_mark(490);
                                            calc_fill_timber_mark(490);

                                          end;
                                end;

                              // add timber sprue marks if wanted...

                           // far end ( 6 marks) ...

                        if dxf_form.sprues_combo.ItemIndex<>0
                           then begin

                                  p1.x:=xns+crab+(xfs-xns-sprue_width*inscale)/2;

                                  if dxf_form.sprue_kerf_checkbox.Checked=True       // 235a
                                     then p1.y:=yfs+yret+kerf_offset
                                     else p1.y:=yfs+yret;

                                  p2.x:=p1.x;                                     // top corner
                                  p2.y:=p1.y+(sprue_length+sprue_runner_width)*inscale;

                                  calc_fill_timber_mark(9999);   // near side

                                  p1:=p2;
                                  p2.x:=p1.x+33*inscale;  // overlap next timber
                                  p2.y:=p1.y;

                                  calc_fill_timber_mark(9999);   // top outer

                                  p1:=p2;
                                  p2.x:=p1.x;
                                  p2.y:=p1.y-sprue_runner_width*inscale;

                                  calc_fill_timber_mark(9999);   // far end

                                  p1:=p2;
                                  p2.x:=p1.x-(33-sprue_width)*inscale;
                                  p2.y:=p1.y;

                                  calc_fill_timber_mark(9999);   // top inner

                                  p1:=p2;
                                  p2.x:=xns+crab+(xfs-xns+sprue_width*inscale)/2;

                                  if dxf_form.sprue_kerf_checkbox.Checked=True       // 235a
                                     then p2.y:=yfs+yret+kerf_offset
                                     else p2.y:=yfs+yret;

                                  calc_fill_timber_mark(9999);   // far side

                                  p1:=p2;
                                  p2.x:=xns+crab+(xfs-xns-sprue_width*inscale)/2;

                                  if dxf_form.sprue_kerf_checkbox.Checked=True       // 235a
                                     then p2.y:=yfs+yret+kerf_offset
                                     else p2.y:=yfs+yret;

                                  calc_fill_timber_mark(9999);   // back to start  for 3-D


                                     // near end ( 6 marks) ...

                                  p1.x:=xns+crab+(xfs-xns-sprue_width*inscale)/2;

                                  if dxf_form.sprue_kerf_checkbox.Checked=True       // 235a
                                     then p1.y:=yns+yret-kerf_offset
                                     else p1.y:=yns+yret;

                                  p2.x:=p1.x;                                     // bottom corner
                                  p2.y:=p1.y-(sprue_length+sprue_runner_width)*inscale;

                                  calc_fill_timber_mark(9999);   // near side

                                  p1:=p2;
                                  p2.x:=p1.x+33*inscale;  // overlap next timber
                                  p2.y:=p1.y;

                                  calc_fill_timber_mark(9999);   // bottom outer outer

                                  p1:=p2;
                                  p2.x:=p1.x;
                                  p2.y:=p1.y+sprue_runner_width*inscale;

                                  calc_fill_timber_mark(9999);   // far end

                                  p1:=p2;
                                  p2.x:=p1.x-(33-sprue_width)*inscale;
                                  p2.y:=p1.y;

                                  calc_fill_timber_mark(9999);   // bottom inner

                                  p1:=p2;
                                  p2.x:=xns+crab+(xfs-xns+sprue_width*inscale)/2;

                                  if dxf_form.sprue_kerf_checkbox.Checked=True       // 235a
                                     then p2.y:=yns+yret-kerf_offset
                                     else p2.y:=yns+yret;

                                  calc_fill_timber_mark(9999);   // far side

                                  p1:=p2;
                                  p2.x:=xns+crab+(xfs-xns-sprue_width*inscale)/2;

                                  if dxf_form.sprue_kerf_checkbox.Checked=True       // 235a
                                     then p2.y:=yns+yret-kerf_offset
                                     else p2.y:=yns+yret;

                                  calc_fill_timber_mark(9999);   // back to start  for 3-D

                                end;


                            // add timber web marks if wanted...    (16 marks total)

                        if dxf_form.webs_combo.ItemIndex<>0
                           then begin
                                  flexi:=0; // init

                                  if dxf_form.flexi_checkbox.Checked=True  // 241f
                                     then begin
                                            if dxf_form.flexi_random_radio.Checked=True
                                               then flexi:=RANDOM-0.5
                                               else if dxf_form.flexi_1_radio.Checked=True
                                                       then flexi:=FRAC(tbn/2)-0.25
                                                       else if dxf_form.flexi_2_radio.Checked=True
                                                              then flexi:=FRAC(tbn/4)-0.375
                                                              else if dxf_form.flexi_3_radio.Checked=True
                                                                      then flexi:=FRAC(tbn/6)-0.417;
                                          end;

                                  if ( (dxf_form.flanges_combo.ItemIndex<>0) or ((dxf_form.cot_radio.Checked=True) and (dxf_form.cot_flanges_checkbox.Checked=True)) )
                                  and (dxf_form.end_flanges_checkbox.Checked=True)
                                     then web_end_offset:=(end_flange_width+flange_offset)*inscale
                                     else web_end_offset:=flange_offset*inscale;

                                       // near side ...

                                  if ( (dxf_form.flanges_combo.ItemIndex<>0) or ((dxf_form.cot_radio.Checked=True) and (dxf_form.cot_flanges_checkbox.Checked=True)) )
                                  and (n_flange_wanted=True)
                                     then web_side_offset:=side_flange_width_mm+flange_offset*inscale
                                     else web_side_offset:=flange_offset*inscale;

                                  web_side_offset:=web_side_offset-(web_integrity_overlap+flange_integrity_overlap)*inscale;  // 234c

                                  if flexi<>0
                                     then begin
                                            web_width_mm:=web_width_flexi*inscale;  // 241f

                                            n_web_length_mm:=(web_length+3)*inscale;   // init 241f       3" arbitrary increase in case single side
                                            f_web_length_mm:=(web_length+3)*inscale;   // init 241f
                                          end
                                     else begin
                                            web_width_mm:=web_width*inscale;

                                            n_web_length_mm:=web_length*inscale;   // init 241f
                                            f_web_length_mm:=web_length*inscale;   // init 241f
                                          end;

                                     // near side lower ...      4 marks

                                  if (ms_flange_wanted=True) and (mn_web_wanted=True) and (flexi>=0)   // no web if no end flange 229b
                                     then begin

                                            p1.x:=xns+crab-web_side_offset;
                                            p1.y:=yns+yret-web_end_offset;

                                            p2.x:=xns+crab-n_web_length_mm;
                                            p2.y:=p1.y;

                                            calc_fill_timber_mark(9998);

                                            p1:=p2;
                                            p2.x:=p1.x;
                                            p2.y:=p1.y+web_width_mm;

                                            calc_fill_timber_mark(9998);

                                            p1:=p2;
                                            p2.x:=xns+crab-web_side_offset;
                                            p2.y:=p1.y;

                                            calc_fill_timber_mark(9998);

                                            p1:=p2;
                                            p2.x:=p1.x;
                                            p2.y:=yns+yret-web_end_offset;

                                            calc_fill_timber_mark(9998);

                                          end;

                                     // near side upper ...     4 marks

                                  if (ts_flange_wanted=True) and (tn_web_wanted=True) and (flexi<=0)    // no web if no end flange 229b
                                     then begin

                                            p1.x:=xns+crab-web_side_offset;
                                            p1.y:=yfs+yret+web_end_offset;

                                            p2.x:=xns+crab-f_web_length_mm;
                                            p2.y:=p1.y;

                                            calc_fill_timber_mark(9998);

                                            p1:=p2;
                                            p2.x:=p1.x;
                                            p2.y:=p1.y-web_width_mm;

                                            calc_fill_timber_mark(9998);

                                            p1:=p2;
                                            p2.x:=xns+crab-web_side_offset;
                                            p2.y:=p1.y;

                                            calc_fill_timber_mark(9998);

                                            p1:=p2;
                                            p2.x:=p1.x;
                                            p2.y:=yfs+yret+web_end_offset;

                                            calc_fill_timber_mark(9998);

                                          end;

                                  // far side ( 8 marks) ...

                                  if ( (dxf_form.flanges_combo.ItemIndex<>0) or ((dxf_form.cot_radio.Checked=True) and (dxf_form.cot_flanges_checkbox.Checked=True)) )
                                  and (f_flange_wanted=True)
                                     then web_side_offset:=side_flange_width_mm+flange_offset*inscale
                                     else web_side_offset:=flange_offset*inscale;

                                  web_side_offset:=web_side_offset-(web_integrity_overlap+flange_integrity_overlap)*inscale;  // 234c

                                     // far side lower ...         4 marks

                                  if (ms_flange_wanted=True) and (mf_web_wanted=True) and (flexi<=0)    // no web if no end flange 229b
                                     then begin

                                            p1.x:=xfs+crab+web_side_offset;
                                            p1.y:=yns+yret-web_end_offset;

                                            p2.x:=xfs+crab+n_web_length_mm;
                                            p2.y:=p1.y;

                                            calc_fill_timber_mark(9998);

                                            p1:=p2;
                                            p2.x:=p1.x;
                                            p2.y:=p1.y+web_width_mm;

                                            calc_fill_timber_mark(9998);

                                            p1:=p2;
                                            p2.x:=xfs+crab+web_side_offset;
                                            p2.y:=p1.y;

                                            calc_fill_timber_mark(9998);

                                            p1:=p2;
                                            p2.x:=p1.x;
                                            p2.y:=yns+yret-web_end_offset;

                                            calc_fill_timber_mark(9998);

                                          end;

                                     // far side upper ...          4 marks

                                  if (ts_flange_wanted=True) and (tf_web_wanted=True) and (flexi>=0)    // no web if no end flange 229b
                                     then begin

                                            p1.x:=xfs+crab+web_side_offset;
                                            p1.y:=yfs+yret+web_end_offset;

                                            p2.x:=xfs+crab+f_web_length_mm;
                                            p2.y:=p1.y;

                                            calc_fill_timber_mark(9998);

                                            p1:=p2;
                                            p2.x:=p1.x;
                                            p2.y:=p1.y-web_width_mm;

                                            calc_fill_timber_mark(9998);

                                            p1:=p2;
                                            p2.x:=xfs+crab+web_side_offset;
                                            p2.y:=p1.y;

                                            calc_fill_timber_mark(9998);

                                            p1:=p2;
                                            p2.x:=p1.x;
                                            p2.y:=yfs+yret+web_end_offset;

                                            calc_fill_timber_mark(9998);

                                          end;
                                end;

                      end;//exp_chairing


                      //-------------------

              if (nine_foot=True) and (reduced_ends=True)   // standard 9ft timbering..
                 then begin
                        if shove_this=30 then shove_this:=50;

                        p1.x:=xns-tbl+crab;           // draw reduced timber ends (close-dotted) ...
                        p1.y:=ynsred+yret;
                        p2.x:=xfs+tbl+crab;
                        p2.y:=ynsred+yret;
                        calc_fill_timber_mark(5+shove_this); // 5 =  timber reduced near end
                                                             // 55 = highlighted reduced ends (selected, red).
                                                             // 95 = highlighted reduced ends (shoved, blue).
                        if full_length=True
                           then begin
                                  p1.x:=xns-tbl+crab;
                                  p1.y:=yfsred+yret;
                                  p2.x:=xfs+tbl+crab;
                                  p2.y:=yfsred+yret;
                                  calc_fill_timber_mark(5+shove_this);       // timber reduced far end
                                end;
                      end;



                  // can now do the timber centre-lines if there is some data...

              if (pad_form.timber_centres_menu_entry.Checked=True) and (timbcentre_wait.valid=True)
                 then begin

                        with timbcentre_wait do begin  // saved actual co-ords, to be entered in list after the outlines.
                          p1:=pex1;                    // (this is a fix - we must do the centre-line calcs first for the shove timber select,
                          p2:=pex2;                    //  but don't want the centre-lines overdrawn by timber infill.)

                          keq:=kq;
                          shove_this:=shove_code;

                          valid:=False       // don't use this data again.
                        end;//with

                        if midline=False then calc_fill_timber_mark( 4+shove_this)  // 4 = centre-line,  44 = selected timber.
                                         else calc_fill_timber_mark(14+shove_this); // 14,54 = timber centre-line, use solid line if drawing rail centrelines. (for rivets?)

                      end;


            end; // outline_wanted




        // 221a and finally do the experimental chairs on this timber...
                                                    
    if exp_chairing=True // experimental chairing
       then begin

                   // chair codes ...

               //  0=ignore

               //  1=S1 chair, 2=P chair, 3=  , 4= , 5=L1 bridge chair, 6=M1 bridge chair, 7=S1J joint chair
               //  8=S1O half-bolted chair outer, 9=S1N half-bolted chair inner, 10=SC fictional 4-screw chair

               //  11=switch block chair type 1 (no jaw)     A B C D switches  1PR and 2PR
               //  12=switch block chair type 2 (normal key) A B C D E F switches
               //  13=switch block chair type 3 (inner key)  E F switches 1PR and 2PR

               //  14-15-16  // check rail chairs  flare-in, parallel, flare-out    237a

               //  98=FG dummy chair  foot groove        234a
               //  99=RG dummy chair  rail head groove   234a

              with null_chair do begin    // ignore this on a rail

                chair_code:=0;

                chair_outlong:=0;
                chair_inlong:=0;
                chair_halfwide:=0;


                chair_mid_halfwide_near:=0;
                chair_mid_halfwide_far:=0;


                inbolt_1x:=0;
                inbolt_2x:=0;

                outbolt_1x:=0;
                outbolt_2x:=0;

                bolts_from_end:=0;

                crad_inner:=0;
                crad_outer:=0;
              end;//with

              init_rail_section;   // update any changes in rail-fit

                // init chair outlines for 2-D ...

              with _2d_data do begin

                with S1_chairing_dims do begin  // init ..

                  chair_code:=1;

                  chair_outlong:=S1_chair_outlong*inscale;
                  chair_inlong:=S1_chair_inlong*inscale;
                  chair_halfwide:=S1_chair_halfwide*inscale;

                  chair_mid_halfwide_near:=0;
                  chair_mid_halfwide_far:=0;

                  inbolt_1x:=S1_inbolt_1x*inscale;
                  inbolt_2x:=S1_inbolt_2x*inscale;

                  outbolt_1x:=S1_outbolt_1x*inscale;
                  outbolt_2x:=S1_outbolt_2x*inscale;

                  bolts_from_end:=S1_bolts_from_end*inscale;

                  crad_inner:=S1_chair_crad_inner*inscale;
                  crad_outer:=S1_chair_crad_outer*inscale;
                end;//with


                with S1J_chairing_dims do begin  // init ..

                  chair_code:=7;

                  chair_outlong:=S1J_chair_outlong*inscale;
                  chair_inlong:=S1J_chair_inlong*inscale;
                  chair_halfwide:=S1J_chair_halfwide*inscale;

                  chair_mid_halfwide_near:=0;
                  chair_mid_halfwide_far:=0;

                  inbolt_1x:=S1J_inbolt_1x*inscale;
                  inbolt_2x:=S1J_inbolt_2x*inscale;

                  outbolt_1x:=S1J_outbolt_1x*inscale;
                  outbolt_2x:=S1J_outbolt_2x*inscale;

                  bolts_from_end:=S1J_bolts_from_end*inscale;

                  crad_inner:=S1J_chair_crad_inner*inscale;
                  crad_outer:=S1J_chair_crad_outer*inscale;
                end;//with

                  // 232c  set switch opening to model gauge, if not set as custom ...

                if use_custom_switch_opening=True                // set switch opening to model gauge, if not set as custom...
                   then switch_opening:=custom_switch_opening
                   else begin
                          if fw<(2.25*inscale)
                             then switch_opening:=4.25     // inches full-size
                             else if fw<(3.25*inscale)
                                     then switch_opening:=5.25     // 00-SF, EM, 0-MF
                                     else switch_opening:=6.25;    // 00-MF, 00-BF
                        end;

                switch_opening:=switch_opening+0.5;    // inches  + 1/2" allowance for vertical model rail   arbitrary

                P_chair_inlong_mod:=P_chair_inlong+(switch_opening-4.25);   // allow for increased model switch opening, set up for DXF

                with P_chairing_dims do begin  // init ..

                  chair_code:=2;

                  chair_outlong:=P_chair_outlong*inscale;
                  chair_inlong:=P_chair_inlong_mod*inscale;
                  chair_halfwide:=P_chair_halfwide*inscale;

                  chair_mid_halfwide_near:=0;
                  chair_mid_halfwide_far:=0;

                  inbolt_1x:=P_inbolt_1x*inscale;
                  inbolt_2x:=P_inbolt_2x*inscale;

                  outbolt_1x:=P_outbolt_1x*inscale;
                  outbolt_2x:=P_outbolt_2x*inscale;

                  bolts_from_end:=P_bolts_from_end*inscale;

                  crad_inner:=P_chair_crad_inner*inscale;
                  crad_outer:=P_chair_crad_outer*inscale;
                end;//with


                with L1_chairing_dims do begin  // init ..

                  chair_code:=5;

                  chair_outlong:=L1_chair_outlong*inscale;
                  chair_inlong:=L1_chair_inlong*inscale;
                  chair_halfwide:=L1_chair_halfwide*inscale;

                  chair_mid_halfwide_near:=0;
                  chair_mid_halfwide_far:=0;

                  inbolt_1x:=L1_inbolt_1x*inscale;
                  inbolt_2x:=L1_inbolt_2x*inscale;

                  outbolt_1x:=L1_outbolt_1x*inscale;
                  outbolt_2x:=L1_outbolt_2x*inscale;

                  bolts_from_end:=L1_bolts_from_end*inscale;

                  crad_inner:=L1_chair_crad_inner*inscale;
                  crad_outer:=L1_chair_crad_outer*inscale;
                end;//with


                with SC_chairing_dims do begin  // init ..         fictional 4-bolt ordinary chairs

                  chair_code:=10;

                  chair_outlong:=SC_chair_outlong*inscale;
                  chair_inlong:=SC_chair_inlong*inscale;
                  chair_halfwide:=SC_chair_halfwide*inscale;

                  chair_mid_halfwide_near:=0;
                  chair_mid_halfwide_far:=0;

                  inbolt_1x:=SC_inbolt_1x*inscale;
                  inbolt_2x:=SC_inbolt_2x*inscale;

                  outbolt_1x:=SC_outbolt_1x*inscale;
                  outbolt_2x:=SC_outbolt_2x*inscale;

                  bolts_from_end:=SC_bolts_from_end*inscale;

                  crad_inner:=SC_chair_crad_inner*inscale;
                  crad_outer:=SC_chair_crad_outer*inscale;
                end;//with


                with PLR1_chairing_dims do begin  // init ..

                  chair_code:=11;  // no inner jaw   block chairs 1 and 2  switch rail half  switches A..D

                  chair_outlong:=PLR1_chair_outlong*inscale;  // from stock rail (gauge-face)

                  chair_inlong:=aq2offset(xtimbcl,chair_k)+PLR1_block_slide_chair_inlong*inscale;  // from stock rail (gauge-face)  calc for this timber

                  chair_halfwide:=PLR1_chair_halfwide*inscale;

                  chair_mid_halfwide_near:=0;
                  chair_mid_halfwide_far:=0;

                  inbolt_1x:=PLR1_inbolt_1x*inscale;
                  inbolt_2x:=PLR1_inbolt_2x*inscale;

                  outbolt_1x:=PLR1_outbolt_1x*inscale;
                  outbolt_2x:=PLR1_outbolt_2x*inscale;

                  bolts_from_end:=PLR1_bolts_from_end*inscale;

                  crad_inner:=PLR1_chair_crad_inner*inscale;
                  crad_outer:=PLR1_chair_crad_outer*inscale;
                end;//with


                PLR3_chairing_dims:=PLR1_chairing_dims;   // block chairs 3..11    init same as chairs 1 and 2 except...

                with PLR3_chairing_dims do begin  // init ..
                  chair_code:=12;
                  chair_inlong:=aq2offset(xtimbcl,chair_k)+PLR3_block_heel_chair_inlong*inscale;  // from stock rail (gauge-face)  calc for this timber
                end;//with


                // nyi PLR1X_chairing_dims:=PLR1_chairing_dims;  // first 2 switch block slide  E and F switches (inside keyed)
                // nyi chair_code:=13;


                with CCW_chairing_dims do begin  // init ..

                  chair_code:=14;    // flare in

                  chair_outlong:=CC_chair_outlong*inscale;
                  chair_inlong:=0;                            // mod later   adjust chair length for model flangeway and flare

                  chair_halfwide:=CC_chair_halfwide*inscale;

                  chair_mid_halfwide_near:=0;
                  chair_mid_halfwide_far:=0;

                  inbolt_1x:=CC_inbolt_1x*inscale;
                  inbolt_2x:=CC_inbolt_2x*inscale;

                  outbolt_1x:=CC_outbolt_1x*inscale;
                  outbolt_2x:=CC_outbolt_2x*inscale;

                  bolts_from_end:=CC_bolts_from_end*inscale;

                  crad_inner:=CC_chair_crad_inner*inscale;
                  crad_outer:=CC_chair_crad_outer*inscale;
                end;//with


                with CCE_chairing_dims do begin  // init ..

                  chair_code:=16;    // flare out

                  chair_outlong:=CC_chair_outlong*inscale;
                  chair_inlong:=0;                            // mod later adjust chair length for model flangeway and flare

                  chair_halfwide:=CC_chair_halfwide*inscale;

                  chair_mid_halfwide_near:=0;
                  chair_mid_halfwide_far:=0;

                  inbolt_1x:=CC_inbolt_1x*inscale;
                  inbolt_2x:=CC_inbolt_2x*inscale;

                  outbolt_1x:=CC_outbolt_1x*inscale;
                  outbolt_2x:=CC_outbolt_2x*inscale;

                  bolts_from_end:=CC_bolts_from_end*inscale;

                  crad_inner:=CC_chair_crad_inner*inscale;
                  crad_outer:=CC_chair_crad_outer*inscale;
                end;//with


                with CC_chairing_dims do begin  // init ..

                  chair_code:=15;    // parallel part

                  chair_outlong:=CC_chair_outlong*inscale;
                  chair_inlong:=chair_outlong+(fw+fw_mods);   // adjust chair length for model flangeway

                  chair_halfwide:=CC_chair_halfwide*inscale;

                  chair_mid_halfwide_near:=0;
                  chair_mid_halfwide_far:=0;

                  inbolt_1x:=CC_inbolt_1x*inscale;
                  inbolt_2x:=CC_inbolt_2x*inscale;

                  outbolt_1x:=CC_outbolt_1x*inscale;
                  outbolt_2x:=CC_outbolt_2x*inscale;

                  bolts_from_end:=CC_bolts_from_end*inscale;

                  crad_inner:=CC_chair_crad_inner*inscale;
                  crad_outer:=CC_chair_crad_outer*inscale;
                end;//with


                  // V-crossing chairs  239a  ...

                with ZY_chairing_dims do begin  // init ..

                  chair_code:=21;    // ZY  in front of knuckle

                  chair_outlong:=X_chair_outlong*inscale;
                  chair_inlong:=X_chair_inlong*inscale;                         // adjust chair length later from rail separation

                  chair_halfwide:=X_chair_halfwide*inscale;

                  chair_mid_halfwide_near:=0;
                  chair_mid_halfwide_far:=0;

                  inbolt_1x:=X_inbolt_1x*inscale;
                  inbolt_2x:=X_inbolt_2x*inscale;

                  outbolt_1x:=X_outbolt_1x*inscale;
                  outbolt_2x:=X_outbolt_2x*inscale;

                  bolts_from_end:=X_bolts_from_end*inscale;

                  crad_inner:=X_chair_crad_inner*inscale;
                  crad_outer:=X_chair_crad_outer*inscale;
                end;//with


                with XN_chairing_dims do begin  // init ..

                  chair_code:=22;    // XN  at knuckle

                  chair_outlong:=XN_chair_outlong*inscale; // adjust chair length later from rail separation
                  chair_inlong:=0;                         // adjust chair length later from rail separation

                  chair_halfwide:=XN_chair_halfwide*inscale;

                  chair_mid_halfwide_near:=XN_chair_mid_halfwide_near*inscale;
                  chair_mid_halfwide_far:=XN_chair_mid_halfwide_far*inscale;

                  inbolt_1x:=XN_inbolt_1x*inscale;
                  inbolt_2x:=XN_inbolt_2x*inscale;

                  outbolt_1x:=XN_outbolt_1x*inscale;
                  outbolt_2x:=XN_outbolt_2x*inscale;

                  bolts_from_end:=XN_bolts_from_end*inscale;

                  crad_inner:=XN_chair_crad_inner*inscale;
                  crad_outer:=XN_chair_crad_outer*inscale;
                end;//with

                with XA_chairing_dims do begin  // init ..

                  chair_code:=23;    // XA  at crossing gap

                  chair_outlong:=XA_chair_outlong*inscale;
                  chair_inlong:=0;                         // adjust chair length later from rail separation

                  chair_halfwide:=XA_chair_halfwide*inscale;

                  chair_mid_halfwide_near:=XA_chair_mid_halfwide_near*inscale;
                  chair_mid_halfwide_far:=XA_chair_mid_halfwide_far*inscale;

                  inbolt_1x:=XA_inbolt_1x*inscale;
                  inbolt_2x:=XA_inbolt_2x*inscale;

                  outbolt_1x:=XA_outbolt_1x*inscale;
                  outbolt_2x:=XA_outbolt_2x*inscale;

                  bolts_from_end:=XA_bolts_from_end*inscale;

                  crad_inner:=XA_chair_crad_inner*inscale;
                  crad_outer:=XA_chair_crad_outer*inscale;
                end;//with


                with FG_chairing_dims do begin  // init ..

                  chair_code:=98;

                  with rail_section_data_mm do begin
                    chair_inlong:=(rail_foot_width_mm-rail_head_width_mm)/2+inscale;     // +1" to chair edge
                    chair_outlong:=(rail_foot_width_mm+rail_head_width_mm)/2+inscale;
                  end;//with

                  chair_halfwide:=FG_chair_halfwide*inscale;

                  chair_mid_halfwide_near:=0;
                  chair_mid_halfwide_far:=0;

                  inbolt_1x:=FG_inbolt_1x*inscale;
                  inbolt_2x:=FG_inbolt_2x*inscale;

                  outbolt_1x:=FG_outbolt_1x*inscale;
                  outbolt_2x:=FG_outbolt_2x*inscale;

                  bolts_from_end:=FG_bolts_from_end*inscale;

                  crad_inner:=FG_chair_crad_inner*inscale;
                  crad_outer:=FG_chair_crad_outer*inscale;
                end;//with


                with RG_chairing_dims do begin  // init ..

                  chair_code:=99;

                  with rail_section_data_mm do begin
                    chair_inlong:=inscale;                          // 1" to chair edge
                    chair_outlong:=rail_head_width_mm+inscale;      // +1" to chair edge
                  end;//with

                  chair_halfwide:=RG_chair_halfwide*inscale;

                  chair_mid_halfwide_near:=0;
                  chair_mid_halfwide_far:=0;

                  inbolt_1x:=RG_inbolt_1x*inscale;
                  inbolt_2x:=RG_inbolt_2x*inscale;

                  outbolt_1x:=RG_outbolt_1x*inscale;
                  outbolt_2x:=RG_outbolt_2x*inscale;

                  bolts_from_end:=RG_bolts_from_end*inscale;

                  crad_inner:=RG_chair_crad_inner*inscale;
                  crad_outer:=RG_chair_crad_outer*inscale;
                end;//with


                          // add chair outlines and markers ...

                if (timb_str='S') and (csi.num_slide_chairs=0) then EXIT;  // temp only REA and straight switches yet. 241a



                if (timb_str='R') and (xtimbcl<trpx) then EXIT;            // temp not yet on return curve section

                yeq:=aq3offset(xtimbcl,dummy_k)/2;

                docurving(False,True,xtimbcl,yeq,x_curtimb,y_curtimb,k_curtimb,dummy);    // calc curving for the timber rotation point.

                x_curmod:=x_curtimb-xtimbcl;   // shift required to rotation point, to suit the curving..

                y_curmod:=y_curtimb-yeq;


                chairing_dims1:=null_chair;    // init no chairs on this timber..
                chairing_dims2:=null_chair;
                chairing_dims3:=null_chair;
                chairing_dims4:=null_chair;

                   // over-ride null with required chairs on this timber ...

                swap_s1j:=(ABS(xfs-xns-cpi.slwide_pi*inscale)>minfp);    // S1J on plain track if wider sleeper width

                pt_chairs:=(plain_track=True) or (timb_str='J') or (timb_str='A') or (timb_str='N') or (timb_str='R') or (timb_str='E');  // 244d plain track chairs on rails 1 and 3 only

                if use_all_FG_chairs=True      // code 98  FG dummy chairs for rail grooves ...   234a
                   then begin
                                                  chairing_dims1:=FG_chairing_dims;
                          if pt_chairs=False then chairing_dims2:=FG_chairing_dims;
                                                  chairing_dims3:=FG_chairing_dims;
                          if pt_chairs=False then chairing_dims4:=FG_chairing_dims;
                        end
                   else if use_all_RG_chairs=True   // code 99  RG dummy chairs for rail grooves ...   234a
                           then begin
                                                          chairing_dims1:=RG_chairing_dims;
                                  if pt_chairs=False then chairing_dims2:=RG_chairing_dims;
                                                          chairing_dims3:=RG_chairing_dims;
                                  if pt_chairs=False then chairing_dims4:=RG_chairing_dims;
                                end
                           else if use_all_bridge_chairs=True     // code 5  L1 bridge chairs (on waybeams)
                                   then begin
                                                                  chairing_dims1:=L1_chairing_dims;
                                          if pt_chairs=False then chairing_dims2:=L1_chairing_dims;
                                                                  chairing_dims3:=L1_chairing_dims;
                                          if pt_chairs=False then chairing_dims4:=L1_chairing_dims;
                                        end
                                   else begin   // others ...

                                               // code 1  ordinary chairs   default S1 chair on all 4 rails    some over-rides below ...

                                          if use_all_SC_chairs=True      // code 10  SC fictional  4-bolt chairs ...   237a
                                             then begin
                                                                            chairing_dims1:=SC_chairing_dims;
                                                    if pt_chairs=False then chairing_dims2:=SC_chairing_dims;
                                                                            chairing_dims3:=SC_chairing_dims;
                                                    if pt_chairs=False then chairing_dims4:=SC_chairing_dims;
                                                  end
                                             else begin                                                        // normal S1 default...
                                                                            chairing_dims1:=S1_chairing_dims;
                                                    if pt_chairs=False then chairing_dims2:=S1_chairing_dims;
                                                                            chairing_dims3:=S1_chairing_dims;
                                                    if pt_chairs=False then chairing_dims4:=S1_chairing_dims;
                                                  end;

                                          if (cpi.s1j_on_wing_pi=True) and (xtimbcl>(xtbzz-6*inscale)) and (xtimbcl<(xtbz+6*inscale))    // at wing front rail joint  6" extent arbitrary    239a
                                             then begin
                                                    chairing_dims2:=S1J_chairing_dims;
                                                    chairing_dims3:=S1J_chairing_dims;
                                                  end;

                                          if (timb_str='J')  // switch front over-ride        238a
                                             then begin
                                                    chairing_dims2:=null_chair;     // switch front rails 1 and 3 only
                                                    chairing_dims4:=null_chair;
                                                  end;

                                          if (timb_str='A') or (timb_str='N') or (timb_str='R')  // plain track over-rides
                                             then begin
                                                    if swap_s1j=True then chairing_dims1:=S1J_chairing_dims;   // S1J if wider sleeper width
                                                    chairing_dims2:=null_chair;                                // plain track/approach track -- rails 1 and 3 only
                                                    chairing_dims4:=null_chair;
                                                    if swap_s1j=True then chairing_dims3:=S1J_chairing_dims;
                                                  end;

                                          if (timb_str='E')    // exit track -- rails 1 and 3 only
                                             then begin
                                                    if swap_s1j=True then chairing_dims1:=S1J_chairing_dims;   // S1J if wider sleeper width and sleepers
                                                    chairing_dims2:=null_chair;
                                                    if swap_s1j=True then chairing_dims3:=S1J_chairing_dims;   // S1J if wider sleeper width and sleepers
                                                    chairing_dims4:=null_chair;
                                                  end;



                                            // see if L1 bridge chairs needed instead...


                                          if ( (timb_str='T') and (tbn>(csi.num_block_slide_chairs_in_T+csi.num_block_heel_chairs_in_T)) )
                                          or ( (timb_str='S') and (csi.num_slide_chairs>0) and ((csi.num_block_slide_chairs_in_T+csi.num_block_heel_chairs_in_T)=0)
                                               and (tbn>(csi.num_slide_chairs+csi.num_block_slide_chairs+csi.num_block_heel_chairs)) )
                                               
                                             then begin
                                                      // 1. calc space available on the turnout rail at switch heel (main rail has priority for ordinary chairs)

                                                    chair_space_y:=aq3offset(xtimbcl-S1_chair_halfwide*inscale,dummy_k)-g;  // calc for turnout side

                                                    if chair_space_y<(L1_chair_outlong*inscale+L1_chair_inlong*inscale+inscale/4)     // less than 1/4" between them
                                                       then begin  //EXIT;      // temp even bridge chairs conflict - needs M1 or bolted half-chairs.

                                                              heel_lengthx:=xtimbcl+9*inscale;     // temp 9" beyond last heel block chair  243a

                                                              chairing_dims1:=PLR3_chairing_dims;   // 241a temp use switch block chairs
                                                              chairing_dims4:=PLR3_chairing_dims;

                                                              chairing_dims2:=null_chair;
                                                              chairing_dims3:=null_chair;
                                                            end
                                                       else if chair_space_y<(S1_chair_outlong*inscale+S1_chair_inlong*inscale+inscale/2)  // require 1/2" space between them
                                                               then begin

                                                                        // need 1 or 2 L1 bridge chairs ...

                                                                      chairing_dims2:=L1_chairing_dims;     // crossing rails..
                                                                      chairing_dims3:=L1_chairing_dims;

                                                                      if chair_space_y<(S1_chair_outlong*inscale+L1_chair_inlong*inscale+inscale/2)  // calc for turnout rail, both chairs L1 ?      1/2" space needed
                                                                         then begin
                                                                                chairing_dims1:=L1_chairing_dims;     // stock rails..
                                                                                chairing_dims4:=L1_chairing_dims;
                                                                              end;

                                                                    end;
                                                            //end;
                                                  end;


                                          if (timb_str='T')
                                             then begin
                                                      // 2. calc space available between crossing rails at knuckle (main rail has priority for ordinary chairs)

                                                    chair_space_y:=g-aq2offset(xtimbcl+S1_chair_halfwide*inscale,dummy_k);  // calc space between S1 or S1J

                                                    if chair_space_y<(L1_chair_inlong*inscale*2+inscale/2)  // require 1/2" space between them
                                                       then begin
                                                                    // even bridge chairs conflict - needs knuckle chairs:

                                                              chairing_dims2:=null_chair;       // knuckle chairs on main rail only

                                                              if xtimbcl<(wingcx_minus-1.5*inscale)   // before start of knuckle bend on main rail   1.5" arbitrary
                                                                 then begin

                                                                          // find xtimbcl2 intersect of ZY chair centre-line (equalized at k3/2) on rail 2 ...

                                                                        get_xtimbcl2;

                                                                        try
                                                                          rail_sep:=(g-aq2offset(xtimbcl2,dummy_k))/COS(k3/2);   // 239a  rail separation along chair
                                                                        except
                                                                          rail_sep:=0;  // ???
                                                                        end;//try

                                                                        chairing_dims3:=ZY_chairing_dims;

                                                                        with chairing_dims3 do begin   // chair on rail 3  includes rail 2

                                                                           chair_inlong:=chair_outlong+rail_sep;    // add rail separation

                                                                        end;//with

                                                                      end
                                                                 else chairing_dims3:=null_chair; // ??? should be "X" timbers

                                                             end
                                                       else begin
                                                              if chair_space_y<(S1_chair_inlong*inscale*2+inscale/2)  // require 1/2" space between them
                                                                 then begin
                                                                            // need 1 or 2 L1 bridge chairs ...

                                                                        chairing_dims2:=L1_chairing_dims;     // L1 on turnout crossing rail.

                                                                        if chair_space_y<(S1_chair_inlong*inscale+L1_chair_inlong*inscale+inscale/2)  // both chairs need L1 ?
                                                                           then begin
                                                                                  chairing_dims3:=L1_chairing_dims;     // L1 on main crossing rail too.
                                                                                end;
                                                                      end;
                                                            end;

                                                  end;

                                                  // see if any more get over-ridden...

                                          if (csi.front_timbered=True) and (timb_str='J') and ( ((xtb-xorg)<(24*inscale)) or (cpi.s1j_on_j1_pi=True) )   // S1J joint chair(s) on switch front
                                             then begin
                                                          // code 7  S1J joint chairs

                                                    chairing_dims1:=S1J_chairing_dims;       // switch front rails 1 and 3 only
                                                    chairing_dims2:=null_chair;
                                                    chairing_dims3:=S1J_chairing_dims;
                                                    chairing_dims4:=null_chair;
                                                  end;

                                                // xing chairs - might extend past joint if exit timbered ...

                                          if (timb_str='X') and (xtb>(mvjp_nx-(mvj_sp+1)*inscale)) and (xtb<(mvjp_nx+(mvj_sp+1)*inscale))    // 233d   S1J joint chair on V-crossing exit timber   mvjp_nx=bnx+veelong   mvj_sp=12.5" back from rail end joint MVJP
                                             then begin
                                                          // code 7  S1J joint chairs  on all rails ..

                                                    chairing_dims1:=S1J_chairing_dims;
                                                    chairing_dims2:=S1J_chairing_dims;
                                                    chairing_dims3:=S1J_chairing_dims;
                                                    chairing_dims4:=S1J_chairing_dims;
                                                  end;


                                          if  (timb_str='S')
                                          and (csi.num_slide_chairs>0)
                                          and (tbn<=csi.num_slide_chairs)
                                             then begin
                                                         // code 2  P slide chairs ..

                                                    chairing_dims1:=P_chairing_dims;
                                                    chairing_dims2:=null_chair;       // remove S1 chairs on crossing rails..
                                                    chairing_dims3:=null_chair;
                                                    chairing_dims4:=P_chairing_dims;
                                                  end;


                                          if (timb_str='S')
                                         and (tbn>csi.num_slide_chairs)
                                         and (tbn<=(csi.num_slide_chairs+csi.num_block_slide_chairs+extra_opening))
                                             then begin

                                                    if tbn=(csi.num_slide_chairs+1) then heel_blankingx:=xtb-9*inscale;     // 9" in front of first heel block chair

                                                     // code 11    PLR1  switch block slide chairs  1P..2P

                                                    chairing_dims1:=PLR1_chairing_dims;
                                                    chairing_dims4:=PLR1_chairing_dims;

                                                    chairing_dims2:=null_chair;       // not on crossing rails   remove S1 chairs..
                                                    chairing_dims3:=null_chair;
                                                  end;


                                          if (timb_str='T')
                                         and (csi.num_block_slide_chairs_in_T>0)
                                         and (tbn<=csi.num_block_slide_chairs_in_T)
                                             then begin

                                                     // code 11    PLR1  switch block slide chairs  1P..2P  in "T" section ...

                                                    chairing_dims1:=PLR1_chairing_dims;
                                                    chairing_dims4:=PLR1_chairing_dims;

                                                    chairing_dims2:=null_chair;       // not on crossing rails   remove S1 chairs..
                                                    chairing_dims3:=null_chair;
                                                  end;


                                          if (timb_str='S')
                                         and (tbn>(csi.num_slide_chairs+csi.num_block_slide_chairs+extra_opening))
                                         and (tbn<=(csi.num_slide_chairs+csi.num_block_slide_chairs+csi.num_block_heel_chairs))
                                             then begin

                                                    if tbn=(csi.num_slide_chairs+csi.num_block_slide_chairs+csi.num_block_heel_chairs) then heel_lengthx:=xtimbcl+9*inscale;     // 9" beyond last heel block chair

                                                     // code 12    PLR3  switch block heel chairs  3P..11P

                                                    chairing_dims1:=PLR3_chairing_dims;
                                                    chairing_dims4:=PLR3_chairing_dims;


                                                    chairing_dims2:=null_chair;       // not on crossing rails   remove S1 chairs..
                                                    chairing_dims3:=null_chair;
                                                  end;


                                          if (timb_str='T')                          // straight switches...
                                         and (csi.num_block_heel_chairs_in_T>0)
                                         and (tbn>csi.num_block_slide_chairs_in_T)
                                         and (tbn<=(csi.num_block_slide_chairs_in_T+csi.num_block_heel_chairs_in_T))
                                             then begin

                                                    if tbn=(csi.num_block_slide_chairs_in_T+csi.num_block_heel_chairs_in_T) then heel_lengthx:=xtimbcl+9*inscale;    // 9" beyond last heel block chair

                                                     // code 12    PLR3  switch block heel chairs  3P..11P in "T" section

                                                    chairing_dims1:=PLR3_chairing_dims;
                                                    chairing_dims4:=PLR3_chairing_dims;


                                                    chairing_dims2:=null_chair;       // not on crossing rails   remove S1 chairs..
                                                    chairing_dims3:=null_chair;
                                                  end;




                                          if timb_str='X'
                                             then begin
                                                            // 239a  do L1 over-rides?

                                                    need_vee_chairs:=False;  // init
                                                    need_EF_chairs:=False;


                                                    if xtimbcl>fpx     // vee section
                                                       then begin
                                                              chair_space_y:=aq2offset(xtimbcl-S1_chair_halfwide*inscale,dummy_k)-g;  // calc space between S1 or S1J

                                                              get_xtimbcl2;                                               // 244a
                                                              wing_end_flares:=get_wing_rail_ends(xtimbcl,xtimbcl2);      // 244a

                                                              if chair_space_y<(L1_chair_outlong*inscale*2+inscale/2)  // require 1/2" space between them
                                                                 then begin
                                                                              // even bridge chairs conflict - needs crossing chairs:

                                                                         {if xing_lengthx=0 then} xing_lengthx:=xtimbcl+9*inscale;    // 9" beyond last heel block chair   243a

                                                                         chairing_dims2:=null_chair;       // V-crossing chairs are on rail 3 main rail

                                                                         if (chair_space_y<(12*inscale))     // 12" min space needed   arbitrary   for 2 loose jaws+keys
                                                                         or ( ( (wing_end_flares.ms_wing_chair<>0) or (wing_end_flares.ts_wing_chair<>0) ) and (cpi.temp_dd_parallel_wing_pi=True) ) // parallel wing over-ride
                                                                            then need_vee_chairs:=True
                                                                            else need_EF_chairs:=True;

                                                                       end
                                                                 else begin                                    // beyond crossing chairs...
                                                                        if chair_space_y<(S1_chair_outlong*inscale*2+inscale/2)  // require 1/2" space between them
                                                                           then begin
                                                                                  if ( (wing_end_flares.ms_wing_chair<>0) or (wing_end_flares.ts_wing_chair<>0) ) and (cpi.temp_dd_parallel_wing_pi=True)  // 244a
                                                                                     then begin                               // parallel wing over-ride
                                                                                            chairing_dims2:=null_chair;       // V-crossing chairs are on rail 3 main rail
                                                                                            need_vee_chairs:=True;
                                                                                          end
                                                                                     else begin
                                                                                                // need 1 or 2 L1 bridge chairs ...

                                                                                            //if xing_lengthx=0 then xing_lengthx:=xtimbcl-18*inscale;    // 18" in front of first L1

                                                                                            chairing_dims2:=L1_chairing_dims;     // L1 on turnout crossing rail

                                                                                            if chair_space_y<(S1_chair_outlong*inscale+L1_chair_outlong*inscale+inscale/2)  // both chairs need L1 ?
                                                                                               then begin
                                                                                                      chairing_dims3:=L1_chairing_dims;     // L1 on main crossing rails
                                                                                                    end;
                                                                                          end;

                                                                                end;
                                                                      end;

                                                            end;

                                                    if need_EF_chairs=True
                                                       then begin
                                                                      // find xtimbcl2 intersect of EF chair centre-line (equalized at k3/2) on rail 2 ...

                                                              get_xtimbcl2;

                                                              try
                                                                rail_sep:=(aq2offset(xtimbcl2,dummy_k)-g)/COS(k3/2);   // 239a  rail separation along chair centre-line
                                                              except
                                                                rail_sep:=0;  // ???
                                                              end;//try

                                                              chairing_dims3:=ZY_chairing_dims;  //dims same as ZY

                                                              with chairing_dims3 do begin   // chair on rail 3  includes rail 2

                                                                chair_code:=28;

                                                                chair_outlong:=chair_inlong+rail_sep;

                                                              end;//with

                                                            end;

                                                    if (xtimbcl<=fpx) or (need_vee_chairs=True)   // do all xing chairs from wing front forwards

                                                       then begin

                                                              get_xtimbcl2;

                                                              wing_end_flares:=get_wing_rail_ends(xtimbcl,xtimbcl2);      // 241a

                                                                // first check inner ends of conflicting chairs ...

                                                              chair_space_y:=g-aq2offset(xtimbcl+S1_chair_halfwide*inscale,dummy_k);  // calc space between S1

                                                              if chair_space_y<(L1_chair_inlong*inscale*2+inscale/2)  // require 1/2" space between them
                                                                 then begin
                                                                              // even bridge chairs conflict - needs knuckle chairs:

                                                                        if xing_blankingx=0 then xing_blankingx:=xtimbcl-9*inscale;     // 9" in front of first xing chair   243a

                                                                        chairing_dims2:=null_chair;       // knuckle chairs on main rail only

                                                                        //get_xtimbcl2;

                                                                        if xtimbcl<(wingcx_minus+1.5*inscale)   // before knuckle bend on main rail    ZY chair
                                                                                                                // 1.5" arbitrary overlap of ZY chair into knuckle bend
                                                                           then begin
                                                                                       // find xtimbcl2 intersect of ZY chair centre-line (equalized at k3/2) on rail 2 ...

                                                                                  //get_xtimbcl2;

                                                                                   try
                                                                                     rail_sep:=(g-aq2offset(xtimbcl2,dummy_k))/COS(k3/2);   // 239a  rail separation along chair centre-line
                                                                                   except
                                                                                     rail_sep:=0;  // ???
                                                                                   end;//try

                                                                                   chairing_dims3:=ZY_chairing_dims;

                                                                                   with chairing_dims3 do begin   // chair on rail 3  includes rail 2

                                                                                      chair_inlong:=chair_outlong+rail_sep;    // add rail separation

                                                                                   end;//with

                                                                                end
                                                                           else if xtimbcl<(wingcx_plus-1.5*inscale)   // within knuckle bend on main rail    XN chair
                                                                                                                        // 1.5" arbitrary overlap of XA chair into knuckle bend
                                                                                   then begin
                                                                                               // find xtimbcl2 intersect of XN chair centre-line (equalized at k3/2) on rail 2 ...

                                                                                          //get_xtimbcl2;

                                                                                          rail_sep:=(g-aq2offset(xtimbcl2,dummy_k)); // nominal rail separation at gauge lines

                                                                                          xnl_3:=xtimbcl-wingcx_minus;      // along knuckle on rail 3
                                                                                          xnl_2:=xtimbcl2-wingcx_minus;     // along knuckle on rail 2

                                                                                          chairing_dims3:=XN_chairing_dims;

                                                                                          with chairing_dims3 do begin   // chair on rail 3  includes rail 2

                                                                                            chair_inlong:=chair_outlong+rail_sep+xnl_2*SIN(k3/2);    // add rail separation

                                                                                            chair_outlong:=chair_outlong+xnl_3*SIN(k3/2);            // rail in knuckle bend is moving away from rail 3  gauge-line

                                                                                          end;//with

                                                                                        end

                                                                                   else if xtimbcl<(atx-(tbwide*inscale))    // XA gap chair   centre is between XN chair and XA timber in contact with the "A" timber
                                                                                           then begin
                                                                                                  xtimbcl2:=fpx-(fpx-xtimbcl)*COS(k3);  // assume regular crossing close to FP

                                                                                                  try
                                                                                                    rail_sep:=(g-aq2offset(xtimbcl2,dummy_k))/COS(k3/2);   // 239a  rail separation along chair centre-line     to gauge lines
                                                                                                  except
                                                                                                    rail_sep:=0;  // ???
                                                                                                  end;//try

                                                                                                  chairing_dims3:=XA_chairing_dims;

                                                                                                  with chairing_dims3 do begin   // chair on rail 3  includes rail 2

                                                                                                    chair_inlong:=chair_outlong+rail_sep+(xtimbcl2-wingx)*TAN(k3/2)*2;    // main-side wing rail

                                                                                                    chair_outlong:=chair_outlong+(xtimbcl-wingcx)*TAN(k3/2)*2;            // turnout-side wing rail

                                                                                                    chair_inlong:=chair_inlong+fw_tweak_xing;      // adjust for flangeway tweaks
                                                                                                    chair_outlong:=chair_outlong+fw_tweak_xing;

                                                                                                  end;//with

                                                                                                end

                                                                                           else if xtimbcl<(atx+15*inscale)            // AA nose chair
                                                                                                  then begin
                                                                                                         if cpi.slab_bracket_pi=True
                                                                                                            then do_nothing              // temp nyi

                                                                                                            else begin     // old-style cast AA chair -- use XA chair settings...

                                                                                                                    xtimbcl2:=fpx-(fpx-xtimbcl)*COS(k3);  // assume regular crossing close to FP

                                                                                                                    try
                                                                                                                      rail_sep:=(g-aq2offset(xtimbcl2,dummy_k))/COS(k3/2);   // 239a  rail separation along chair centre-line     to gauge lines
                                                                                                                    except
                                                                                                                      rail_sep:=0;  // ???
                                                                                                                    end;//try

                                                                                                                    chairing_dims3:=XA_chairing_dims;

                                                                                                                    with chairing_dims3 do begin   // chair on rail 3  includes rail 2

                                                                                                                      chair_code:=24;  // AA chair instead of XA

                                                                                                                      chair_inlong:=chair_outlong+rail_sep+(xtimbcl2-wingx)*TAN(k3/2)*2;    // main-side wing rail

                                                                                                                      chair_outlong:=chair_outlong+(xtimbcl-wingcx)*TAN(k3/2)*2;            // turnout-side wing rail

                                                                                                                      chair_inlong:=chair_inlong+fw_tweak_xing;      // adjust for flangeway tweaks
                                                                                                                      chair_outlong:=chair_outlong+fw_tweak_xing;

                                                                                                                    end;//with

                                                                                                                 end;
                                                                                                       end
                                                                                                  else if {((xtimbcl<wflarex) and (xtimbcl2<flarecx))}       // AB chair

                                                                                                          ((wing_end_flares.ms_wing_chair=2) and (wing_end_flares.ts_wing_chair=2)) // or (cpi.temp_dd_parallel_wing_pi=True) //or ((cpi.ms_parallel_wing_pi=True) and (cpi.ts_parallel_wing_pi=True))  // start of flares   AB splice/parallel chair
                                                                                                          then begin
                                                                                                                 //get_xtimbcl2;

                                                                                                                 // xtimbcl2:=fpx-(fpx-xtimbcl)*COS(k3);  // assume regular crossing close to FP

                                                                                                                   // AB chair ...

                                                                                                                 try
                                                                                                                   rail_sep:=(g-aq2offset(xtimbcl2,dummy_k))/COS(k3/2);   // 239a  rail separation along chair centre-line     to gauge lines
                                                                                                                 except
                                                                                                                   rail_sep:=0;  // ???
                                                                                                                 end;//try

                                                                                                                 chairing_dims3:=XA_chairing_dims;

                                                                                                                 with chairing_dims3 do begin   // chair on rail 3  includes rail 2

                                                                                                                   chair_code:=25;  // AB chair instead of XA

                                                                                                                   chair_inlong:=chair_outlong+rail_sep+(xtimbcl2-wingx)*TAN(k3/2)*2;    // main-side wing rail

                                                                                                                   chair_outlong:=chair_outlong+(xtimbcl-wingcx)*TAN(k3/2)*2;            // turnout-side wing rail

                                                                                                                   chair_inlong:=chair_inlong+fw_tweak_xing;      // adjust for flangeway tweaks
                                                                                                                   chair_outlong:=chair_outlong+fw_tweak_xing;

                                                                                                                 end;//with

                                                                                                               end
                                                                                                          else if (wing_end_flares.ms_wing_chair<>0) or (wing_end_flares.ts_wing_chair<>0)     //  BB/BC chair with flared wing rail one or both sides
                                                                                                                  then begin
                                                                                                                                 // BB/BC chair ...
                                                                                                                         try
                                                                                                                           rail_sep:=(g-aq2offset(xtimbcl2,dummy_k))/COS(k3/2);   // 239a  rail separation along chair centre-line     to gauge lines
                                                                                                                         except
                                                                                                                           rail_sep:=0;  // ???
                                                                                                                         end;//try

                                                                                                                         if (k3n<10.01)                                                                                                                                                   // up to 1:10 inclusive
                                                                                                                         and NOT ( (xtimbcl>(atx+45*inscale)) and ( (wing_end_flares.ms_wing_chair<>0) or (wing_end_flares.ts_wing_chair<>0) ) and (cpi.temp_dd_parallel_wing_pi=True) )  // 244a  over-ride narrow chairs for parallel-wing beyond BB
                                                                                                                            then chairing_dims3:=XA_chairing_dims       // BB chair up to 1:10   wider xing width
                                                                                                                            else chairing_dims3:=ZY_chairing_dims;      // BC chair over 1:10    normal width

                                                                                                                         with chairing_dims3 do begin   // chair on rail 3  includes rail 2

                                                                                                                           chair_code:=26;  // BB/BC chair

                                                                                                                           chair_inlong:=chair_outlong+rail_sep+(xtimbcl2-wingx)*TAN(k3/2)*2+wing_end_flares.ms_wing_add+inscale/4;;    // main-side wing rail     1/4" arbitrary to clear skewed slot

                                                                                                                           chair_outlong:=chair_outlong+(xtimbcl-wingcx)*TAN(k3/2)*2+wing_end_flares.ts_wing_add/COS(k3)+inscale/4;    // turnout-side wing rail   1/4" arbitrary to clear skewed slot

                                                                                                                           chair_inlong:=chair_inlong+fw_tweak_xing;      // adjust for flangeway tweaks
                                                                                                                           chair_outlong:=chair_outlong+fw_tweak_xing;

                                                                                                                         end;//with






                                                                                                                       end

                                                                                                                  else if ((wing_end_flares.ms_wing_chair=0) and (wing_end_flares.ts_wing_chair=0))      // CD/DD chair
                                                                                                                          then begin

                                                                                                                                          // find xtimbcl2 intersect of CD/DD chair centre-line (equalized at k3/2) on rail 2 ...

                                                                                                                                  get_xtimbcl2;

                                                                                                                                  try
                                                                                                                                    rail_sep:=(aq2offset(xtimbcl2,dummy_k)-g)/COS(k3/2);   // 239a  rail separation along chair centre-line
                                                                                                                                  except
                                                                                                                                    rail_sep:=0;  // ???
                                                                                                                                  end;//try

                                                                                                                                  chairing_dims3:=ZY_chairing_dims;  //dims same as ZY

                                                                                                                                  with chairing_dims3 do begin   // chair on rail 3  includes rail 2

                                                                                                                                    chair_code:=27;  // CD/DD chair  (CD has no space for middle DX jaws)

                                                                                                                                    chair_outlong:=chair_inlong+rail_sep;

                                                                                                                                  end;//with

                                                                                                                                  cd_yes:=(rail_sep<(7.5*inscale));   // 7.5" arbitrary, no middle DX outers

                                                                                                                               end
                                                                                                                          else chairing_dims3:=null_chair; // EF chairs already done


                                                                      end
                                                                 else begin
                                                                        if chair_space_y<(S1_chair_inlong*inscale*2+inscale/2)  // require 1/2" space between them
                                                                           then begin
                                                                                      // need 1 or 2 L1 bridge chairs ...

                                                                                  chairing_dims2:=L1_chairing_dims;     // L1 on turnout crossing rail..

                                                                                  if chair_space_y<(S1_chair_inlong*inscale+L1_chair_inlong*inscale+inscale/2)  // both chairs need L1 ?
                                                                                     then begin
                                                                                            chairing_dims3:=L1_chairing_dims;     // L1 on main crossing rails..
                                                                                          end;
                                                                                end;
                                                                      end;
                                                            end;

                                                  end;


                                          if (timb_str='K') or (timb_str='D')     // nyi K-crossing chairs   remove conflicting S1 chairs..
                                             then begin

                                                    if cpi.temp_wrong_k_xing_pi=True
                                                       then chairing_dims1:=S1_chairing_dims   // 244a temporary for customizing
                                                       else chairing_dims1:=null_chair;

                                                    chairing_dims2:=null_chair;
                                                    chairing_dims3:=null_chair;

                                                    if cpi.temp_wrong_k_xing_pi=True
                                                       then chairing_dims4:=S1_chairing_dims   // 244a temporary for customizing
                                                       else chairing_dims4:=null_chair;

                                                  end;


                                        end;

              end;//with 2-D chair data


              calculate_chairs_on_rails(timb_str+IntToStr(tbn),cd_yes);


            end;//exp_chairing

  finally
    keq:=save_keq;        // restore global.
  end;//try
end;
//_______________________________________________________________________________________________________________________________

procedure timberend(size:integer);            //  calc timber ends and equalizing angle.

  //   enter with xtb and return yns, yfs for centre of timber ends.
  //   size=0 is timber, size=1 is sleeper, size=2 is reduced-length sleeper (for crossover exit track)
  //   also enter with frackeq fraction of full equalizing angle required.

var
  k:extended;
  sliptimb_inc:extended;
  yaq3,rawns,rawfs,rawtblen,tblen6,tblen6int,tblen:extended;
  extended_for_slip:boolean;

begin
  yaq3:=aq3offset(xtb,k);       // offset to curved stock rail, gauge-face at this xtb. also returns angle in k.
  rawfs:=0-ynsnorm+yaq3;        // raw timber far-end to equalize it each side of stock rails.
  rawns:=ynsnorm;               // timber near-end.

         // extend timbers for tandem   218a  ...

  if (size=0) and (tandem_timb>0)
     then begin
            if tandem_timb=1
               then if xtb>dpx then rawns:=(g-rawfs)*3/4;     // =1  1st double-sided tandem turnout

            if tandem_timb=2
               then if xtb<dpx then rawns:=(g-rawfs)*1.7      // =2  2nd double-sided tandem turnout
                               else rawns:=(g-rawfs)*1.4;

            if tandem_timb=3
               then if xtb<dpx then rawfs:=rawfs+yaq3-g*0.75    // =3  1st single-sided tandem turnout
                               else rawfs:=rawfs+yaq3-g*0.6;

            if tandem_timb=4
               then rawns:=g*3+rawns*4-rawfs; //rawns+(g-rawfs)/2;                   


          end;

         // calc extension length of half_diamond timbers for slip...

  if (size=0) and (half_diamond=True) and (hd_timbers>0) and (xtb>setx) and (xtb<(toex+sliptipsl))
     then begin
            extended_for_slip:=True;
            try
              sliptimb_inc:=sliprad-SQRT(SQR(sliprad)-SQR(sliptipsl-(xtb-toex)));
            except
              extended_for_slip:=False;
              sliptimb_inc:=0;
            end;//try

            if (hd_timbers=1) or (hd_timbers=3) then rawfs:=rawfs+sliptimb_inc;    // TS or both.
            if (hd_timbers=2) or (hd_timbers=3) then rawns:=rawns-sliptimb_inc;    // MS or both.
          end
     else begin
            extended_for_slip:=False;
            sliptimb_inc:=0;
          end;

  tblen:=0;    // keep compiler happy.

   // mods 0.78.a   11-11-02... adjustable step size        was also for 00 gauge

  case size of
         0: begin                          // timber...
              if timbinc>minfp             // stepped timber lengths.
                 then begin
                        rawtblen:=rawfs-rawns;                // raw timber length.
                        tblen6:=(rawtblen)/inscale/timbinc;   // number of length increments in this timber length.
                        tblen6int:=INT(tblen6+0.67);          // round up to next increment if more than 1/3rd increment extra, otherwise down.
                                                              // ( 2" is 1/3rd of 6")
                        tblen:=tblen6int*timbinc*inscale;     // adjusted timber length in mm
                        maintimb:=1;                          // flag timber width
                        yfsnorm:=rawns+tblen;                 // return adjusted far end.

                      end
                 else begin
                        yfsnorm:=rawfs;                 // gradual steps, leave unadjusted.
                        tblen:=yfsnorm-rawns;
                        maintimb:=1;                    // flag timber width
                      end;
            end;

         1: begin
              tblen:=tb;        // standard sleeper.

              if (gaunt=False) or (xtb>=xorg) or (plain_track=True) // plain track 211c bug fix          //0.93.a
                 then yfsnorm:=rawns+tblen                                // return far end.
                 else yfsnorm:=rawns+tblen+gaunt_sleeper_mod_in*inscale;  // extended sleeper for gauntletted approach track.

              yaq3:=g;                 // for end centralizing.
              maintimb:=0;             // flag sleeper width.
            end;

         2: begin
              tblen:=tb/8;             // 1/8th of standard sleeper.
              yfsnorm:=rawns+tblen;    // return adjusted far end.
              yaq3:=g;                 // for end centralizing.
              maintimb:=0;             // flag sleeper width.
            end;

       else run_error(141);

  end;//case

  if (half_diamond=True) and (size=0)
     then begin
            if tradius_is_straight=True then keq:=k3/2   // regular diamond, timbers equalized-constant at half V-crossing angle.
                                        else keq:=k/2;   // 0.93.a irregular diamond, maintain timbers at half angle in stock rail.
          end
     else begin
            if (timbers_equalized=True) or (force_eq=True)  // 239b       // equalized style, rotate timbers...
               then begin
                      if (equalizing_fixed=True) and (frackeq<>0) // constant angle and not a square-on portion..
                         then keq:=k3/2                           // constant angle = half of crossing angle.
                         else keq:=k/2*frackeq;                   // angle is half gradient of curved stock rail, modified by the equalizing fraction in force for this timber.
                    end
               else begin                                     // square-on style...
                      if (square_on_angled=True) and (frackeq<>0)
                         then keq:=k    // to turnout road. (29-7-01 approximately square to turnout road, rotation point is not on outer rail)
                         else keq:=0;   // to main road.
                    end;
          end;

  yeq:=yaq3/2;           // centralize rotation point between stock rails.

  randk:=randangle/50;              // randomize angle by 0-.18 rads (approx 10 degrees).
  keq:=keq-randk+randk*Random*2;

        // centralize ends if wanted...

  eqendmod:=0;            // init amount of movement needed.
  if size<>2              // normal length sleeper or timber.
     then begin
            if ( (half_diamond=False) and (ms_ends=False) )  // turnout/plain track with centralized ends.
            or (half_diamond=True) and (tandem_timb<>4)      // half-diamond always centralized    unless partial in a tandem 218b
               then eqendmod:=0-rawns-(tblen-yaq3)/2;        // amount of centralizing mod needed.

                     // modify if extended for slip one side only...

            if (half_diamond=True) and (extended_for_slip=True) // half-diamond extended timbers for slip.
               then begin
                      case hd_timbers of
                             1: eqendmod:=eqendmod+sliptimb_inc/2;  // TS side.
                             2: eqendmod:=eqendmod-sliptimb_inc/2;  // MS side.
                      end;//case
                    end;
          end;

  randmod:=0-randend+randend*Random*2;      // end-randomizing amount

  yns:=rawns+randmod+eqendmod;            // return near end
  yfs:=yfsnorm+randmod+eqendmod;          // return far end

  ynsred:=yns+tbred;                        // and the reduced near end
  if size=2 then yfsred:=yfs
            else yfsred:=yfs-tbred;         // and the reduced far end.

  yns:=yns+tb_end_offset*inscale;      // 232a ..
  yfs:=yfs+tb_end_offset*inscale;

end;
//_______________________________________________________________________________________________________________________________

procedure set_plain_track(pt,clear_blanking:boolean);

var
  save_peg_code:integer;

begin
  with pad_form do begin
    if pt=True                       // set up for drawing plain track.
       then begin
              save_peg_code:=peg_code;

              if peg_rail=2 then peg_on_rail1_menu_entry.Click;     // no curved turnout rail.
              //if peg_rail=4 then peg_on_rail3_menu_entry.Click;   // no curved stock rail.
              if peg_rail=3 then peg_on_rail4_menu_entry.Click;     // no straight turnout rail.
              if peg_rail=9 then peg_on_rail8_menu_entry.Click;     // can't run peg up turnout road centre-line, put it on main-side centre-line.

              peg_code:=save_peg_code;                           // (the above clicks set code 20).
              startx:=0;                                         // init
              if clear_blanking=True then pt_blank:=0;           // init plain-track blanking  236b
              half_diamond:=False;                               // half-diamond not valid for plain track (loses the rails).
              hd_switch_timbering:=False;

              peg_on_rail2_menu_entry.Enabled:=False;
              peg_on_rail3_menu_entry.Enabled:=False;
              peg_on_rail9_menu_entry.Enabled:=False;

              peg_on_rail4_menu_entry.Caption:='on  running  rail  &4  ( TS )               Ctrl-Numpad-4';
              peg_on_rail1_menu_entry.Caption:='on  running  rail  &1  ( MS )              Ctrl-Numpad-1';

              crop_approach_menu_entry.Enabled:=False;
              blank_to_peg_menu_entry.Enabled:=False;      // no blanking for plain track.

              length_free_popup_entry.Enabled:=False;      // must stay locked to draw approach track...
              length_free_menu_entry.Enabled:=False;

              snap_to_catch_points_menu_entry.Enabled:=False;
              snap_to_MVJP_menu_entry.Enabled:=False;
              snap_exit_track_menu_entry.Enabled:=False;
              snap_to_heel_menu_entry.Enabled:=False;
              blank_to_toe_menu_entry.Enabled:=False;

              insert_turnout_menu_entry.Enabled:=True;
              lengths_rails_menu_entry.Enabled:=True;
              roll_rails_by_menu_entry.Enabled:=True;
              convert_to_plain_track_menu_entry.Enabled:=False;
              swap_facing_trailing_menu_entry.Enabled:=False;
              swap_current_facing_trailing_menu_entry.Enabled:=False;

              nearest_menu_entry.Enabled:=False;
              basic_turnout_menu_entry.Enabled:=False;

              turnout_i:=1;      // length locked at new turnoutx.

               // so can draw approach track...

              length_free_popup_entry.Enabled:=False;
              length_free_menu_entry.Enabled:=False;
            end
       else begin
              peg_on_rail2_menu_entry.Enabled:=True;
              peg_on_rail3_menu_entry.Enabled:=True;
              peg_on_rail9_menu_entry.Enabled:=True;

              peg_on_rail4_menu_entry.Caption:='on  turnout-road  stoc&k  rail            Ctrl-Numpad-4';
              peg_on_rail1_menu_entry.Caption:='on  main-road  &stock  rail               Ctrl-Numpad-1';

              crop_approach_menu_entry.Enabled:=True;
              blank_to_peg_menu_entry.Enabled:=True;

              snap_to_catch_points_menu_entry.Enabled:=True;
              snap_to_MVJP_menu_entry.Enabled:=True;
              snap_exit_track_menu_entry.Enabled:=True;
              snap_to_heel_menu_entry.Enabled:=True;
              blank_to_toe_menu_entry.Enabled:=True;


              insert_turnout_menu_entry.Enabled:=False;
              lengths_rails_menu_entry.Enabled:=False;
              roll_rails_by_menu_entry.Enabled:=False;
              convert_to_plain_track_menu_entry.Enabled:=True;
              swap_facing_trailing_menu_entry.Enabled:=True;
              swap_current_facing_trailing_menu_entry.Enabled:=True;

              nearest_menu_entry.Enabled:=True;
              basic_turnout_menu_entry.Enabled:=True;

              length_free_popup_entry.Enabled:=True;       // disabled for plain track..
              length_free_menu_entry.Enabled:=True;
            end;

  end;//with pad form

  railedges(gauge_faces,outer_edges,centre_lines);      // !!! 30-11-99 may need to put aq 25 back on.

  if shove_timber_form.Showing=True
     then begin
            current_shove_str:='';      // de-select any shoved timber.
            heave_selected_tb_str:='';  // and any clicked chair label
            shovetimbx:=0;
            shove_buttons(False,0,-1);
          end;

  enable_peg_positions;    // to enable/disable the peg options for Ctrl-# KB shortcuts.
end;
//_________________________________________________________________________________________

function check_infill_ok(str:string):boolean;   // ensure generator is on if he wants timber infill.

begin
  RESULT:=False;                // default init.

  if timbering_infill=False
     then begin
            if alert(3,'    show  timber  infill',
                       'It is not possible to show '+str+' infilled timbers for the control template while this feature remains switched off in the GENERATOR SETTINGS.',
                       '','','','','cancel','change  generator  setting  -  timber  infill  on',0)=5
                       then EXIT;

            pad_form.timbering_infill_menu_entry.Click;
            RESULT:=True;
          end
     else RESULT:=True;
end;
//__________________________________________________________________________________________

procedure adjacent_redraw;     //  do a re-draw after selecting/deselecting adjacent rails.

begin
  railedges(gauge_faces,outer_edges,centre_lines);
  redraw(True);               //  do a re-draw
end;
//_________________________________________________________________________________________

procedure clear_transform_data;

begin
  cancel_adjusts(False);
  xform:=0;
  yform:=0;
  kform:=0;
  xshift:=0;
  yshift:=0;
end;
//____________________________________________________________________________________________

procedure keep_colours1;           // scheme 1 colours for the keep form...

begin
  keep_paper_colour:=clWhite;  // 223a   was clSilver;
  keep_grid_colour:=clWhite;
  keep_timber_colour:=clOlive;
  keep_rail_colour:=clBlack;
  keep_mark_colour:=clGray;
end;
//________________________________________________________________________________________

procedure keep_colours2;           // scheme 2 colours for the keep form...

begin
  keep_paper_colour:=clWhite;
  keep_grid_colour:=clGray;
  keep_timber_colour:=clBlue;
  keep_rail_colour:=clBlack;
  keep_mark_colour:=clSilver;
end;
//________________________________________________________________________________________

procedure keep_colours3;           // scheme 3 colours for the keep form...

begin
  keep_paper_colour:=clWhite;  // 223a   was clSilver;
  keep_grid_colour:=clWhite;
  keep_timber_colour:=clGray;
  keep_rail_colour:=clBlack;
  keep_mark_colour:=clGray;
end;
//________________________________________________________________________________________

procedure set_pad_start_colours(change_flag:boolean);

                               // default pad (screen) colours  (not for printing)...
begin
  if (pad_cols_set=False) or (change_flag=True)  // not if already set unless overide asked for.
     then begin
            if hi_color=True                   // can we use 16-bit colours or better ?
               then pad_normal                 // yes.
               else begin                                   // no, 256 colours or less.
                      case start_colours of
                                      0: pad_silver;
                                      1: begin
                                           pad_bright;
                                           timber_colour:=clOlive;
                                           timber_infill_colour:=clMaroon;
                                           paper_colour:=clBlack;

                                           selection_colour:=clYellow;
                                           hover_colour:=clWhite;

                                           grid_colour:=clTeal;
                                           ring_colour:=clLime;
                                           ring_copy_colour:=clFuchsia;
                                           pad_form.Font.Color:=clSilver;
                                           preview_form.Font.Color:=clWhite;
                                           pad_form.bgnd_keeps_font_label.Font.Color:=clSilver;      // bgnd keeps.
                                           pad_form.pad_timber_font_label.Font.Color:=clSilver;      // timber numbers.

                                           shapes_colour:=clLime;
                                           shapes_label_font.Color:=shapes_colour;

                                           info_form.Color:=clBtnFace;
                                           bgkeep_timber_colour:=clGray;
                                           bgkeep_timberfill_colour:=clNavy;

                                           bgkeep_mark_colour:=clFuchsia;

                                           keep_colours1;               // colours for the keep form.
                                         end;
                                     -1: pad_all_grey;
                                    else run_error(153);
                      end;//case
                    end;
            ink_colour:=clBlack;                     // init current colour for drawing.
            pad_cols_set:=True;                      // colours have been set.
            pad_form.Color:=paper_colour;            // change the pad colour if nec.

            shapes_label_font.Color:=shapes_colour;

            shapes_infill_colour:=timber_infill_colour;  // 239a default
          end;
end;
//_________________________________________________________________________________________

procedure pad_normal;
                                       // set normal pad colours for Hi-Color...
begin
  paper_colour:=$00E0C0A0;             // a nice grey-blue for the drawing paper.
  grid_colour:=$00C0A080;              // with a darker blue for the screen grid.
  ring_colour:=$008000C0;              // mauve spacing-ring.
  ring_copy_colour:=$00FF0080;         // violet ring copies. // 0.98.a
  page_colour:=$00FF0080;              // and violet for the printer page outlines.
  pad_form.Font.Color:=clNavy;
  preview_form.Font.Color:=clBlack;

  selection_colour:=$000000B0;
  hover_colour:=clWhite;

  rail_colour:=clBlack;                // rail lines in black.
  timber_colour:=$000060A0;            // red-brown timbers (screen colour).
  timber_infill_colour:=$00D8D0B0;     //$00C0D090;     //$00B0D888;     // grey-green infill.

  shapes_colour:=$00205878;            // green-brown 214a  was  $0000FFD0;  lime  212a  was $006000A0;          // dark pink.
  shapes_label_font.Color:=shapes_colour;

  bgkeep_rail_colour:=clBlue;
  bgkeep_timber_colour:=$00608000;         // sage green.
  bgkeep_timberfill_colour:=$00C0C090;
  
  bgkeep_mark_colour:=clBlack;
  bgkeep_peg_colour:=clYellow;

  bgkeep_platform_colour:=$0060C0;   // tan 0.93.a

  guide_colour:=$00C0FFFF;             // cream for track centre-lines, planing marks and f.p. - 0.93.a and platform outlines
  align_colour:=$0000FFE0;             // rad ends (limey yellow).
  trans_colour:=clWhite;               // transition marks.
  joint_colour:=clBlue;                // rail joints in blue.

  info_form.Color:=$00F1E3D7;          // 0.93.a for Windows 7 child borders

  pad_form.bgnd_keeps_font_label.Font.Color:=$000000B0;  // brick red   // 208a was clMaroon;   // bgnd keeps.
  pad_form.pad_timber_font_label.Font.Color:=clBlue;     // timber numbers.

  pad_form.sb_black_white_menu_entry.Click;    // scalebar colours.

  keep_paper_colour:=clWhite;  // 223a   was $00FFF0D8;      // ice blue,  colours for the keep form...
  keep_grid_colour:=clTeal;

  keep_rail_colour:=clBlack;
  keep_mark_colour:=clGray;
  keep_timber_colour:=clMaroon;
end;
//__________________________________________________________________________________________

procedure pad_all_grey;
                                     // set pad colours for all-grey scheme...
begin
  paper_colour:=clWhite;
  grid_colour:=clSilver;
  ring_colour:=clBlack;
  ring_copy_colour:=clBlack;
  page_colour:=clGray;
  pad_form.Font.Color:=clBlack;
  preview_form.Font.Color:=clBlack;

  selection_colour:=clBlack;
  hover_colour:=clSilver;

  rail_colour:=clBlack;
  timber_colour:=clBlack;
  timber_infill_colour:=clSilver;

  shapes_colour:=clBlack;
  shapes_label_font.Color:=shapes_colour;

  bgkeep_rail_colour:=clGray;
  bgkeep_timber_colour:=clGray;
  bgkeep_timberfill_colour:=clSilver;

  bgkeep_mark_colour:=clBlack;
  bgkeep_peg_colour:=clBlue;

  bgkeep_platform_colour:=clSilver;   // 0.93.a

  guide_colour:=clBlack;
  joint_colour:=clBlack;
  align_colour:=clBlack;
  trans_colour:=clBlack;

  pad_form.bgnd_keeps_font_label.Font.Color:=clBlack;  // bgnd keeps.
  pad_form.pad_timber_font_label.Font.Color:=clBlack;  // timber numbers.

  pad_form.sb_teal_aqua_menu_entry.Click;              // scalebar colours.

  keep_colours3;              // colours for the keep form.
end;
//__________________________________________________________________________________________

procedure pad_silver;

begin
  paper_colour:=clSilver;
  grid_colour:=clGray;
  ring_colour:=clGreen;
  ring_copy_colour:=$00FF0080;         // violet ring copies. // 0.98.a
  page_colour:=clRed;
  pad_form.Font.Color:=clNavy;
  preview_form.Font.Color:=clBlack;

  selection_colour:=clGreen;
  hover_colour:=clYellow;

  rail_colour:=clBlack;
  timber_colour:=clMaroon;
  timber_infill_colour:=clYellow;

  shapes_colour:=clGreen;
  shapes_label_font.Color:=shapes_colour;

  bgkeep_rail_colour:=clBlue;
  bgkeep_timber_colour:=clPurple;
  bgkeep_timberfill_colour:=clWhite;

  bgkeep_mark_colour:=clRed;
  bgkeep_peg_colour:=clBlack;

  bgkeep_platform_colour:=$0000A0FF;   // 0.93.a  orange

  align_colour:=clRed;
  trans_colour:=clFuchsia;
  guide_colour:=clBlue;
  joint_colour:=clRed;

  pad_form.bgnd_keeps_font_label.Font.Color:=clBlue; // bgnd keeps.
  pad_form.pad_timber_font_label.Font.Color:=clBlue; // timber numbers.

  pad_form.sb_black_white_menu_entry.Click;     // scalebar colours.
  info_form.Color:=$00F1E3D7;  // 0.93.a for Windows 7 child borders

  keep_colours1;              // colours for the keep form.
end;
//________________________________________________________________________________________

procedure pad_bright;
                                     // set pad colours for the grey paper scheme...
begin
  paper_colour:=clGray;
  grid_colour:=clNavy;
  ring_colour:=clLime;
  ring_copy_colour:=clFuchsia;
  page_colour:=clSilver;
  pad_form.Font.Color:=clSilver;
  preview_form.Font.Color:=clBlack;

  selection_colour:=clWhite;
  hover_colour:=clYellow;

  rail_colour:=clAqua;
  timber_colour:=clPurple;
  timber_infill_colour:=clOlive;

  shapes_colour:=clPurple;
  shapes_label_font.Color:=shapes_colour;

  bgkeep_rail_colour:=clLime;
  bgkeep_timber_colour:=clBlue;
  bgkeep_timberfill_colour:=clTeal;

  bgkeep_mark_colour:=clMaroon;
  bgkeep_peg_colour:=clYellow;

  bgkeep_platform_colour:=$0000A0FF;   // 0.93.a  orange

  align_colour:=clFuchsia;
  trans_colour:=clLime;
  guide_colour:=clYellow;
  joint_colour:=clLime;

  pad_form.bgnd_keeps_font_label.Font.Color:=clWhite; // bgnd keeps.
  pad_form.pad_timber_font_label.Font.Color:=clWhite; // timber numbers.

  pad_form.sb_grid_paper_menu_entry.Click;        // scalebar colours.
  info_form.Color:=$00F1E3D7;  // 0.93.a for Windows 7 child borders

  keep_colours1;              // colours for the keep form.
end;
//_______________________________________________________________________________________

procedure no_rails_warn;

begin
  with pad_form do begin
    if (centre_lines_menu_entry.Checked=False) and (stock_rails_menu_entry.Checked=False)
       then begin
              alert(3,'    no  outer  rails  or  track  centre - lines',
                      '||You have switched off the generation of all outer rail and track centre-line elements.'
                     +'||It may not be possible to print this template, and it may not be drawn correctly in the storage box.'
                     +'||To print a template without any rails or centre-lines, switch them back on in the GENERATOR > GENERATOR SETTINGS > menu options,'
                     +' and then select instead the|PRINT > PRINTED ELEMENT OPTIONS > OMIT RAILS menu item.',
                      '','','','','','O K',0);
            end;
  end;//with
end;
//________________________________________________________________________________________
                  
procedure pad_caption(text:string);

begin
  pad_form.Caption:='        trackpad  :   '+text;   // 0.91.b  was trackpad
end;
//________________________________________________________________________________________

procedure caption_add(text:string);       // replace '...' in pad caption with '=' + text.

var
  find:integer;

begin
  find:=Pos('..',pad_form.Caption);                              // find  '..
  if find=0 then find:=Pos(' |',pad_form.Caption);               // not there so find the ' |' we put there.
  if find=0 then find:=Pos(' =',pad_form.Caption);               // not there so find the ' =' we put there.
  if find<20 then find:=50;                                      // default
  if (roam_mod=1) or (roll_mod=1) or (ringdia_mod=1)
     then pad_form.Caption:= Copy(pad_form.Caption,1,find-1)+' |  '+text
     else pad_form.Caption:= Copy(pad_form.Caption,1,find-1)+' =  '+text;
end;
//______________________________________________________________________________________

procedure copy_keep(keep_info:Ttemplate_info);   // get control template data from a keep.

var
  exact_flag:boolean;
  custom_flag:boolean;
  n:integer;
  y_offset:extended;

begin

  highlit_symbol:=-1;  // 227a reset if showing

  with keep_info.keep_dims.box_dims1 do begin

    exact_flag:=gauge_exact;   //nyi ignored in version 0  // If true this is an exact-scale template.
    custom_flag:=gauge_custom; //nyi ignored in version 0  // If true this is (or was when saved) a custom gauge setting.

    total_template_timber_length:=total_length_of_timbering;                // 226a
    total_template_sleeper_length:=align_info.total_length_of_sleepering;   // 226a

    template_number_of_timbers:=align_info.file_number_of_timbers;          // 226a
    template_number_of_sleepers:=align_info.file_number_of_sleepers;        // 226a

    with rail_info do begin

      flare_type:=flared_ends_ri;  // 0=straight bent  1=straight machined.

      knuckle_code:=knuckle_code_ri;       // 214a  integer;   0=normal, -1=sharp, 1=use knuckle_radius_ri
      knuckle_radius:=knuckle_radius_ri;   // 214a  extended;

        // rail switches...

      track_centre_lines_flag:=track_centre_lines_sw;

      switch_drive_flag:=switch_drive_sw;  // 0.82.a

      isolated_crossing:=isolated_crossing_sw;    // 217a

      custom_switch_opening:=custom_switch_opening_infile;        // 233a
      use_custom_switch_opening:=use_custom_switch_opening_flag;  // 233a

      turnout_road_stock_rail_flag:=   turnout_road_stock_rail_sw;
      turnout_road_check_rail_flag:=   turnout_road_check_rail_sw;
      turnout_road_crossing_rail_flag:=turnout_road_crossing_rail_sw;
      crossing_vee_flag:=              crossing_vee_sw;
      main_road_crossing_rail_flag:=   main_road_crossing_rail_sw;
      main_road_check_rail_flag:=      main_road_check_rail_sw;
      main_road_stock_rail_flag:=      main_road_stock_rail_sw;

      k_diagonal_side_check_rail_flag:=k_diagonal_side_check_rail_sw;
      k_main_side_check_rail_flag:=k_main_side_check_rail_sw;


    end;//with

    railedges(gauge_faces,outer_edges,centre_lines);   // use these switches.

    cpi:=proto_info;            // get all the gauge data.

    ccd:=check_diffs;           // get all the check-rail diffs 0.94.a

    retain_diffs_on_make:=retain_diffs_on_make_flag;    // 0.94.a check rail diffs
    retain_diffs_on_mint:=retain_diffs_on_mint_flag;    // 0.94.a check rail diffs

          // 0.94.a timber shoving mods..

    retain_shoves_on_make:=retain_shoves_on_make_flag;
    retain_shoves_on_mint:=retain_shoves_on_mint_flag;

          // 213a  for crossing entry straight

    retain_entry_straight_on_make:=retain_entry_straight_on_make_flag;
    retain_entry_straight_on_mint:=retain_entry_straight_on_mint_flag;

    rail_section:=rail_type;               // rail head only or head+foot(BH/FB).
    vertical_rails:=uninclined_rails;      // True = rails vertical.

    fb_kludge:=fb_kludge_template_code;    // 0.94.a  FB rail-foot kludge

    inherited_map_col:=print_mapping_colour; // 227a
    inherited_mark_col:=pad_marker_colour;   // 227a

    inherited_use_mapping:=use_print_mapping_colour;  // 227a
    inherited_use_marker:=use_pad_marker_colour;      // 227a

    label_modx:=mod_text_x;    // 211b not used for control template, but retained for use when stored again
    label_mody:=mod_text_y;    // 211b ditto

    with transform_info do begin

      y_datum:=datum_y;                   // y datum (green dot).

      xform:=x1_shift;                    //  mm    shift info...
      yform:=y1_shift;                    //  mm

      kform:=k_shift;                     //  radians.
      normalize_kform;

      xshift:=x2_shift;                   //  mm
      yshift:=y2_shift;                   //  mm

      pegx:=peg_pos.x;      //  mm  peg position.
      pegy:=peg_pos.y;

      peg_code:=peg_point_code;
      if peg_code=-2 then peg_code:=-1;    // so peg on joints can re-initialise.

      peg_rail:=peg_point_rail;


      with exp_info do begin    // experimental stuff

        normal_chair_key_max_offset:=chair_key_max_offset_infile;    // 233a

        fw_modify_for_gw:=fw_modify_for_gw_infile;  // 239a modify check flangeway for gauge-widening

        slider1_str:=slider1_str_infile;    // 244a ...
        slider2_str:=slider2_str_infile;
        slider3_str:=slider3_str_infile;
        slider4_str:=slider4_str_infile;
        slider5_str:=slider5_str_infile;

      end;//with

      timbering_brick:=timbering_brick_flag;         // 234a

      use_all_SC_chairs:=use_all_SC_chairs_flag; // 237a

      use_all_RG_chairs:=use_all_RG_chairs_flag;     // 234a
      use_all_FG_chairs:=use_all_FG_chairs_flag;     // 234a

    end;//with

    with platform_trackbed_info do begin   // 0.93.a was  Tcheck_rail_mints=record

      adjacent_edges:=adjacent_edges_keep;    // False=adjacent tracks,  True=trackbed edges and platform edges.

      draw_ms_trackbed_edge:=draw_ms_trackbed_edge_keep;
      draw_ts_trackbed_edge:=draw_ts_trackbed_edge_keep;

      draw_ts_platform:=draw_ts_platform_keep;
      draw_ts_platform_start_edge:=draw_ts_platform_start_edge_keep;
      draw_ts_platform_end_edge:=draw_ts_platform_end_edge_keep;
      draw_ts_platform_rear_edge:=draw_ts_platform_rear_edge_keep;

      platform_ts_front_edge_ins:=platform_ts_front_edge_ins_keep;      // centre-line to platform front edge 57 inches 4ft-9in  215a            was 2ft-4.3/4in
      platform_ts_start_width_ins:=platform_ts_start_width_ins_keep;
      platform_ts_end_width_ins:=platform_ts_end_width_ins_keep;

      platform_ts_start_mm:=platform_ts_start_mm_keep;
      platform_ts_length_mm:=platform_ts_length_mm_keep;


      draw_ms_platform:=draw_ms_platform_keep;
      draw_ms_platform_start_edge:=draw_ms_platform_start_edge_keep;
      draw_ms_platform_end_edge:=draw_ms_platform_end_edge_keep;
      draw_ms_platform_rear_edge:=draw_ms_platform_rear_edge_keep;


      platform_ms_front_edge_ins:=platform_ms_front_edge_ins_keep;      // centre-line to platform front edge 57 inches  215a
      platform_ms_start_width_ins:=platform_ms_start_width_ins_keep;
      platform_ms_end_width_ins:=platform_ms_end_width_ins_keep;

      platform_ms_start_mm:=platform_ms_start_mm_keep;
      platform_ms_length_mm:=platform_ms_length_mm_keep;


      platform_ms_start_skew_mm:=platform_ms_start_skew_mm_keep;    // 207a
      platform_ms_end_skew_mm:=platform_ms_end_skew_mm_keep;        // 207a

      platform_ts_start_skew_mm:=platform_ts_start_skew_mm_keep;    // 207a
      platform_ts_end_skew_mm:=platform_ts_end_skew_mm_keep;        // 207a


            // new trackbed edge functions 215a ...   split MS and TS settings  -  using Single floats to fit available file space ...

      trackbed_ms_width_ins:=trackbed_ms_width_ins_keep;     // Single
      trackbed_ts_width_ins:=trackbed_ts_width_ins_keep;     // Single

      cess_ms_width_ins:=cess_ms_width_ins_keep;             // Single
      cess_ts_width_ins:=cess_ts_width_ins_keep;             // Single

      draw_ms_trackbed_cess_edge:=draw_ms_trackbed_cess_edge_keep;   // boolean
      draw_ts_trackbed_cess_edge:=draw_ts_trackbed_cess_edge_keep;   // boolean


      trackbed_ms_start_mm:=trackbed_ms_start_mm_keep;       // extended   need to be extendeds for def_req
      trackbed_ms_length_mm:=trackbed_ms_length_mm_keep;

      trackbed_ts_start_mm:=trackbed_ts_start_mm_keep;
      trackbed_ts_length_mm:=trackbed_ts_length_mm_keep;


    end;//with platform_trackbed_info


    with align_info do begin

      spiral:=trans_flag;      // True=transition, False=fixed radius curving.

      y_offset:=rad_offset;

      nomrad:=fixed_rad+y_offset;       // fixed radius mm.     (include any offset from old files pre 0.64.a)...
      nomrad1:=trans_rad1+y_offset;     // first transition radius mm.
      nomrad2:=trans_rad2+y_offset;     // second transition radius mm.

      tst:=trans_length;       // length of transition mm.
      os:=trans_start;         // start of transition mm.

      slewing:=slewing_flag;   // slewing flag.              // !!! replacing Tspares 10-7-99...
      slew_s:=slew_start;      // slewing zone start mm.
      slew_l:=slew_length;     // slewing zone length mm.
      slew:=slew_amount;       // amount of slew mm.

      slew2_kmax:=tanh_kmax;           {:double;}  {spare_int1:integer;}   // stretch factor for mode 2 slews.
                                                   {spare_int2:integer;}   // !!! double used because only 8 bytes available in existing file format (2 integers).
      slew_mode:=slew_type;             {:byte;}   {spare_flag3:boolean;}  // !!! byte used because only 1 byte available in existing file format 1-11-99.

      cl_only:=cl_only_flag;   // for bgnd centre-line only.

      dummy_template:=dummy_template_flag;  // 212a

      cl_options_code:=cl_options_code_int;                   // 206a
      cl_options_custom_offset:=cl_options_custom_offset_ext; // 206a

      if reminder_flag=True       // 216a
         then begin

                with jotter_form.jotter_memo.Lines do begin

                  Add('');
                  Add('_______________________');
                  Add('');
                  Add(DateToStr(Date)+'   '+TimeToStr(Time)+'   discarded reminder:');
                  Add('');
                  Add(reminder_str);

                end;//with
              end;

      osk_match:=osk_match_kn;       // 226a


    end;//with

    with turnout_info1 do begin

      plain_track:=plain_track_flag;               //  True = plain track only.
      hand_i:=hand;                                //  hand of turnout.
      timbers_equalized:=timbering_flag;           //  True = equalized timbering.

      exittb_i:=exit_timbering;           //  exit timbering style.

      include_front_timbers:=front_timbers_flag;      //  218a
      include_switch_timbers:=switch_timbers_flag;    //  218a
      include_closure_timbers:=closure_timbers_flag;  //  218a
      include_xing_timbers:=xing_timbers_flag;        //  218a

      approach_rails_only:=approach_rails_only_flag;  // 218a

      rolled_in_sleepered:=rolled_in_sleepered_flag;  //223a


                // compatibility mods 211a ...

      if turnout_road_is_adjustable=True
         then turnout_road_i:=2                    // adjustable turnout road exit
         else if turnout_road_is_minimum=True
                 then turnout_road_i:=3                    // minimum turnout road exit     217a
                 else turnout_road_i:=turnout_road_code;   // length of turnout exit road.

      turnoutx:=turnout_length;           //  mm overall length.
      xorg:=origin_to_toe;                //  mm approach length.
      incx:=step_size;                    //  (use saved step-size on reloading - not default).

      turnoutx_max:=xy_pts_c*incx;        // limit overall length.

    end;//with

    if turnoutx>turnoutx_max then turnoutx:=turnoutx_max;
    if xorg>turnoutx then xorg:=turnoutx;

  end;//with

  with keep_info.keep_dims.turnout_info2 do begin

      equalizing_fixed:=equalizing_fixed_flag;     //   1-4-00
      no_timbering:=no_timbering_flag;             //   7-9-00

      exp_chairing:=chairing_flag;                        // 214a
      use_all_bridge_chairs:=use_all_bridge_chairs_flag;  // 230a

      key_direction:=key_direction_infile;           // 233a  chair keys   0=random, 1=forwards, -1=backwards
      keys_towards_joint:=keys_towards_joint_flag;   // 233a  plain track keys driven towards rail joints

      square_on_angled:=angled_on_flag;            // 29-7-01.
      bontimb:=bonus_timber_count;                 // 0.76.a  23-10-01.

      auto_diamond:=diamond_auto_code;                   // 0.77.a 27-8-02...
      timbinc:=timber_length_inc;                        // 0.78.a 11-11-02.
      hd_proto_timbering:=diamond_proto_timbering_flag;

      hd_switch_timbering:=diamond_switch_timbering_flag;  // 213a

      half_diamond:=semi_diamond_flag;
      fixed_diamond:=diamond_fixed_flag;   // N.B. fixed diamond will be reset in calc_switch.

      turnout_road_endx:=turnout_road_endx_infile;   // 209a

      gaunt:=gaunt_flag;                       // 0.93.a ex 0.81
      gaunt_offset_in:=gaunt_offset_inches;    // 0.93.a ex 0.81

      startx:=start_draw_x;               //  {spare_float3:extended;}  turnout startx  3-11-99

      with plain_track_info do begin

        if (pt_custom=True) or (list_index>4)
           then begin
                  pt_i:=plain_track_form.plain_track_spacings_listbox.Items.Count-1;     // list index for current custom plain track.
                  railen[pt_i]:=rail_length;                                    // custom rail length in inches.
                  sleeper_count[pt_i]:=sleepers_per_length;                     // number of sleepers per length.
                  for n:=0 to psleep_c do psleep[pt_i,n]:=sleeper_centres[n];   // custom spacings.

                  plain_track_form.plain_track_spacings_listbox.Items.Strings[pt_i]:='  '+Trim(pt_spacing_name_str);   // put name in the list.
                end
           else pt_i:=list_index;           // copy data if custom, otherwise use index into existing list.


        udpegx:=user_pegx;    // user-defined peg data (here to use former spare floats in file)
        udpegy:=user_pegy;
        udpegangle:=user_pegk;
        udpeg_valid:=user_peg_data_valid;
        udpeg_rail:=user_peg_rail;

        rjcode:=rail_joints_code;   // 0=normal, 1=staggered, -1=none (cwr).

        tb_roll_percent:=pt_tb_rolling_percent;

        gaunt_sleeper_mod_in:=gaunt_sleeper_mod_inches;   // 0.93.a ex 0.81

        pt_blank:=pt_blanking_length;  // 229a

        waybeams_template:=waybeams_template_flag;

      end;//with plain_track_info

                  // switch stuff...

      if set_csi_from_switch_info(switch_info)=False  // set current switch from supplied info.
         then begin
                if set_csi_data(2,2)=False     // set REA B default if copied data invalid.
                   then run_error(82);         // ?????? no B switch in list?
              end;

                  // crossing stuff...

      with crossing_info do begin
        case pattern of
                 -1: begin xing_type_i:=-1; retpar_i:=0; end;  // generic crossing.
                  0: begin xing_type_i:=0;  retpar_i:=0; end;  // straight crossing...
                  1: begin xing_type_i:=1;  retpar_i:=0; end;  // curviform V-crossing...
                  2: begin xing_type_i:=0;  retpar_i:=1; end;  // parallel crossing...
                else begin xing_type_i:=0;  retpar_i:=0; end;  // safety ! (default straight crossing)...
        end;//case

        if (xing_type_i<>0) and (peg_code=108) then peg_code:=0;   // added 205e  not a regular crossing   108=CESP

        entry_straight_code:=   sl_mode;       // 0=auto_fit, 1=use fixed_sl, -1=short
        xing_ret_i:=  retcent_mode;      // 0=return centres as adjacent track, 1=use custom centres.
        k3n:=         k3n_unit_angle;    // k3n angle in units.
        hdkn:=        hdkn_unit_angle;   // K-crossing angle in units. // 0.93.a
        fixed_sl:=    fixed_st;          // length of knuckle straight. mm.

        hd_timbers:=      hd_timbers_code;     // extending of timbers for slip road.
        hd_vcheck_rails:= hd_vchecks_code;     // shortening code for half-diamond v-crossing check rails.

        kck1_long:=   k_check_length_1;  // length of size 1 k-crossing check rail (inches).
        kck2_long:=   k_check_length_2;  // length of size 2 k-crossing check rail (inches).

        k_flare_len:=k_check_flare;     // length of flare on k-crossing check rails. inches F-S

        curviform_timbering:=curviform_timbering_keep;   // 215a

        simplified_short_angle:=simplified_short_angle_keep;  // 228a

        generic_was_short_swapped:=generic_was_short_swapped_keep;  // 228a

              // 0.75.a  9-10-01...

        bn_wide:=     blunt_nose_width;      // full-size inches.
        bn_to_a:=     blunt_nose_to_timb;    // full-size inches - to A timber centre.

        veetimb_sp:=  vee_timber_spacing;    // full-size inches - timber spacing for vee point rail part of crossing (on from "A").
        wingtimb_sp:= wing_timber_spacing;   // full-size inches - timber spacing for wing rail front part of crossing (up to "A").

        mvj_sp:=      vee_joint_half_spacing;   // full-size inches - rail overlap at vee point rail joint.
        wingj_sp:=    wing_joint_spacing;       // full-size inches - timber spacing at wing rail joint.


                   // number of timbers spanned by vee rail incl. "A" timber...

        vee_spco1:=   vee_joint_space_co1;
        vee_spco2:=   vee_joint_space_co2;
        vee_spco3:=   vee_joint_space_co3;
        vee_spco4:=   vee_joint_space_co4;
        vee_spco5:=   vee_joint_space_co5;
        vee_spco6:=   vee_joint_space_co6;

                   // number of timbers spanned by wing rail front excl. "A" timber...

        wing_spco1:=  wing_joint_space_co1;
        wing_spco2:=  wing_joint_space_co2;
        wing_spco3:=  wing_joint_space_co3;
        wing_spco4:=  wing_joint_space_co4;
        wing_spco5:=  wing_joint_space_co5;
        wing_spco6:=  wing_joint_space_co6;

                   // 0.95.a  K-crossing wing rails ...

        k_custom_wing_long:=k_custom_wing_long_keep;    // 0.95.a inches full-size k-crossing wing rails
        k_custom_point_long:=k_custom_point_long_keep;  // 0.95.a inches full-size k-crossing point rails   NYI

        use_k_custom_wing_rails:=use_k_custom_wing_rails_keep;    // 0.95.a
        use_k_custom_point_rails:=use_k_custom_point_rails_keep;  // 0.95.a  NYI

        main_road_endx:=main_road_endx_infile;   // 217a
        main_road_i:=main_road_code;             // 217a

        tandem_timb:=tandem_timber_code;         // 218a

      end;//with crossing_info

      omit_tspt_marks:=omit_ts_plain_track_joints;    // 227a  also for turnout approach and exit track
      omit_mspt_marks:=omit_ms_plain_track_joints;    // 227a  also for turnout approach and exit track

      omit_swfj_marks:=omit_switch_front_joints;  // 0.79.a  25-02-03
      omit_swrj_marks:=omit_switch_rail_joints;
      omit_skj_marks:=omit_stock_rail_joints;
      omit_wj_marks:=omit_wing_rail_joints;
      omit_vj_marks:=omit_vee_rail_joints;

      omit_tsxj_marks:=omit_ts_exit_joints;    // 227a
      omit_msxj_marks:=omit_ms_exit_joints;    // 227a

      omit_kx_marks:=omit_k_crossing_stock_rail_joints;

  end;//with turnout_info2

  copy_shove_array(False,keep_info.file_blocks.keep_shoved_timbers,current_shoved_timbers);

  copy_symbols_array(False,keep_info.file_blocks.keep_symbols,current_symbols);

  current_jaw_options_set:=Copy(keep_info.file_blocks.keep_jaw_options);  // 239a

  copy_jaws_array(False,keep_info.keep_jaws,current_jaws);     // 237c
  copy_seats_array(False,keep_info.keep_seats,current_seats);  // 237c
  copy_keys_array(False,keep_info.keep_keys,current_keys);     // 237c
  copy_jmods_array(False,keep_info.keep_jmods,current_jmods);  // and the jmods  237c

    // got all new data.

  //------------------------------

  if exp_chairing=False then heave_chairs_form.Hide;  // 244a

  with pad_form do begin

    if spiral=True then zone_normal_menu_entry.Checked:=True;          // radio item - normal length adjust action.

    turnout_i:=1;      // length locked at turnoutx.

    set_plain_track(plain_track,False);

    snap_exit_to_return_curve_menu_entry.Enabled:=(retpar_i=1);

    with peg_indicator_panel do begin
      case peg_code of
         -2: Caption:='J';
         -1: Caption:='S';
          0: Caption:='0';
          1: Caption:='1';
   2,22,222: Caption:='2';
       3,33: Caption:='N';   // 234d  was 3
       4,44: Caption:='4';
          6: Caption:='5';
          9: Caption:='7';
         11: Caption:='9';
         17: Caption:='8';
         18: Caption:='6';
         20: Caption:='F';
        610: Caption:='3';  // 234d peg on blanked length   was N
        999: Caption:='U';
        else Caption:='N';
      end;//case
    end;//with

    set_xing_k_i;             // set selector list index to match current k3n.

    gauge_dims(False,False,False);  // data from cg current gauge, not from the gauge list (ignore return).

    if (cl_only=False) and (gen_centre_lines_were_off=True) and (centre_lines_menu_entry.Checked=True)
       then begin
              centre_lines_menu_entry.Checked:=False;    // toggle centre-lines off (not needed for this one)...
              centre_lines:=False;
              railedges(gauge_faces,outer_edges,centre_lines);
              gen_centre_lines_were_off:=False;                  // clear the flag again.
            end;

    if (cl_only=True) and (centre_lines_menu_entry.Checked=False)  // toggles on click
       then begin
              centre_lines_menu_entry.Checked:=True;     // make sure centre-lines are on.
              centre_lines:=True;
              railedges(gauge_faces,outer_edges,centre_lines);
              gen_centre_lines_were_off:=True;      // so we can switch them off again on next call.
            end;

  end;//with form

     // 218b  update gauge list ...

  for n:=0 to gauge_form.gauge_listbox.Items.Count-1 do begin

    if gauge[n].name_str_glist=cpi.name_str_pi
       then begin
              gauge_i:=n;
              BREAK;
            end;
  end;//next

  locatorx:=xorg;     // 227a  abitrary start locator at CTRL-1

end;
//______________________________________________________________________________

function any_control_rails_omitted:boolean;       // 208a

var
  pt_all,turnout_all,hd_all:boolean;

begin
  RESULT:=False;  // init

  pt_all:=turnout_road_stock_rail_flag
      and main_road_stock_rail_flag;

  turnout_all:=pt_all
           and turnout_road_check_rail_flag
           and turnout_road_crossing_rail_flag
           and crossing_vee_flag
           and main_road_check_rail_flag
           and main_road_crossing_rail_flag;

  hd_all:=turnout_all
      and k_diagonal_side_check_rail_flag
      and k_main_side_check_rail_flag;

  if plain_track=True then RESULT:= NOT pt_all
                      else if half_diamond=True then RESULT:= NOT hd_all
                                                else RESULT:= NOT turnout_all;

end;
//______________________________________________________________________________

function create_id_number_str(idnum,hand:integer; startx,turnoutx,ipx,fpx:extended; plain_track,half_diamond,any_omitted:boolean):string;    // 208a

var
  hand_str,prefix_str:string;

begin
  if plain_track=True
     then prefix_str:='P'
     else begin
            if half_diamond=True
               then begin
                      if turnoutx<fpx then prefix_str:='C'      // custom partial template
                                      else prefix_str:='D';     // full half-diamond
                    end
               else begin
                      if turnoutx<ipx then prefix_str:='S'   // switch or catch points
                                      else prefix_str:='T';  // full turnout
                    end;

            if startx>ipx then prefix_str:='C';    // blanking overide -- custom partial template
          end;

  if any_omitted=True then prefix_str:='C';  // overide -- custom partial template

  if hand=1 then hand_str:='L'
            else hand_str:='R';

  RESULT:=prefix_str+hand_str+FormatFloat('000',idnum);
end;
//______________________________________________________________________________

procedure fill_kd(var keep_info:Ttemplate_info);  // copy control template data to the keep record.

var
  n:integer;
  rand_label_factor:extended;

begin

  FillChar(keep_info.keep_dims, SizeOf(keep_info.keep_dims),0);    // all unused bytes to zero.  26-6-00

  with keep_info.keep_dims.box_dims1 do begin

    now_time:=time_now_modified(Random($7FFFFFFF));    // modify Delphi float time format to integer.
                                                       // if conversion problem, returns random integer.

    keep_date:=DateToStr(Date);
    keep_time:=TimeToStr(Time);

    top_label:=Copy(info_form.gauge_label.Caption,1,99);

    this_was_control_template:=False;  // 0.93.a  this is modified later in save_box() on the last one added there.

    reference_string:=Copy(current_name_str,1,99);    // template name.

    id_number:=highest_id_number+1;    // 208a

    id_number_str:=create_id_number_str(id_number,hand_i,startx,turnoutx,ipx,fpx,plain_track,half_diamond,any_control_rails_omitted);   // 208a

    total_length_of_timbering:=total_template_timber_length;                // 0.96.a  for box totals info
    align_info.total_length_of_sleepering:=total_template_sleeper_length;   // 226a

    align_info.file_number_of_timbers:=template_number_of_timbers;          // 226a
    align_info.file_number_of_sleepers:=template_number_of_sleepers;        // 226a

    with rail_info do begin

      flared_ends_ri:=flare_type;  // 0=straight bent  1=straight machined.

      knuckle_code_ri:=knuckle_code;       // 214a  integer;   0=normal, -1=sharp, 1=use knuckle_radius_ri
      knuckle_radius_ri:=knuckle_radius;   // 214a  extended;


      track_centre_lines_sw:=track_centre_lines_flag;

      switch_drive_sw:=switch_drive_flag;  // 0.82.a

      isolated_crossing_sw:=isolated_crossing;   // 217a

      custom_switch_opening_infile:=custom_switch_opening;        // 233a
      use_custom_switch_opening_flag:=use_custom_switch_opening;  // 233a

      turnout_road_stock_rail_sw:=   turnout_road_stock_rail_flag;
      turnout_road_check_rail_sw:=   turnout_road_check_rail_flag;
      turnout_road_crossing_rail_sw:=turnout_road_crossing_rail_flag;
      crossing_vee_sw:=              crossing_vee_flag;
      main_road_crossing_rail_sw:=   main_road_crossing_rail_flag;
      main_road_check_rail_sw:=      main_road_check_rail_flag;
      main_road_stock_rail_sw:=      main_road_stock_rail_flag;

      k_diagonal_side_check_rail_sw:=k_diagonal_side_check_rail_flag;
      k_main_side_check_rail_sw:=k_main_side_check_rail_flag;

    end;//with

    auto_restore_on_startup:=False;    // defaults - changed on saving the backup (these two only read from the first keep in the file)..
    ask_restore_on_startup:=True;

    bgnd_code_077:=0;                  // can't go on background until in keeps box.
    pre077_bgnd_flag:=False;           // in case reloaded in older version than 0.77.a

    templot_version:=program_version;  // program version number (*100, e.g Templot v:1.3 = 130).

    gauge_index:=gauge_i;            // index into the gauge list. Only for showing the list,
                                     // the data comes from the file.

    if gauge_i=gauge_form.gauge_listbox.Items.Count-1
       then gauge_exact:=True                            // nyi // If true this is an exact-scale template.
       else gauge_exact:=False;

    if (gauge_i<gauge_form.gauge_listbox.Items.Count-1) and (gauge_i>gauge_form.gauge_listbox.Items.Count-6)  // 4 custom slots -2, -3, -4, -5.

       then gauge_custom:=True   // nyi // If true this is (or was when saved) a custom gauge setting.
       else gauge_custom:=False;

    proto_info:=cpi;              // all the current gauge-size dimensions.

    check_diffs:=ccd;             // all the check-rail diffs. 0.94.a

    retain_diffs_on_make_flag:=retain_diffs_on_make;    // 0.94.a check rail diffs
    retain_diffs_on_mint_flag:=retain_diffs_on_mint;    // 0.94.a check rail diffs

          // 0.94.a timber shoving mods..

    retain_shoves_on_make_flag:=retain_shoves_on_make;
    retain_shoves_on_mint_flag:=retain_shoves_on_mint;

          // 213a  for crossing entry straight

    retain_entry_straight_on_make_flag:=retain_entry_straight_on_make;
    retain_entry_straight_on_mint_flag:=retain_entry_straight_on_mint;


    box_save_done:=False;        // default, changed on saving backup, read only from first keep on restore previous contents. 23-6-00 v:0.62.a //spare_flag1:boolean;

                  // name label position modifiers..

       // 211b mods...

    if (control_room_form.previous_labels_menu_entry.Checked=True)   // 211b option
    or ((GetAsyncKeyState(VK_CONTROL) AND -2)<>0)                    // or CTRL-Key down
        then begin
              mod_text_x:=label_modx;    // 211b put label back where it was
              mod_text_y:=label_mody;
            end
       else begin
                      // 0.82.a randomise label position (if currently on default)...

              if control_room_form.fixed_labels_menu_entry.Checked=True
                 then begin
                        mod_text_x:=0;
                        mod_text_y:=0;
                      end
                 else begin
                        if control_room_form.very_random_labels_menu_entry.Checked=True
                           then rand_label_factor:=3.0
                           else rand_label_factor:=1.0;

                           mod_text_x:=((Random*30)-15)*scale*rand_label_factor;  // 0.82.a up to 15ft either way of randomising. arbitrary.
                           mod_text_y:=((Random*12)-4)*scale*rand_label_factor;   // 0.82.a add -4ft to +8ft of randomising. arbitrary.
                      end;
            end;

    with transform_info do begin

      datum_y:=y_datum;                   // y datum (green dot).

      x_go_limit:=minfp;                  // nyi // print cropping limits (paper inches)...
      x_stop_limit:=minfp;                // nyi


      transforms_apply:=True;             // !!! no longer used. False=ignore transform data.

                                          // transform data...

      x1_shift:=xform;                    //  mm    (xform,yform always zero after a normalize,)
      y1_shift:=yform;                    //  mm    (e.g. for keeps, etc. (but not roll-back).  )
      k_shift:=kform;                     //  radians.
      x2_shift:=xshift;                   //  mm
      y2_shift:=yshift;                   //  mm

      peg_pos.x:=pegx;                    //  mm  peg position.
      peg_pos.y:=pegy;

      peg_point_code:=peg_code;
      peg_point_rail:=peg_rail;

      mirror_on_x:=False;                 //  nyi // True= invert on x.
      mirror_on_y:=False;                 //  nyi // True= invert on y. (swap hand).


      with exp_info do begin    // experimental stuff

        chair_key_max_offset_infile:=normal_chair_key_max_offset;    // 233a

        fw_modify_for_gw_infile:=fw_modify_for_gw;  // 239a modify check flangeway for gauge-widening

        slider1_str_infile:=slider1_str;    // 244a ...
        slider2_str_infile:=slider2_str;
        slider3_str_infile:=slider3_str;
        slider4_str_infile:=slider4_str;
        slider5_str_infile:=slider5_str;


      end;//with

    end;//with

    with platform_trackbed_info do begin   // 0.93.a was  Tcheck_rail_mints=record

              adjacent_edges_keep:=adjacent_edges;    // False=adjacent tracks,  True=trackbed edges and platform edges.

              draw_ms_trackbed_edge_keep:=draw_ms_trackbed_edge;
              draw_ts_trackbed_edge_keep:=draw_ts_trackbed_edge;

              draw_ts_platform_keep:=draw_ts_platform;
              draw_ts_platform_start_edge_keep:=draw_ts_platform_start_edge;
              draw_ts_platform_end_edge_keep:=draw_ts_platform_end_edge;
              draw_ts_platform_rear_edge_keep:=draw_ts_platform_rear_edge;

              platform_ts_front_edge_ins_keep:=platform_ts_front_edge_ins;      // centre-line to platform front edge 57 inches 215a
              platform_ts_start_width_ins_keep:=platform_ts_start_width_ins;
              platform_ts_end_width_ins_keep:=platform_ts_end_width_ins;

              platform_ts_start_mm_keep:=platform_ts_start_mm;
              platform_ts_length_mm_keep:=platform_ts_length_mm;


              draw_ms_platform_keep:=draw_ms_platform;
              draw_ms_platform_start_edge_keep:=draw_ms_platform_start_edge;
              draw_ms_platform_end_edge_keep:=draw_ms_platform_end_edge;
              draw_ms_platform_rear_edge_keep:=draw_ms_platform_rear_edge;


              platform_ms_front_edge_ins_keep:=platform_ms_front_edge_ins;      // centre-line to platform front edge 57 inches 215a
              platform_ms_start_width_ins_keep:=platform_ms_start_width_ins;
              platform_ms_end_width_ins_keep:=platform_ms_end_width_ins;

              platform_ms_start_mm_keep:=platform_ms_start_mm;
              platform_ms_length_mm_keep:=platform_ms_length_mm;


              platform_ms_start_skew_mm_keep:=platform_ms_start_skew_mm;    // 207a
              platform_ms_end_skew_mm_keep:=platform_ms_end_skew_mm;        // 207a

              platform_ts_start_skew_mm_keep:=platform_ts_start_skew_mm;    // 207a
              platform_ts_end_skew_mm_keep:=platform_ts_end_skew_mm;        // 207a

                    // new trackbed edge functions 215a ...   split MS and TS settings  -  using Single floats to fit available file space ...

              trackbed_ms_width_ins_keep:=trackbed_ms_width_ins;     // Single
              trackbed_ts_width_ins_keep:=trackbed_ts_width_ins;     // Single

              cess_ms_width_ins_keep:=cess_ms_width_ins;             // Single
              cess_ts_width_ins_keep:=cess_ts_width_ins;             // Single

              draw_ms_trackbed_cess_edge_keep:=draw_ms_trackbed_cess_edge;   // boolean
              draw_ts_trackbed_cess_edge_keep:=draw_ts_trackbed_cess_edge;   // boolean


              trackbed_ms_start_mm_keep:=trackbed_ms_start_mm;       // extended   need to be extendeds for def_req
              trackbed_ms_length_mm_keep:=trackbed_ms_length_mm;

              trackbed_ts_start_mm_keep:=trackbed_ts_start_mm;
              trackbed_ts_length_mm_keep:=trackbed_ts_length_mm;

    end;//with platform_trackbed_info


    with align_info do begin

      curving_flag:=True;      // no longer used 0.77.a - all templates curved (straight=max_rad).

      trans_flag:=spiral;      // True=transition, False=fixed radius curving.

      fixed_rad:=nomrad;       // fixed radius mm.
      trans_rad1:=nomrad1;     // first transition radius mm.
      trans_rad2:=nomrad2;     // second transition radius mm.
      trans_length:=tst;       // length of transition mm.
      trans_start:=os;         // start of transition mm.
      rad_offset:=0;{ycurv;}   // curving line offset mm.   // scrapped 26-7-00.

      slewing_flag:=slewing;   // slewing flag.             // !!! replacing Tspares 10-7-99...
      slew_start:=slew_s;      // slewing zone start mm.
      slew_length:=slew_l;     // slewing zone length mm.
      slew_amount:=slew;       // amount of slew mm.

      try
        tanh_kmax:=slew2_kmax;     // ! double from extended..
        slew_type:=slew_mode;      // ! byte from integer.
      except
                              // in case of overflows...
        tanh_kmax:=2;
        slew_type:=1;
      end;//try

      cl_only_flag:=cl_only;    // for bgnd centre-line only.

      dummy_template_flag:=dummy_template;  // 212a

      cl_options_code_int:=cl_options_code;                   // 206a
      cl_options_custom_offset_ext:=cl_options_custom_offset; // 206a

      reminder_flag:=False;          // 216a  defaults no reminder yet
      reminder_colour:=clYellow;
      reminder_str:='';

      osk_match_kn:=osk_match;            // 226a

    end;//with

    rail_type:=rail_section;               // rail head only or head+foot(BH/FB).
    uninclined_rails:=vertical_rails;      // True = rails vertical.

    fb_kludge_template_code:=fb_kludge;    // 0.94.a  FB rail-foot kludge

    print_mapping_colour:=inherited_map_col; // 227a
    pad_marker_colour:=inherited_mark_col;   // 227a

    if keep_form.use_inherited_colours_menu_entry.Checked=True
       then begin
              use_print_mapping_colour:=inherited_use_mapping;  // 227a
              use_pad_marker_colour:=inherited_use_marker;      // 227a
            end
       else begin
              use_print_mapping_colour:=False;  // 227a
              use_pad_marker_colour:=False;     // 227a
            end;

    disable_f7_snap:=False;           // 0.82.a  14-10-06  default.

    with turnout_info1 do begin

      plain_track_flag:=plain_track;            //  True = plain track only.
      hand:=hand_i;                             //  hand of turnout.

      timbering_flag:=timbers_equalized;        //  True = equalized timbering.

      exit_timbering:=exittb_i;           //  exit timbering style.

      front_timbers_flag:=include_front_timbers;      //  218a
      switch_timbers_flag:=include_switch_timbers;    //  218a
      closure_timbers_flag:=include_closure_timbers;  //  218a
      xing_timbers_flag:=include_xing_timbers;        //  218a

      approach_rails_only_flag:=approach_rails_only;  // 218a

      rolled_in_sleepered_flag:=rolled_in_sleepered;  // 223a

          // compatibility mods 211a  217a ...

      if turnout_road_i=2          // adjustable turnout road exit
         then begin
                turnout_road_code:=0;               // so can be loaded in 208 and earlier ( =2 will crash)
                turnout_road_is_adjustable:=True;
              end
         else if turnout_road_i=3  // minimum turnout road exit   217a
                 then begin
                        turnout_road_code:=0;               // so can be loaded in 208 and earlier ( =3 will crash)
                        turnout_road_is_minimum:=True;
                      end

                 else begin
                        turnout_road_code:=turnout_road_i;    //  length of turnout exit road.
                        turnout_road_is_adjustable:=False;
                        turnout_road_is_minimum:=False;       // 217a
                      end;

      turnout_length:=turnoutx;           //  mm overall length.
      origin_to_toe:=xorg;                //  mm approach length.

      step_size:=incx;                    //  (use saved step-size on reloading - not default).

    end;//with turnout_info1

    timbering_brick_flag:=timbering_brick;       // 234a

    use_all_SC_chairs_flag:=use_all_SC_chairs;   // 237a

    use_all_RG_chairs_flag:=use_all_RG_chairs;   // 234a
    use_all_FG_chairs_flag:=use_all_FG_chairs;   // 234a

  end;//with

  with keep_info.keep_dims.turnout_info2 do begin

      equalizing_fixed_flag:=equalizing_fixed;  // {spare_flag1:boolean;} equalizing style 1-4-00
      no_timbering_flag:=no_timbering;          // {spare_flag2:boolean;} 7-9-00

      chairing_flag:=exp_chairing;                        // 214a
      use_all_bridge_chairs_flag:=use_all_bridge_chairs;  // 230a

      key_direction_infile:=key_direction;           // 233a  chair keys   0=random, 1=forwards, -1=backwards
      keys_towards_joint_flag:=keys_towards_joint;   // 233a  plain track keys driven towards rail joints

      angled_on_flag:=square_on_angled;         // 29-7-01.
      bonus_timber_count:=bontimb;              // 0.76.a  23-10-01.

      diamond_auto_code:=auto_diamond;                  // 0.77.a 27-8-02...
      timber_length_inc:=timbinc;                       // 0.78.a 1-11-02.
      diamond_proto_timbering_flag:=hd_proto_timbering;

      diamond_switch_timbering_flag:=hd_switch_timbering;  // 213a

      semi_diamond_flag:=half_diamond;
      diamond_fixed_flag:=fixed_diamond;   // N.B. fixed diamond will be reset in calc_switch.

      turnout_road_endx_infile:=turnout_road_endx;   // 209a

      gaunt_flag:=gaunt;                      // 0.93.a ex 081
      gaunt_offset_inches:=gaunt_offset_in;   // 0.93.a ex 081

      start_draw_x:=startx;                     //  {spare_float3:extended;}  turnout startx  3-11-99.

      smallest_radius_stored:=smallest_radius;  // 208a needed for box data -- not loaded to the control

      dpx_stored:=dpx;    // 208a needed for ID number creation -- not loaded to the control
      ipx_stored:=ipx;    // 208a needed for ID number creation -- not loaded to the control
      fpx_stored:=fpx;    // 208a needed for ID number creation -- not loaded to the control

      with plain_track_info do begin

        if pt_i>4 then pt_custom:=True       // list index for custom plain track.
                  else pt_custom:=False;

        list_index:=pt_i;
        rail_length:=railen[pt_i];                                    // rail length in inches (only used for custom lengths).
        sleepers_per_length:=sleeper_count[pt_i];                     // number of sleepers per length.
        for n:=0 to psleep_c do sleeper_centres[n]:=psleep[pt_i,n];   // spacings (only used for custom spacings).

        pt_spacing_name_str:=Copy(plain_track_form.plain_track_spacings_listbox.Items.Strings[pt_i],1,198);   // get name from the list.

        user_pegx:=udpegx;    // user-defined peg data (here to use former spare floats in file)
        user_pegy:=udpegy;
        user_pegk:=udpegangle;
        user_peg_data_valid:=udpeg_valid;
        user_peg_rail:=udpeg_rail;

        rail_joints_code:=rjcode;   // 0=normal, 1=staggered, -1=none (cwr).

        pt_tb_rolling_percent:=tb_roll_percent;

        gaunt_sleeper_mod_inches:=gaunt_sleeper_mod_in;   // 0.93.a ex 0.81

        pt_blanking_length:=pt_blank;  // 229a

        waybeams_template_flag:=waybeams_template;

      end;//with plain_track_info

              // switch stuff ..

      switch_info:=csi;  // current switch.

              // crossing stuff...

      with crossing_info do begin

        if retpar_i=1 then pattern:=2
                      else pattern:=xing_type_i;  // 0=straight, 1=curviform, 2=parallel, -1=generic.

        sl_mode:=          entry_straight_code;   // 0=auto_fit, 1=use fixed_sl, -1=short
        retcent_mode:=     xing_ret_i;  // 0=return centres as adjacent track, 1=use custom centres.
        k3n_unit_angle:=   k3n;         // k3n angle in units.
        hdkn_unit_angle:=  hdkn;        // K-crossing angle in units. 0.93.a
        fixed_st:=         fixed_sl;    // length of knuckle straight. mm.

        hd_timbers_code:=  hd_timbers;       // extending of timbers for slip road.
        hd_vchecks_code:=  hd_vcheck_rails;  // shortening code for half-diamond v-crossing check rails.

        k_check_length_1:= kck1_long;   // length of size 1 k-crossing check rail (inches).
        k_check_length_2:= kck2_long;   // length of size 2 k-crossing check rail (inches).

        k_check_flare:=k_flare_len;  // length of flare on k-crossing check rails. inches F-S

        curviform_timbering_keep:=curviform_timbering;   // 215a

        simplified_short_angle_keep:=simplified_short_angle;  // 228a

        generic_was_short_swapped_keep:=generic_was_short_swapped;  // 228a

            // 0.75.a  9-10-01...

        blunt_nose_width:=bn_wide;         // full-size inches.
        blunt_nose_to_timb:=bn_to_a;       // full-size inches - to A timber centre.

        vee_timber_spacing:=veetimb_sp;    // full-size inches - timber spacing for vee point rail part of crossing (on from "A").
        wing_timber_spacing:=wingtimb_sp;  // full-size inches - timber spacing for wing rail front part of crossing (up to "A").

        vee_joint_half_spacing:=mvj_sp;    // full-size inches - rail overlap at vee point rail joint.
        wing_joint_spacing:=wingj_sp;      // full-size inches - timber spacing at wing rail joint.

                   // number of timbers spanned by vee rail incl. "A" timber...

        vee_joint_space_co1:=vee_spco1;
        vee_joint_space_co2:=vee_spco2;
        vee_joint_space_co3:=vee_spco3;
        vee_joint_space_co4:=vee_spco4;
        vee_joint_space_co5:=vee_spco5;
        vee_joint_space_co6:=vee_spco6;

                   // number of timbers spanned by wing rail front excl. "A" timber...

        wing_joint_space_co1:=wing_spco1;
        wing_joint_space_co2:=wing_spco2;
        wing_joint_space_co3:=wing_spco3;
        wing_joint_space_co4:=wing_spco4;
        wing_joint_space_co5:=wing_spco5;
        wing_joint_space_co6:=wing_spco6;

                   // 0.95.a  K-crossing wing rails ...

        k_custom_wing_long_keep:=k_custom_wing_long;    // 0.95.a inches full-size k-crossing wing rails
        k_custom_point_long_keep:=k_custom_point_long;  // 0.95.a inches full-size k-crossing point rails   NYI

        use_k_custom_wing_rails_keep:=use_k_custom_wing_rails;    // 0.95.a
        use_k_custom_point_rails_keep:=use_k_custom_point_rails;  // 0.95.a  NYI

        main_road_endx_infile:=main_road_endx;   // 217a
        main_road_code:=main_road_i;             // 217a

        tandem_timber_code:=tandem_timb;         // 218a

      end;//with crossing_info

      omit_ts_plain_track_joints:=omit_tspt_marks;    // 227a  also for turnout approach and exit track
      omit_ms_plain_track_joints:=omit_mspt_marks;    // 227a  also for turnout approach and exit track

      omit_switch_front_joints:=omit_swfj_marks;  // 0.79.a  25-02-03
      omit_switch_rail_joints:=omit_swrj_marks;
      omit_stock_rail_joints:=omit_skj_marks;
      omit_wing_rail_joints:=omit_wj_marks;
      omit_vee_rail_joints:=omit_vj_marks;

      omit_ts_exit_joints:=omit_tsxj_marks;    // 227a
      omit_ms_exit_joints:=omit_msxj_marks;    // 227a

      omit_k_crossing_stock_rail_joints:=omit_kx_marks;

  end;//with turnout_info2

  keep_info.notch_info:=get_peg_for_notch;   // 227d

  keep_info.file_blocks.keep_jaw_options:=Copy(current_jaw_options_set);  // 239a

  copy_shove_array(False,current_shoved_timbers,keep_info.file_blocks.keep_shoved_timbers);  // copy all the current shoved timber data to the keep

  copy_symbols_array(False,current_symbols,keep_info.file_blocks.keep_symbols);              // and the symbols

  copy_jaws_array(False,current_jaws,keep_info.keep_jaws);    // and the jaws   237c
  copy_seats_array(False,current_seats,keep_info.keep_seats); // and the seats  237c
  copy_keys_array(False,current_keys,keep_info.keep_keys);    // and the keys   237c
  copy_jmods_array(False,current_jmods,keep_info.keep_jmods); // and the jmods  237c

   // copy timber lengths...

  SetLength(keep_info.keep_timber_counts,timb_len_list.Count);

  if timb_len_list.Count>0
     then begin
            for n:=0 to timb_len_list.Count-1 do begin

              keep_info.keep_timber_counts[n].num_str:=timb_len_list.Strings[n];
              keep_info.keep_timber_counts[n].number:=Ttimber_count_object(timb_len_list.Objects[n]).numco;

            end;//next
          end;
end;
//______________________________________________________________________________

function pad_view_fit_bgnd(who,brick_col:integer):boolean;  // zoom to fit background templates.

  // who = which templates? 0=all, 1=group only, 2=brick templates matching brick_col only (DXF 3-D exports)  234a

var
  n:integer;
  max_long,max_wide:extended;
  min_long,min_wide:extended;
  wl_factor:extended;
  margin_factor:extended;

  reduced_screeny:extended;   // 217a

  brick_exists:boolean;  // 234a

begin
  RESULT:=False;        // init

  cancel_adjusts(False);

    // mods 216c for increased size of top toolbar ...

  if ABS(fy)<minfp then EXIT;   // ??  div 0

  reduced_screeny:=screeny-pad_form.top_toolbar_panel.Height/ABS(fy);  // 217a

  if ABS(reduced_screeny)<minfp then EXIT;    // ??  div 0

  wl_factor:=screenx/reduced_screeny;

  brick_exists:=False;  // init


  if (who=0) or ( (who=1) and (any_selected=0) )      // include control template and if no group
     then begin
            max_wide:=y_datum*100+xy_max[1];  // init for control template in 1/100th mm...
            max_long:=xy_max[0];

            min_wide:=y_datum*100+xy_min[1];
            min_long:=xy_min[0];
          end
     else begin                                 // group or brick only
            max_wide:=0-screenx_max*100;        // init max neg.
            max_long:=0-screenx_max*100;

            min_wide:=screenx_max*100;          // init max pos.
            min_long:=screenx_max*100;
          end;


  if any_bgnd>0      // any in list and on background?
     then begin
            for n:=0 to (keeps_list.Count-1) do begin

              with Ttemplate(keeps_list.Objects[n]) do begin

                if bg_copied=False then CONTINUE;  // this one not a background template.

                if (who=1) and (group_selected=False) then CONTINUE;  // ignore if not in group.

                if (who=2) and (   (template_info.keep_dims.box_dims1.timbering_brick_flag=False)         // ignore if not in brick  234a
                                or (template_info.keep_dims.box_dims1.pad_marker_colour<>brick_col)
                                or (template_info.keep_dims.box_dims1.use_pad_marker_colour=False) )
                   then CONTINUE;

                if who=2 then brick_exists:=True;

                with template_info.bgnd_keep do begin

                  if xlist_max>max_long then max_long:=xlist_max;
                  if ylist_max>max_wide then max_wide:=ylist_max;

                  if xlist_min<min_long then min_long:=xlist_min;
                  if ylist_min<min_wide then min_wide:=ylist_min;

                end;//with bgnd_keep.
              end;//with template.
            end;//for-next n template.
          end;//if bgnd.

  if (who=2) and (brick_exists=False)
     then begin
            show_modal_message('There are no brick templates matching this colour.');
            EXIT;
          end;

  min_long:=min_long/100;      // to mm.
  min_wide:=min_wide/100;

  max_long:=(max_long)/100;
  max_wide:=(max_wide)/100;

  margin_factor:=1.10;         // arbitrary 10% extra for margins.   216c

  screenx:=(max_long-min_long)*margin_factor;

  if screenx<((max_wide-min_wide)*margin_factor*wl_factor) then screenx:=(max_wide-min_wide)*margin_factor*wl_factor;

  if screenx<screenx_min then screenx:=screenx_min; // minimum for screen width (max zoom in).
  if screenx>screenx_max then screenx:=screenx_max; // maximum zoom out.

             // centralize on pad..

  zoom_offsetx:=min_long-(screenx-(max_long-min_long))/2;
  if wl_factor>minfp then zoom_offsety:=min_wide-(screenx/wl_factor-(max_wide-min_wide))/2;

  pad_form.lock_scaling_menu_entry.Click; // lock pad zoom.

  RESULT:=True;  // 234a

  show_and_redraw(False,False);    // in case current is hidden.
                                   // do immediate redraw (so that a reload appears behind the keeps box).
end;
//____________________________________________________________________________________

procedure explode_shrink(new_screenx:extended; loop,wheel:boolean);     // mouse wheel mode added 0.97.d

var
  screen_factor,zoom_factor:extended;
  mps:TPoint;
  border_width,mouse_left,mouse_top:integer;

begin

  if draw_mode<>2 then pad_form.lock_scaling_menu_entry.Click;

  do_rollback:=False;   // no need to put this in rollback register on redraw.

  screen_factor:=screeny/screenx;

  if new_screenx>screenx_max then new_screenx:=screenx_max;
  if new_screenx<screenx_min then new_screenx:=screenx_min;

  if (wheel=True) and (wheel_zoom_code=1)  // mouse wheel, zoom and keep mouse on location
     then begin

            zoom_factor:=new_screenx/screenx;

            zoom_offsetx:=mouse_now_x-(mouse_now_x-zoom_offsetx)*zoom_factor;
            zoom_offsety:=mouse_now_y-(mouse_now_y-zoom_offsety)*zoom_factor;

            screenx:=new_screenx;

            redraw(False);
            EXIT;
          end;


  if (wheel=True) and (wheel_zoom_code=2)  // mouse wheel, jump to centre and zoom.
     then begin

                  // first zoom normal settings...

            zoom_offsetx:=zoom_offsetx+(screenx-new_screenx)/2;
            zoom_offsety:=zoom_offsety+(screenx-new_screenx)/2*screen_factor;

            screenx:=new_screenx;

            if GetCursorPos(mps)=False then begin mps.X:=0; mps.Y:=0; end;  // get cursor X,Y

                // move cursor to centre of pad ...

            with pad_form do begin

              border_width:=Round((Width-ClientWidth)/2);

              mouse_left:=Left+border_width+Round((ex+ClientWidth)/2);

              mouse_top:=Top+Height-border_width-ClientHeight+Round(by/2);

            end;//with

            SetCursorPos(mouse_left,mouse_top);

                // move pad to match ...

            zoom_offsetx:=zoom_offsetx-(mouse_left-mps.X)*ffx;
            zoom_offsety:=zoom_offsety-(mouse_top-mps.Y)*ffy;

            redraw(False);
            EXIT;
          end;

      // zoom on pad centre ...

  zoom_offsetx:=zoom_offsetx+(screenx-new_screenx)/2;                 // normal zoom, keep screen centre stable.
  zoom_offsety:=zoom_offsety+(screenx-new_screenx)/2*screen_factor;

  screenx:=new_screenx;

  redraw(loop);     // draw direct if click/keypress so each one is calculated.   (True via onIdle for examine peg)
end;
//______________________________________________________________________________________

procedure shift_group_into_positive_quadrant(warn:boolean);

var
  i,n:integer;
  min_long,min_wide:extended;

  xshapes,yshapes:extended;

begin
  mouse_shift_sync_wanted:=False;    // not mouse action here

  if any_selected=0
     then begin
            if alert_no_group=True    // alert him, and does he want all?
               then EXIT;
          end;

  min_wide:=screenx_max*100;          // init max pos.
  min_long:=screenx_max*100;

  for n:=0 to (keeps_list.Count-1) do begin

    with Ttemplate(keeps_list.Objects[n]) do begin

      if bg_copied=False then CONTINUE;  // this one not a background template.

      if group_selected=False then CONTINUE;  // ignore if not in group.

      with template_info.bgnd_keep do begin

        if xlist_min<min_long then min_long:=xlist_min;
        if ylist_min<min_wide then min_wide:=ylist_min;

      end;//with bgnd_keep.
    end;//with template.
  end;//for-next n template.

  min_long:=min_long/100;      // to mm.
  min_wide:=min_wide/100;

  if (min_long>=0) and (min_wide>=0)
     then begin
            show_modal_message('All group templates are currently within the positive quadrant on the grid.');
            EXIT;
          end;

   if warn=True
      then begin
             i:=alert(7,'    shift  group  into  positive  quadrant',
                       '||You are about to shift all group templates as a block such that every one is within the positive quadrant on the grid.'
                      +'||This is a one-time process which cannot be undone.'
                      +'||You may wish to save a data file first, so that you can revert to the existing template locations if necessary.',
                       '','','','save  group  templates  first','cancel  shift','shift  group  without  saving',0);

             case i of
               4: keep_form.save_group_menu_entry.Click;
               5: EXIT;
             end;//case
           end;

  if min_long<0 then xshift_keeps:=0-min_long+g     // +g arbitrary to clear axes
                else xshift_keeps:=0;

  if min_wide<0 then yshift_keeps:=0-min_wide+g
                else yshift_keeps:=0;

  xshapes:=xshift_keeps;
  yshapes:=yshift_keeps;

  shift_all_group(False);

  rebuild_group(False,False);

  redraw(True);

  if (bgnd_form.bgnd_shapes_listbox.Items.Count<1) or (bgnd_form.allow_sync_checkbox.Checked=False) then EXIT;

  if alert(4,'    shift  background  shapes  in  sync ?',
             'The group templates have been shifted into the positive quadrant.'
            +'||Do you now want your background shapes to be shifted by the same amount?'
            +'||Any shapes which have their `0allow to sync with templates`1 option box unticked will not be shifted.',
             '','','','','no  thanks','yes  please',0)=5
     then EXIT;

  shift_all_shapes(True,xshapes,yshapes);      // True=sync 226b

  shapes_saved:=False;      // need a resave.
  shapes_current_state;

  do_rollback:=False;
  redraw(True);
end;
//______________________________________________________________________________

procedure init_rotate(x,y:extended; rad_centre:boolean);  // set up transform constants for rotates.
                                                          // rotation centre at x,y.
var
  pc,pf:Tpex;
  dummy1,dummy2:extended;

begin
  if rad_centre=False then docurving(False,True,x,y, pc.x,pc.y, dummy1,dummy2)  // curve untransformed rotation centre position in pc (rad and angle not needed).
                      else begin pc.x:=x; pc.y:=y; end;                         // but don't curve the curving centre!

  dotransform(kform,xform,yform,pc,pf);              // transform it to pf.

  xform:=pc.x;     // pc is the new rotation point.
  yform:=pc.y;

  xshift:=xshift-pc.x+pf.x;     // adjust shifts to maintain position.
  yshift:=yshift-pc.y+pf.y;
end;
//___________________________________________________________________________________________

procedure rotate_turnout(k:extended;draw:boolean);     // rotate turnout k radians around peg.

begin
  init_rotate(pegx,pegy,False);
  kform:=kform+k;                  // positive k anti-clockwise.
  normalize_kform;
  if draw=True then redraw(True);
end;
//________________________________________________________________________________________

function alert_no_group:boolean;    // return False if any get selected.

const
  nosel_help_str:string='      Selecting  Group  Templates'

  +'||Before a group of stored templates can be subject to shift, rotate, save, or other group operations, they must first be selected.'
  +'||To select all background templates as a group, click the GROUP > GROUP SELECT ALL menu item (or press CTRL+A).'
  +'||To add or remove individual templates to or from a group, click anywhere on each template and then click the GROUP SELECT (TOGGLE) menu item on the pop-up menu which appears for that template.'
  +'||Or to quickly select multiple background templates with the mouse, click the GROUP > GROUP SELECT > CLICK BACKGND TO GROUP menu item and then click on their name labels.'
  +'||A group of templates can also be selected by clicking the GROUP-SELECTION FENCE toolbutton at the top of the pad, and then drawing a rectangle around them.'
  +'||Group templates can also be selected or de-selected in the Storage Box, and the group can then also include unused stored templates. Click the ? HELP button on the Storage Box for more information.';

var
  i:integer;

begin
  RESULT:=True;       // default init, no group.
  repeat

    i:=alert(3,'        no  group',
               'There are no stored templates currently selected for group operations.',
               '','','','?  help','cancel','group  select  all',4);
    case i of
        4: alert_help(0,nosel_help_str,'');
        6: begin
             pad_form.select_all_keeps_menu_entry.Click;
             if any_selected<>0 then RESULT:=False;
           end;
    end;//case
  until i<>4;
end;
//____________________________________________________________________________________________

procedure alert_no_bgnd;      // mod 0.93.a

begin
  alert(3,'    no  templates  currently  on  background',
          '||There are no stored templates currently on the background drawing on the trackpad.'
         +'||The template which you can see on the trackpad is the control template.'
         +'||Many functions in Templot apply to background templates only, for example setting a marker colour, exporting a DXF file, selecting a group of templates.'
         +'||And some control template functions require a background template to be present, for example make simple link, make transition link, extend template to meet.'
         +'||To place a copy of the current control template on the background, click the `0PROGRAM > STORE & BACKGROUND`1 menu item or press the `0INSERT`2 key.'
         +'||To make an unused stored template appear on the background, click the `0COPY TO BACKGROUND`1 button on the storage box.| ',
          '','','','','','continue',0);
end;
//____________________________________________________________________________________________

procedure alert_no_unused;

begin
  alert(3,'    no  templates  currently  unused',
          'There are currently no unused stored templates.'
         +'||To store an unused copy of the control template, click the|`0PROGRAM > STORE AS UNUSED`1 menu item.'
         +'||To remove a stored template from the background, so making it an unused stored template, click the|`0WIPE FROM BACKGROUND`1 button in the storage box.',
          '','','','','','continue',0);
end;
//____________________________________________________________________________________________

procedure alert_no_library;

begin
  alert(3,'    no  library  templates',
          'There are currently no library templates in your storage box.'
         +'||To store a library copy of the control template, click the|`0MAIN > STORE AS LIBRARY`1 menu item.'
         +'||To add library templates from a file, click the `0ADD LIBRARY...`1 button on the storage box.',
          '','','','','','continue',0);
end;
//____________________________________________________________________________________________

function get_new_rad_org(rad_str:string; var orgx,orgy:extended):boolean;   // get new radial centres.

const
  help_xradshift_str:string='     Shift radial centre to X dimension.'
                  +'||Enter an X-dimension in millimetres for the new position of the radial centre on the trackpad (the centre location from which the curving line radius is drawn).'
                  +'||X-dimensions are measured across the width of the screen, positive from left to right.'
                  +'||Unless the exact location of the radial centre is important, shifting the control template is more usually done with the mouse action, select the|`0ACTION > MOUSE ACTIONS: GEOMETRY > SHIFT POSITION`1 menu item (or press `0F7`2).';

  help_yradshift_str:string='     Shift radial centre to Y dimension.'
                  +'||Enter an Y-dimension in millimetres for the new position of the radial centre on the trackpad (the centre location from which the curving line radius is drawn).'
                  +'||Y-dimensions are measured vertically on the screen, the positive direction is upwards from the bottom.'
                  +'||Unless the exact location of the radial centre is important, shifting the control template is more usually done with the mouse action, select the|`0ACTION > MOUSE ACTIONS: GEOMETRY > SHIFT POSITION`1 menu item (or press `0F7`2).';

var
  n:integer;
  od:Toutdim;

begin
  RESULT:=False;     // default init.

     putdim(help_xradshift_str,1,'shift  '+rad_str+'radial  centre  to  X  ( from  left )',orgx,False,True,False,False);     // negative ok, no preset, zero ok, don't terminate on zero.
  n:=putdim(help_yradshift_str,1,'shift  '+rad_str+'radial  centre  to  Y  ( from  bottom )',orgy,False,True,False,False);   // negative ok, no preset, zero ok, don't terminate on zero.

  if n<>1 then EXIT;
  if getdims('shift  '+rad_str+'radial  centre  to...','',pad_form,n,od)=True
     then begin
            orgx:=od[0];
            orgy:=od[1];
            RESULT:=True;
          end;
end;
//_______________________________________________________________________________________

procedure shift_radial_centre(to_notch:boolean);

var
  orgx,orgy:extended;
  first_rad:boolean;
  i:integer;

begin
  if spiral=False
     then begin
            if ABS(nomrad)>max_rad_test
               then begin
                      alert(6,'    no  radial  centre',
                              '            Shift  radial  centre.'
                             +'The current curving radius is so great that the template is effectively dead straight and there is no radial centre to be shifted.',
                              '','','','','cancel','',0);
                      EXIT;
                    end;

            if to_notch=True    // move centre to notch
               then begin
                      orgx:=notchx;
                      orgy:=notchy;
                      xshift:=xshift+(orgx-rad1_orgx);
                      yshift:=yshift+(orgy-rad1_orgy)*hand_i;

                      redraw(True);
                    end
               else begin                // move centre somewhere else...
                      orgx:=rad1_orgx;
                      orgy:=rad1_orgy;

                      if get_new_rad_org('',orgx,orgy)=True   // get new radial centres.
                         then begin
                                xshift:=xshift+(orgx-rad1_orgx);
                                yshift:=yshift+(orgy-rad1_orgy)*hand_i;

                                redraw(True);
                              end;
                    end;
          end
     else begin
            if (ABS(nomrad1)<=max_rad_test) and (ABS(nomrad2)<=max_rad_test)  // neither rad straight?
               then begin
                      if to_notch=True
                         then begin
                                i:=alert(4,'    which  radial  centre ?',
                                        '      Shift  radial  centre  to  notch.'
                                       +'||The control template is on a transition curve. Which radial centre do you want to shift to the pegging notch?',
                                        '','','shift  centre  of  1st  radius  to  notch','shift  centre  of  2nd  radius  to  notch','cancel','',0);
                              end
                         else begin
                                i:=alert(4,'    which  radial  centre ?',
                                        '            Shift  radial  centre.'
                                       +'||The control template is on a transition curve. For which radial centre do you want to set a new position?'
                                       +'||(It is not possible to set both centres directly. The other centre can be moved using the CTRL-F5 ORBIT mouse action,'
                                       +' or by adjusting the transition start and length settings while the fixing peg is in the fixed part of the curve.)',
                                        '','','shift  centre  of  1st  radius  to ...','shift  centre  of  2nd  radius  to ...','cancel','',0);
                              end;
                      case i of
                           3: first_rad:=True;
                           5: EXIT;
                         else first_rad:=False;
                      end;//case
                    end
               else begin
                      if ABS(nomrad1)>max_rad_test then first_rad:=False  //easement from straight.
                                                   else first_rad:=True;  //easement to straight.
                    end;

              if first_rad=True
                 then begin
                        if to_notch=True
                           then begin
                                  orgx:=notchx;
                                  orgy:=notchy;
                                  xshift:=xshift+(orgx-rad1_orgx);
                                  yshift:=yshift+(orgy-rad1_orgy)*hand_i;

                                  //if transform=False then transform_on_and_redraw;    //  won't work otherwise - also does a redraw.
                                  redraw(True);
                                end
                           else begin
                                  orgx:=rad1_orgx;
                                  orgy:=rad1_orgy;

                                  if get_new_rad_org('1st  ',orgx,orgy)=True   // get new radial centres.
                                     then begin
                                            xshift:=xshift+(orgx-rad1_orgx);
                                            yshift:=yshift+(orgy-rad1_orgy)*hand_i;

                                            //if transform=False then transform_on_and_redraw;    //  won't work otherwise - also does a redraw.
                                            redraw(True);
                                          end;
                                end;
                      end//rad1
                 else begin
                        if to_notch=True
                           then begin
                                  orgx:=notchx;
                                  orgy:=notchy;
                                  xshift:=xshift+(orgx-rad2_orgx);
                                  yshift:=yshift+(orgy-rad2_orgy)*hand_i;

                                  redraw(True);
                                end
                           else begin
                                  orgx:=rad2_orgx;
                                  orgy:=rad2_orgy;

                                  if get_new_rad_org('2nd  ',orgx,orgy)=True   // get new radial centres.
                                     then begin
                                            xshift:=xshift+(orgx-rad2_orgx);
                                            yshift:=yshift+(orgy-rad2_orgy)*hand_i;

                                            redraw(True);
                                          end;
                                end;
                      end;//rad2
          end;//transition
end;
//________________________________________________________________________________________

function get_peg_for_notch:Tnotch;

var
  temp,dummy1,dummy2:extended;
  notch_data:Tnotch;

begin
  with RESULT do begin      // default inits...
    notch_x:=0;
    notch_y:=0;
    notch_k:=0;
  end;//with

  with notch_data do begin
    try
      normalize_transforms;
      docurving(True,True,pegx,pegy,notch_x,temp,dummy1,dummy2);   // get notch data from current peg position.
      notch_y:=temp*hand_i+y_datum;
      notch_k:=arm_angle*hand_i;
    except
      EXIT;
    end;//try
  end;//with
  RESULT:=notch_data;
end;
//____________________________________________________________________________________________

function get_current_notch:Tnotch;

begin
  with RESULT do begin
    notch_x:=notchx;
    notch_y:=notchy;
    notch_k:=notch_angle;
  end;//with
end;
//________________________________________________________________________________________

procedure set_current_notch(notch_data:Tnotch);

begin
  with notch_data do begin
    notchx:=notch_x;
    notchy:=notch_y;
    notch_angle:=notch_k;
  end;//with
end;
//________________________________________________________________________________________

procedure do_group_link_to_notch;

begin
  shift_rotate_group(notchx-old_notchx,notchy-old_notchy,0-(notch_angle-old_notch_angle),False);   //  moving peg, maintain direction.  no egg-timer.
end;
//________________________________________________________________________________________

procedure new_notch(notch_data:Tnotch; link_group:boolean);    // set new notch position on pad.

begin

  old_notchx:=notchx;
  old_notchy:=notchy;
  old_notch_angle:=notch_angle;

  set_current_notch(notch_data);

  cancel_adjusts(False);  // notch is shown on the background - can't change it during foreground adjusts.

  INC(notch_index);                             // to next rollback slot.
  if notch_index>notch_c then notch_index:=0;

  undo_notch[notch_index]:=notch_data;          // save notch in this slot...

  pad_form.cycle_notch_menu_entry.Enabled:=True;

  if (link_group=True) and (group_notch_linked=True) then do_group_link_to_notch;

  do_rollback:=False;       // no need to put this change in rollback register on redraw.
  redraw(True);
end;
//___________________________________________________________________________________________

procedure shift_onto_notch(click,min_rot:boolean);

  // click=True if he clicked the menu, so minimise rotation (visually closest fit).
  // if min_rot=True, use minimum amount of rotation even if not clicked.

var
  padpegx,padpegy,dummy1,dummy2:extended;
  angle_diff:extended;
  new_padpegx, new_padpegy:extended;

begin
  pad_form.notch_unlinked_from_current_menu_entry.Click;  // radio item - cancel any moving the notch in mouse actions.

  normalize_transforms;
  docurving(True,True,pegx,pegy,padpegx,padpegy,dummy1,dummy2);   // calc current peg position.

  xshift:=xshift+(notchx-padpegx);
  yshift:=yshift+(notchy-(padpegy*hand_i+y_datum))*hand_i;   // then shift turnout onto notch.

  angle_diff:=notch_angle*hand_i-arm_angle;

  if (click=True) or (min_rot=True)   // he's not likely to be wanting more than 90 degs of rotation...
     then begin
            while angle_diff<(0-Pi/2) do angle_diff:=angle_diff+Pi;  // less than -90, add 180 degrees.
            while angle_diff>Pi/2 do angle_diff:=angle_diff-Pi;      // more than 90, subtract 180 degrees.
          end;

  rotate_turnout(angle_diff,True);                           // rotate turnout around peg to match angles and redraw.

      // job done, but we need to keep a record of what's happened in case of shift group.

      // if it was already on notch, rotate it instead (but not if creating a tandem 218a).

  if click=True
     then begin

            docurving(True,True,pegx,pegy,new_padpegx,new_padpegy,dummy1,dummy2);   // calc new peg position.

            saved_pegging_shiftx:=new_padpegx-padpegx;
            saved_pegging_shifty:=(new_padpegy-padpegy)*hand_i;
            saved_pegging_rot:=0-angle_diff*hand_i;

            if     (ABS(saved_pegging_shiftx)<minfp)
               and (ABS(saved_pegging_shifty)<minfp)
               and (ABS(saved_pegging_rot)<minfp)
               and (creating_tandem=False)                              // 218a
                   then pad_form.rotate_current_180_menu_entry.Click;   // didn't need to move it, so rotate instead.
          end;
end;
//____________________________________________________________________________________

procedure retain_on_make;     // 213a

var
  dummy:extended;

begin

  timbering_brick:=False;         // 234a  always cancel
  inherited_use_marker:=False;    // no marker colour

  cancel_platforms;  // always cancel existing platforms

  omit_wj_marks:=False;     // always put back on (turned off in make_slip)    215a

  if making_a_diamond=False    // global flag    215a
     then begin
            hd_vcheck_rails:=0;   // normal
            hd_timbers:=0;        // normal
          end
     else making_a_diamond:=False;   // for next make

  if pad_form.make_tools_cancel_blanking_menu_entry.Checked=True then startx:=0;    // cancel any blanking.

  if retain_shoves_on_make=False then clear_shovedata;  // clear any shoved timbers.
  if retain_diffs_on_make=False then clear_check_diffs; // 0.94.a clear any check rail diffs

  if gaps_form.symbols_retain_on_make_checkbox.Checked=False    // 227a
     then begin
            delete_all_symbols;
          end;

  if (retain_entry_straight_on_make=False) and (entry_straight_code<>0)  // 213a 0=using auto-fit instead
     then begin

            if plain_track=True
               then entry_straight_code:=0     // changing to auto-fit has no effect on plain track geometry
               else begin

                      // modify the new control template for the changed entry straight...

                      kform_now:=kform;
                      arm_angle_now:=arm_angle;
                      docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy);    // first save current peg position on pad and curving angle.

                      entry_straight_code:=0;  // changed to auto-fit, so geometry will now be modified.

                      gocalc(0,0);           // recalc to get new pegx, pegy, arm_angle.

                      peg_curve;             //  adjust shifts and rotates for current peg position.
                      gocalc(0,0);           //  get new arm_angle (peg might be on turnout road).

                      rotate_turnout(arm_angle_now-arm_angle,False);   // re-align to previous angle,no draw.
                    end;

            gocalc(0,0);
          end;

end;
//______________________________________________________________________________

procedure make_double_track_calcs(side:integer);

var
  old_rad,rad_mod:extended;

  dummy1,dummy2,dummy3,dummy4,dummy5:extended;

  old_apartl:extended;

  old_rad2_orgx,old_rad2_orgy:extended;
  old_rad1_orgx,old_rad1_orgy:extended;



                             //////////////////////////////////////////////////////////////

                             procedure calc_error;

                             var
                               slew_str:string;

                             begin
                               wait_form.Close;
                               if Application.Terminated=False then Application.ProcessMessages;

                               if slewing=True then slew_str:=', or try repeating the process with slewing cancelled. Slewing can then be re-applied to the new template as required.'
                                               else slew_str:='.';

                               alert(5,'    calculation  failed',
                                       'Sorry, automatic adjustment has failed.'
                                      +'||Please adjust the new template manually'+slew_str,
                                       '','','','','','continue',0);

                             end;
                             //////////////////////////////////////////////////////////////

begin
try
                          // keep compiler happy..

  old_rad1_orgx:=0;
  old_rad1_orgy:=0;

  old_rad2_orgx:=0;
  old_rad2_orgy:=0;

  if (spiral=True) and (auto_spiral_adjust=True)
     then begin

             // first get the existing transition data (relative to TRANSITION datum).
             // (apartl ignores any slewing)

            if calc_transition(nomrad1,nomrad2,tst,dummy1,dummy2,dummy3,dummy4,old_apartl,dummy5)=False
               then begin
                      calc_error;
                      auto_spiral_adjust:=False;
                    end;

            old_rad2_orgx:=rad2_orgx;       // 2nd rad centres on pad..
            old_rad2_orgy:=rad2_orgy;

            old_rad1_orgx:=rad1_orgx;       // 1st rad centres on pad..
            old_rad1_orgy:=rad1_orgy;

          end;

  with pad_form do begin

    do_rollback:=False;
    adjacent_redraw;                         // update rail-edges.

    do_rollback:=False;
    store_and_background(False,False);     // first keep it and copy to background.
    if keep_added=False then EXIT;         // he cancelled.

    retain_on_make;    // do blanking, shoves, diffs, crossing entry straight, cancel platforms  213a

    show_and_redraw(False,False);   // in case copy caused a current hide.
                                    // force redraw to remove adjacent track.

    rad_mod:=0;               // keep compiler happy.

    do_rollback:=False;
    case side of
          -1: begin
                rad_mod:=0-trmscent;
                peg_on_adjacent_ms_menu_entry.Click;  // put the peg on the adjacent main-side track.
              end;
           1: begin
                rad_mod:=trtscent;
                peg_on_adjacent_ts_menu_entry.Click;  // put the peg on the adjacent turnout-side track.
              end;
         else run_error(199);
    end;//case

    if (spiral=True) and (auto_spiral_adjust=True)   // !!! 14-7-00 put peg well behind transition start.
                                                     // (os might be negative and peg must be in rad1 for calcs to work).
       then begin

              do_rollback:=False;
              gocalc(0,0);                     // first set pegy for adjacent.

              peg_code:=-1;                    // now make peg free.
              case side of
                    -1: pegx:=os-10*trmscent;  // main side.  (10*spacing arbitrary).
                     1: pegx:=os-10*trtscent;  // turnout side.
                   else run_error(198);
              end;//case
            end;

    do_rollback:=False;
    gocalc(0,0);                           // ensure peg calcs done,

    new_notch(get_peg_for_notch,False);    // so can put notch under.

            // now change to plain track...

    plain_track:=True;
    set_plain_track(True,True);

    xorg:=turnoutx;           // keep current length for starters.

    hand_i:=0-hand_i;                    // swap hand (so turnout-side is to same double-track centre).
    if slewing=True then slew:=0-slew;   // need to swap the hand of any slewing also.

    if (ABS(nomrad)<max_rad_test) and (spiral=False)     // fixed curved template, so must adjust the curving rad...
       then begin
              old_rad:=nomrad;
              nomrad:=0-(nomrad-rad_mod);   // adjust for adjacent track (swapping hand).

              if ABS(old_rad)>minfp   // 0.79.a
                 then begin
                        xorg:=xorg*ABS(nomrad/old_rad);  // adjust length to maintain swing angle. 0.79.a
                        turnoutx:=xorg;
                      end;
            end;

    if spiral=True     // transition template, adjust the rads...
       then begin
              nomrad1:=0-(nomrad1-rad_mod);     // adjust for adjacent track (swapping hand)...
              nomrad2:=0-(nomrad2-rad_mod);

              if auto_spiral_adjust=True
                 then begin
                        case do_auto_trans_length_adjust(old_apartl) of   // set the new transition length.

                           -1: auto_spiral_adjust:=False;    // he cancelled.

                            0: begin                         // calc error.
                                 auto_spiral_adjust:=False;
                                 calc_error;
                               end;

                        //  1: ok
                        end;//case
                      end;
            end;

    do_rollback:=False;
    turnout_i:=1;      // length locked at turnoutx.

      // so can draw approach track...

    length_free_popup_entry.Enabled:=False;
    length_free_menu_entry.Enabled:=False;

    do_rollback:=False;

    if (spiral=True) and (auto_spiral_adjust=True) and (wait_cancel_clicked=False)
       then pegy:=g/2                                  //!!! 14-7-00 put peg back on centre-line)
       else reset_peg_menu_entry.Click;

    do_rollback:=False;
    gocalc(0,0);                   // ensure peg calcs done.

    do_rollback:=False;
    shift_onto_notch(False,False);       // and finally put it on the notch.

                      //!!! mods 14-7-00 ...

    if (spiral=True) and (auto_spiral_adjust=True) and (wait_cancel_clicked=False)
       then begin
              case do_auto_trans_start_adjust(old_rad1_orgx, old_rad1_orgy, old_rad2_orgx, old_rad2_orgy) of   // set the new transition start.

                -1: auto_spiral_adjust:=False;    // he cancelled.

                 0: begin                         // calc error.
                      auto_spiral_adjust:=False;
                      calc_error;
                    end;

             //  1: ok
              end;//case

              reset_peg_menu_entry.Click;  // put peg back in sensible place.
              wait_form.Close;

            end;

    reset_notch_menu_entry.Click;     // put notch back on datum.

    clear_current_name;

    show_and_redraw(True,True);                // in case hidden.
  end;//with
finally
  wait_form.Close;
end;//try
end;
//______________________________________________________________________________________

procedure make_double_track(side:integer);   // change to plain track in place of the current adjacent track.
                                             // side=1 is turnout side.
                                             // side=-1 is main side.
const
  slew_adj_str:string='      Make  Slewed  Double - Track'
  +'||The MAKE DOUBLE-TRACK functions create double-track by first storing the control template and making a copy of it on the background, and then creating a new plain track template alongside,'
  +' adjusting the curving radius according to the current setting for the turnout-side or main-side adjacent track spacings.'

  +'||When the tracks are slewed, some additional adjustment is necessary.'
  +'||To adjust the new track created, do this:'
  +'||First using the MOVE SLEWING START (SHIFT+CTRL-F5) mouse action, move the slewing zone start marker on the newly created track until it aligns'
  +' with the slewing zone start marker on the original track.'
  +'||Then using the ADJUST SLEWING LENGTH (SHIFT+CTRL-F6) mouse action, move the slewing zone end marker on the newly created track until it aligns'
  +' with the slewing zone end marker on the original track.'
  +'||It is helpful to zoom in on these marks while making the adjustments, and to check the spacing between the tracks while doing so. On sharp curves or where there is significant slewing,'
  +' the slewing zone end marks may need to be displaced slightly to maintain the proper clearance between the tracks.'
  +'||Use the SPACING-RING tool to check track spacing clearances.'
  +'||N.B. If the original template was being drawn with ADJACENT TRACKS switched on in the GENERATOR SETTINGS, these will be switched off to avoid duplication.';

  spiral_adj_str:string='      Make  Double - Track  on  Transition  Curve'

  +'||The MAKE DOUBLE-TRACK functions create double-track by first storing the control template and making a copy of it on the background, and then creating a new plain track template alongside,'
  +' adjusting the curving radius according to the current setting for the turnout-side or main-side adjacent track spacings.'

  +'||When the tracks are on a transition curve, additional adjustments are made to the transition start and length settings to ensure that the initial and final radii remain concentric with those of the previous template.'

  +'||The calculations for these adjustments may take some time to complete, during which a PLEASE WAIT message will be displayed.'

  +'||You can watch these calculations being performed by expanding the INFORMATION panel, and scrolling the INFO area to see the transition data.'

  +'||Occasionally these calculations may not be able to reach a conclusion, in which case you should click the CANCEL button on the PLEASE WAIT window (or press the ESC key), and then complete the adjustment manually.'

  +'||Or if preferred, you can perform the whole adjustment manually, for more information see below.'

  +'||Caution: It is not mathematically possible to have two perfectly "parallel" transition curves. This means that within the transition zones it may not be possible to maintain the current adjacent track spacing dimension.'
  +'||Before making double-track on a transition curve therefore, it may be advisable to increase the adjacent track spacing slightly above the minimum (GEOMETRY > ADJACENT TRACK CENTRES... menu items), and to check the spacing afterwards'
  +' (UTILS > DUMMY VEHICLE • SPACING-RING menu item).'
  +'||These precautions are more important when working with a reverse S-curve transition. For easements to or from straight track and transitions where both the initial and final radii are curving'
  +' in the same direction (both radii having the same sign), it will usually be found that the spacing discrepancy is small or insignificant.'

  +'||If the double-track templates also contain a SLEW, the slew settings should be adjusted manually if necessary before checking the spacings.'

  +'|------------------------'
  +'||To adjust manually the new track created, do this:'

  +'||First using the MOVE TRANSITION START (SHIFT+CTRL-F3) mouse action, move the transition start marker on the newly created track until it aligns'
  +' with the transition start marker on the original track.'

  +'||Then using the ADJUST TRANSITION LENGTH (SHIFT+CTRL-F4) mouse action, move the transition end marker on the newly created track until it aligns'
  +' with the transition end marker on the original track.'

  +'||It is helpful to zoom in on these marks while making the adjustments, and to check the rail alignments while doing so. On sharp curves the best rail alignment'
  +' may require one or both of the transition marks to be displaced slightly from these positions.'

  +'||( A slight discrepancy in the curving centres may remain, because it is not mathematically possible to have two perfectly "parallel" transition curves. The alternative method'
  +' of applying the transition maths to the double-track centre-line is not used because this would introduce unacceptable distortion when the tracks are widely spaced, and also disrupt'
  +' the alignment of the original track.)'

  +'||The adjustments detailed above will produce satisfactory double-track on transition curves in the majority of situations found in practice.'
  +' Use the SPACING-RING tool to check the track spacings for adequate clearance.'

  +'||If the double-track templates also contain a SLEW, the slew settings should be adjusted similarly if necessary before checking the spacings.'

  +'|------------------------'

  +'||N.B. If the original track or turnout was being drawn with ADJACENT TRACKS switched on in the GENERATOR SETTINGS, these will be switched off to avoid duplication.';

var
  i:integer;
  s_curve_str:string;

  side_str:string;

begin

  if check_control_template_is_valid('double-track')=False then EXIT;  // 0.93.a  zero length

  if side=1 then side_str:='TS'
            else side_str:='MS';

  way_spacings_top_str:='    make  double-track  on  '+side_str;   // 226c
  way_spacings_cancel_str:='cancel  double-track     ';           // 226c

  if do_way_spacings(True,side)=False then EXIT;

  if (side=-1) and (g>(47*inscale)) and (cpi.trmscent_pi<(134*inscale))
     then begin
            if alert(1,'   make  double-track  -  MS',
               'Your current setting for the MS (main-side) track spacing is less than the 6ft WAY (11ft-2in centres, 134 inches) minimum for running lines on UK standard-gauge railways.'
              +'||For sidings and marshalling yards a closer track spacing down to 5ft WAY is sometimes used.'
              +'||If the tracks are sharply curved you will need additional track spacing above the minimum to provide sufficient clearance.',
               '','','','','cancel','continue - make  MS  double-track  at  current  setting    ',0)=5 then EXIT;
          end;

  if (side=1) and (g>(47*inscale)) and (cpi.trtscent_pi<(134*inscale))
     then begin
            if alert(1,'   make  double-track  -  TS',
               'Your current setting for the TS (turnout-side) track spacing is less than the 6ft WAY (11ft-2in centres, 134 inches) minimum for running lines on UK standard-gauge railways.'
              +'||For sidings and marshalling yards a closer track spacing down to 5ft WAY is sometimes used.'
              +'||If the tracks are sharply curved you will need additional track spacing above the minimum to provide sufficient clearance.',
               '','','','','cancel','continue - make  TS  double-track  at  current  setting    ',0)=5 then EXIT;
          end;

  if slewing=True
     then begin
            repeat
              i:=alert(3,'   make  double-track  -  slewed  track',
                         'Your control template contains a slew.'
                        +'||The new template created may need to have the slewing zone start and length settings adjusted to ensure a proper spacing from the original track.',
                         '','','','more  information','cancel  make','continue  -  make  double-track',4);
              case i of
                  4: alert_help(0,slew_adj_str,'');
                  5: EXIT;
              end;//case
            until i<>4;
          end;

  if spiral=True
     then begin
            auto_spiral_adjust:=True;    // default init.

            if (ABS(nomrad1)<max_rad_test) and (ABS(nomrad2)<max_rad_test) and (SGZ(nomrad1)<>SGZ(nomrad2))
               then s_curve_str:='||This is a reverse S-curve transition. It may be necessary to check the track spacing within the transition zone for adequate passing clearance.'
               else s_curve_str:='';

            repeat
              i:=alert(4,'   make  double-track  -  transition  curve',
                         'Your control template is on a transition curve.'
                        +'||The new track created will need to have the transition start and length settings adjusted.'
                        +'||You can let Templot do this automatically, or make the adjustments yourself manually.'
                        +s_curve_str,
                         '','','more  information','make  double-track  -  manual  adjust','cancel  make','make  double-track  -  automatic  adjust',3);
              case i of
                  3: alert_help(0,spiral_adj_str,'');
                  4: auto_spiral_adjust:=False;
                  5: EXIT;
              end;//case
            until i<>3;

            wait_cancel_clicked:=False;
            wait_form.cancel_button.Show;
            wait_form.waiting_label.Caption:='calculating ...';

            wait_form.waiting_label.Width:=wait_form.Canvas.TextWidth(wait_form.waiting_label.Caption);  // 205b bug fix for Wine

            pad_form.trans_calc_timer.Tag:=side;        // do the calcs while the wait message shows modal.
            pad_form.trans_calc_timer.Enabled:=True;    // one-shot only.

            if auto_spiral_adjust=True then do_show_modal(wait_form);  // 212a   ShowModal

            if Application.Terminated=False then Application.ProcessMessages;

          end
     else make_double_track_calcs(side);  // not transition, no need to show wait/cancel message.

  rail_options_form.restore_all_button.Click;  // 211c

end;
//______________________________________________________________________________

procedure make_pt_geo_rad(int_ext:integer; click:boolean);

              // create plain track replacing the turnout road.
              // store the previous approach section up to the tangent point (if any) as a separate template.

              // click always True 0.93.a

var
  geor,geox,geok:extended;

begin
  if plain_track=True then EXIT;    // ? shouldn't get here, menu disabled for plain track.

  case int_ext of

           -1: begin                 // internal geometrical radius...
                 geor:=igeo_rad;
                 geox:=igpx;
                 geok:=igeo_swing;
                 pad_form.peg_on_IGTP_menu_entry.Click;
               end;

           else begin               // external geometrical radius...
                 geor:=egeo_rad;
                 geox:=egpx;
                 geok:=egeo_swing;
                 pad_form.peg_on_EGTP_menu_entry.Click;
               end;

  end;//case

  if geox<(0-minfp) then EXIT;  // can't fit the new turnout road template.

  if click=True
     then begin
            if check_control_template_is_valid('turnout  road')=False then EXIT;  // 0.93.a  zero length
          end
     else begin
            if turnoutx=0 then EXIT;    // 0.93.a  zero length, no alert
          end;

  case int_ext of

     -1: pad_form.peg_on_IGTP_menu_entry.Click;

    else pad_form.peg_on_EGTP_menu_entry.Click;

  end;//case

  gocalc(0,0);                                 // peg calcs.

  pad_form.notch_under_peg_menu_entry.Click;   // and put notch ready at the tangent point.

  pad_form.reset_peg_menu_entry.Click;         // peg on CTRL-0.
  do_rollback:=False;
  gocalc(0,0);                                 // peg calcs.

  plain_track:=True;
  set_plain_track(True,True);       // cancels any blanking.

  retain_on_make;

  if geox>inscale     // minimum 1" scale length for stored template (arbitrary)..
     then begin
            xorg:=geox;
            turnoutx:=geox;                   // new length
            redraw(False);

            store_and_background(False,False);   // keep it and copy to background.
            if keep_added=False
               then begin
                      show_and_redraw(True,False);
                      EXIT;                                // he cancelled.
                    end;
          end;

    // now change current to the geometrical rad...

  gocalc(0,0);                                        // peg calcs.

  xorg:=ABS(geor*geok);     // curve length.
  turnoutx:=xorg;
  nomrad:=geor;       // might be negative - we haven't changed the hand.

  gocalc(0,0);        // peg calcs.

  shift_onto_notch(False,False);

  rail_options_form.restore_all_button.Click;  // 211c

  show_and_redraw(True,True);
end;
//______________________________________________________________________________

function make_separate_approach(click:boolean):boolean;

var
  saved_control:Ttemplate_info;

begin
  RESULT:=False;  // init

  if (plain_track=True) and (click=True)
     then begin
            alert(6,'    make  separate  approach  track',
                    'The control template is a plain track template.'
                    +'||A plain track template has no approach track to be split off.'
                    +'||To make a split in plain track, click the `0tools > make split > make split at peg`1 menu item.',
                    '','','','','cancel','',0);
            EXIT;
          end;

  if (half_diamond=True) and (click=True)
     then begin
            alert(6,'    make  separate  approach  track',
                    'The control template is a half-diamond template.'
                    +'||A half-diamond template has no approach track to be split off.',
                    '','','','','cancel','',0);
            EXIT;
          end;

  if (xorg<minfp) and (click=True)
     then begin
            alert(6,'    make  separate  approach  track',
                    'This turnout has no approach track to be separated.',
                    '','','','','cancel','',0);
            EXIT;
          end;

  if (xorg<(10*scale)) and (click=True)            // 10ft scale arbitrary minimum
     then begin
            if alert(7,'  make  separate  approach  track  -  very  short',
                       'The approach track to this turnout is very short.'
                      +'||Are you sure you want it to be a separate template?',
                       '','','','','no  -  cancel','yes  -  make  separate  approach  track',0)=5
               then EXIT;
          end;

  if click=True
     then begin
            if check_control_template_is_valid('split')=False then EXIT;  // 0.93.a  zero length
          end
     else begin
            if turnoutx=0 then EXIT;    // 0.93.a  zero length, no alert
          end;

  try
    saved_control:=hold_the_control;  // 227a

    crop_approach;       // then crop all approach.
    gocalc(0,0);         // peg calcs.

    if (spiral=True) and (pad_form.make_tools_normalize_transitions_menu_entry.Checked=True)
       then begin
              normalize_transition;   // ignore result.
              gocalc(0,0);            // peg calcs.
            end;

    if (slewing=True) and (slew_s>turnoutx)
       then begin
              pad_form.disable_slewing_menu_entry.Click;   // new template in unslewed section.
              gocalc(0,0);                                 // peg calcs.
            end;

    store_and_background(False,False); // keep it and copy to background.

    if keep_added=False                // he cancelled.
       then begin
              unhold_the_control(saved_control);  // 227a
              show_and_redraw(True,False);
              EXIT;
            end;

              // now change previous current to plain track...

    unhold_the_control(saved_control);  // 227a

    retain_on_make;    // do blanking, shoves, diffs, crossing entry straight, cancel platforms  213a

    turnoutx:=xorg;          // only the approach track wanted.

    pad_form.peg_on_joint_end_menu_entry.Click;     // put peg at CTRL-1 rail joint with turnout.

    plain_track:=True;
    set_plain_track(True,True);

    if (spiral=True) and (pad_form.make_tools_normalize_transitions_menu_entry.Checked=True)
       then begin
              gocalc(0,0);                 // peg calcs.
              normalize_transition;        // ignore result.
            end;

    if (slewing=True) and (slew_s>turnoutx)
       then begin
              gocalc(0,0);                                 // peg calcs.
              pad_form.disable_slewing_menu_entry.Click;   // new template in unslewed section.
            end;

    clear_current_name;

    rail_options_form.restore_all_button.Click;  // 211c

    RESULT:=True;  // 227a

  finally
    show_and_redraw(True,True);                 // in case copy caused a current hide.
  end;//try
end;
//_______________________________________________________________________________________

function make_crossover(simple,allow_curviform,for_slip:boolean):boolean;

const
  slewed_cross_str:string='      Crossover  in  Slewed  Track'
  +'||Your control template contains a slew.'
  +'||If a crossover on a constant radius cannot be accommodated it is usually better to use one or more transition curves instead of slewing. The slewing function is intended primarily for plain track.'
  +'||It is generally unwise to construct a crossover if any part of it will be within the slewing zone (unless the amount of slew is very small and/or the slewing zone is very long).'

  +'||If you do so, some additional adjustment will probably be necessary. To adjust the new turnout created, do this:'
  +'||First using the MOVE SLEWING START (SHIFT+CTRL-F5) mouse action, move the slewing zone start marker on the newly created turnout until it aligns'
  +' with the slewing zone end marker on the original turnout.'
  +'||Then using the ADJUST SLEWING LENGTH (SHIFT+CTRL-F6) mouse action, move the slewing zone end marker on the newly created turnout until it aligns'
  +' with the slewing zone start marker on the original turnout.'
  +'||It is helpful to zoom in on these marks while making the adjustments, and to check the spacing between the tracks while doing so. On sharp curves or where there is significant slewing,'
  +' the rail alignments and clearances will need to be carefully checked and the slewing zone marks may need to be displaced slightly to maintain the proper clearance between the tracks.'
  +'||Use the SPACING-RING tool to check track spacing clearances.'

  +'||For more help information about slewing, select the GEOMETRY > SLEW (NUDGE) > SLEWING DATA... menu item.';

  curved_cross_str:string='      Crossover  with  Curviform  V-Crossings'
  +'||In a curviform V-crossing, the turnout radius continues through and beyond the crossing. If a crossover is constructed using such crossings, there will be an unacceptable'
  +' instant reverse curve at the mid-point, causing rough running. This is avoided if regular crossings are used; the curving in the main roads then applies unchanged'
  +' along the full length of the section between the crossings.'
  +'||It is possible to manually form a crossover with curviform V-crossings. One reason to do this might be that you are copying existing set-track components, rather than following'
  +' correct prototype practice. To form a crossover manually, do this:'
  +'||1. Ensure that the turnout-side adjacent track spacing dimension is correctly set to your current requirements (GEOMETRY > ADJACENT TRACK CENTRES... menu item). If the crossover'
  +' will be on a transition curve it is helpful to have the adjacent track drawn in (GENERATOR > GENERATOR SETTINGS > ADJACENT TRACK (TURNOUT-SIDE) menu item ticked).'
  +'||2. Shorten the turnout (F4 mouse action) to just beyond the TXP crossover mid-point mark.'
  +'||3. Move the fixing peg onto the TXP point (CTRL-5).'
  +'||4. Store this turnout, and copy it to the background (INSERT key).'
  +'||5. Rotate the turnout 180 degrees (SHIFT-F8).'
  +'||6. If the original turnout was not straight, change the sign of the curving radius, and change the radius dimension to match the current adjacent track spacing.'
  +' (And if the original turnout was on a transition curve, interchange the first and second radius dimensions.)'
  +'||7. If the crossover is on a constant curve, check by zooming in on the marks that the two radial centres are coincident.'
  +'||8. If the crossover is on a transition curve, align the transition length settings. Click "crossover on transition curve" below for more details.'
  +'||N.B.|Contrary to what might be expected at first sight, DO NOT swap the hand of the second turnout. The turnouts forming a crossover are always of the SAME hand, even when'
  +' the crossover is on a curve.';

  spiral_cross_str:string='      Crossover  on  Transition  Curve'

  +'||The MAKE ORDINARY CROSSOVER function creates a crossover by first storing the control template and making a copy of it on the background, and then creating a new control template turnout to form a crossover,'
  +' adjusting the curving line radii according to your current setting for the turnout-side adjacent track spacing.'

  +'||When the crossover is on a transition curve, some subsequent adjustment to the transition start and length settings may be necessary.'
  +' To aid this, the MAKE ORDINARY CROSSOVER function will if necessary extend the overall length of the newly-created control template.'
  +' This change can be undone later using the F4 mouse action, or the DO > SNAP and DO > EXIT TRACK menu functions, to reduce the overall length.'

  +'||To adjust the turnout created, do this:'

  +'||First using the MOVE TRANSITION START (SHIFT+CTRL-F3) mouse action, move the transition start marker on the newly created turnout until it aligns'
  +' with the transition end marker on the original turnout.'

  +'||Then using the ADJUST TRANSITION LENGTH (SHIFT+CTRL-F4) mouse action, move the transition end marker on the newly created turnout until it aligns'
  +' with the transition start marker on the original turnout.'

  +'||It is helpful to zoom in on these marks while making the adjustments, and to check the rail alignments while doing so. On sharp curves the best rail alignment with the adjacent track'
  +' may require one or both of the transition marks to be displaced slightly from these positions.'

  +'||A slight discrepancy in the radial centres may remain, because it is not mathematically possible to have two perfectly "parallel" transition curves.'

  +'||The adjustments detailed above will produce satisfactory crossovers on transition curves in the majority of situations found in practice.'
  +' Use the SPACING-RING tool to check the track spacings for adequate clearance.'

  +'||-----------------'
  +'|Maths notes:'

  +'||The TOOLS > MAKE DOUBLE-TRACK TS function uses slightly different maths, and can produce exactly concentric transition radii if the AUTO-ADJUST option is selected.'
  +' If the crossover mid-point is not within the transition zone, an alternative method of making a crossover is to use this function first to create an adjacent track.'
  +' The crossover is then created using the TEMPLATE > INSERT TURNOUT IN PLAIN TRACK menu item and the SNAKE THROUGH PEG (CTRL-F6) mouse action.'

  +'||If the crossover mid-point is within the transition zone, this method is not suitable.'

  +'||The alternative approach of applying the transition maths to the double-track centre-line is not used because this would introduce unacceptable distortion when the tracks are widely spaced,'
  +' and also disrupt the alignment of the original turnout.';


var
  dummy:extended;
  i:integer;
  temp_str:string;
  saved_control:Ttemplate_info;

begin
  RESULT:=False;
  if plain_track=True
     then begin
            alert(6,'    make  crossover  -  plain  track',
                    'The control template is plain track. It is not possible to construct a crossover from plain track.'
                   +'||You must first change to a turnout or half-diamond template, by selecting the `0TEMPLATE > INSERT TURNOUT IN PLAIN TRACK`1 or the `0TEMPLATE > INSERT HALF-DIAMOND IN PLAIN TRACK`1 menu items.',
                    '','','','','cancel','',0);
            EXIT;
          end;

  if retpar_i=1
     then begin
            alert(6,'    make  crossover  -  parallel  V-crossing',
                    'It is not possible to form a crossover when the V-crossing type is parallel.'
                   +'||To change to a regular or generic type of V-crossing suitable for a crossover, select the'
                   +'|TEMPLATE > V-CROSSING OPTIONS... menu item'
                   +'|or copy a suitable turnout from your storage box or background drawing.',
                  '','','','','cancel','',0);
            EXIT;
          end;

  if (xing_type_i=1) and (allow_curviform=False)    // 209c mod
     then repeat
            i:=alert(2,'    make  crossover  -  curviform  V-crossing',
                    '|Your current V-crossing type is curviform. It is not sensible to construct an ordinary or slip crossover or a regular ladder using curviform V-crossings.'
                   +'||It is possible to override this and form such a crossover manually - click `0more information`1 below.',
                    '','','','more  information','cancel  crossover','change  to  regular  V-crossing  and  continue',4);

            if i=4 then if alert_help(0,curved_cross_str,'crossover  on  transition  curve')=1 then alert_help(0,spiral_cross_str,'');

            if i=5 then EXIT;

            if i=6 then pad_form.regular_crossing_menu_entry.Click;

          until i<>4;

  if slewing=True
     then begin
            repeat
              i:=alert(3,'    make  crossover  -  slewed  track',
                      '||Your control template contains a slew.'
                     +'||It is generally unwise to construct a crossover if any part of it will be within the slewing zone.'
                     +'||The slewing function is intended primarily for plain track.',
                      '','','more  information','make  slewed  crossover','cancel  crossover','cancel  slewing  -  make  normal  crossover',3);
              case i of
                  3: alert_help(0,slewed_cross_str,'');
                  5: EXIT;
                  6: pad_form.disable_slewing_menu_entry.Click;
              end;//case
            until i<>3;
          end;

  if check_control_template_is_valid('crossover')=False then EXIT;  // 0.93.a  zero length

  if do_way_spacings(True,1)=False then EXIT;

  if (g>(47*inscale)) and (cpi.trtscent_pi<(134*inscale))
     then begin
            if alert(1,'    make  crossover',
               'Your current setting for the TS (turnout-side) track spacing is less than the 6ft WAY (11ft-2in centres, 134 inches) minimum for running lines on UK standard-gauge railways.'
              +'||For sidings and marshalling yards a closer track spacing down to 5ft WAY is sometimes used.'
              +'||If the tracks are sharply curved you will need additional track spacing above the minimum to provide sufficient clearance.',
               '','','','','cancel','continue - make  crossover  at  current  setting    ',0)=5 then EXIT;
          end;

  saved_control:=hold_the_control;  // 227a   // local stringlist not initialised.

  do_rollback:=False;
  adjacent_redraw;                         // update rail-edges.

  do_rollback:=False;

  pad_form.peg_on_txp_menu_entry.Click;                // put the peg on the mid-point.

  turnout_road_i:=-1;              // shorten turnout road to suit.

  if turnoutx<(txpx+2.5*scale)     // lengthen existing if nec. (2ft6ins arbitrary)
     then begin
            turnoutx:=txpx+2*scale;                                   // 2ft arbitrary.
            if turnoutx>turnoutx_max then turnoutx:=turnoutx_max;
          end;

  turnout_i:=1;     // length locked at turnoutx.

  do_rollback:=False;
  store_and_background(False,False);       // keep it (does a recalc) and copy to background.

  if keep_added=False          // he cancelled because still on T-55, or no memory for it.
     then begin
            unhold_the_control(saved_control);    // retrieve previous control template.
                                                  // (this is OK even if gauge change on T-55, because
                                                  //  gauge form is not showing Modal, we come back here first.)
            redraw_pad(True,False);
            EXIT;
          end;

  retain_on_make;    // do blanking, shoves, diffs, crossing entry straight, cancel platforms  213a

  show_and_redraw(True,False);   // in case copy caused a current hide.

  do_rollback:=False;
  pad_form.rotate_current_180_menu_entry.Click;      // rotate it.

  if spiral=True
     then begin

            if turnoutx<(txpx*2-xorg) then turnoutx:=txpx*2-xorg;     // and some extra length...
            if turnoutx>turnoutx_max then turnoutx:=turnoutx_max;

            gocalc(0,0);      // need to do new curving calcs, but no need to show results.

            kform_now:=kform;
            docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy);    // save current peg data for peg_curve calcs.

                                              // change sign of centre-line 1st radius.
            nomrad1:=0-(nomrad1-trtscent);    // and adjust for adjacent track.

                                              // change sign of centre-line 2nd radius.
            nomrad2:=0-(nomrad2-trtscent);    // and adjust for adjacent track.

            dummy:=nomrad1;         // swap the 2 radii as we are facing the other way.
            nomrad1:=nomrad2;
            nomrad2:=dummy;

            os:=pegx*2-(os+tst);    // approximate adjust transition start to match previous transition end.

            peg_curve;              // do curving calcs for the current peg position.
          end
     else begin                     // fixed curve or straight...

            turnoutx:=mvjpx;     // turnoutx:=txpx+2.5*scale; // reduce to 2ft6ins arbitrary.
            if turnoutx>turnoutx_max then turnoutx:=turnoutx_max;

            gocalc(0,0);      // need to do new curving calcs, but no need to show results.

            if (ABS(nomrad)<max_rad_test)    // curved turnout, so must adjust the curving rad...
               then begin
                      kform_now:=kform;
                      docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy);    // save current peg data for peg_curve calcs.

                                                    // change sign of centre-line radius.
                      nomrad:=0-(nomrad-trtscent);  // and adjust for adjacent track.

                      peg_curve;                     // do curving calcs for the current peg position.
                    end;
          end;

  clear_current_name;

  if half_diamond=True then begin
                              convert_to_turnout;
                              gocalc(0,0);
                              pad_form.peg_on_txp_menu_entry.Click;      // restore Ctrl-5.
                            end
                       else crop_approach;

  redraw_pad(False,True);                    // needed for rollback.

  show_and_redraw(True,False);       // rollback already done.

  if (spiral=True) and (simple=True) and (for_slip=False)     // 215a
     then begin
            repeat
              i:=alert(3,'    crossover  on  transition  curve',
                         '||The crossover just created is on a transition curve.'
                        +'||The newly created template may need to have the transition start and length settings adjusted to achieve the optimum alignment with the adjacent track.',
                         '','','','more  information','cancel  crossover','O K',4);
              case i of
                  4: alert_help(0,spiral_cross_str,'');

                  5: begin
                       if keeps_list.Count>0
                          then begin
                                 list_position:=keeps_list.Count-1;  // make last one in current in the keeps box.
                                 delete_keep(False, False);          // then delete the current keep.
                               end;
                       unhold_the_control(saved_control);  // 227a
                       redraw(True);
                     end;
              end;//case
            until i<>4;
          end;

  RESULT:=True;
end;
//______________________________________________________________________________

function make_branch_crossover:boolean;     // 209c

var
  save_timbering:boolean;
  i:integer;

begin
  RESULT:=False;  // init   not used

  save_timbering:=pad_form.square_on_menu_entry.Checked;

  if save_timbering=True
     then begin
            i:=alert(3,'    make  branch  crossover    -    timbering  style',
                    'The timbering style is currently set to square-on timbering.'
                   +'||For a branch crossover the equalized-incremental style is usually more appropriate.'
                   +'||Do you want Templot to change it?'
                   +'||If you answer yes, you may want to change it back later to the square-on style for subsequent templates.||The settings are in the `0real > timbering >`1 menu options.',
                    '','','','no  thanks','cancel  branch  crossover','yes  please',0);

            case i of

               4: save_timbering:=False;

               5: EXIT;

            end;//case
          end;

  if save_timbering=True
     then pad_form.equalized_incremental_menu_entry.Click;    // change to equalized timbering for a junction crossover...

  way_spacings_top_str:='    make  branch  crossover';        // 226c
  way_spacings_cancel_str:='cancel  branch  crossover     ';  // 226c

  if make_crossover(False,True,False)=False     // not simple, allow curviform
     then begin
            if save_timbering=True   // restore his square-on
               then pad_form.square_on_menu_entry.Click;

            EXIT;
          end;

  pad_form.notch_unlinked_from_current_menu_entry.Click;  // radio item - cancel any moving the notch in mouse actions.
  new_notch(get_peg_for_notch,False);

  pad_form.peg_on_mxp_menu_entry.Click;  // Ctrl-7

  gocalc(0,0);

  invert_handing;

  gocalc(0,0);

  shift_onto_notch(False,True);   // not clicked, minimum rotation

  turnoutx:=pegx;
  turnout_i:=1;      // length locked at MXP

  if xorg>turnoutx then xorg:=turnoutx;

  gocalc(0,0);

  pad_form.reset_notch_menu_entry.Click;    // does the redraw if needed

  RESULT:=True;  // not used
end;
//______________________________________________________________________________

function make_mirror_on_peg:boolean;

begin
  RESULT:=False;     // default init.

  if check_control_template_is_valid('mirror')=False then EXIT;  // 0.93.a  zero length

  do_rollback:=False;
  store_and_background(False,False);      // keep it (does a recalc) and copy to background.
  if keep_added=False then EXIT;          // he cancelled.

  retain_on_make;    // do blanking, shoves, diffs, crossing entry straight, cancel platforms  213a

  do_rollback:=False;
  pad_form.rotate_current_180_menu_entry.Click;      // rotate it.
  gocalc(0,0);                                       // need a recalc so can swap hand.

  swap_hand;               // to opposite hand.
  clear_current_name;
  RESULT:=True;
end;
//___________________________________________________________________________________________

function make_diamond_crossing:boolean;

const
  make_diamond_help_str:string='php/101     `0make  irregular  diamond-crossing`9'
  +'||It is not possible to create a full diamond-crossing from this half-diamond template.'
  +'||The difference between the V-crossing angle and the K-crossing angle is too great.'
  +'||Try using the `0F9`2 and `0F10`2 mouse actions, to adjust the V-crossing and K-crossing angles respectively.';

var
  temp1:integer;
  temp2:extended;
  cos_new_hdk:extended;
  cos_new_k3:extended;
  i:integer;

  new_k3,new_hdk:extended;
  new_k3n,new_hdkn:extended;

begin
  RESULT:=False;     // default init.

  if check_control_template_is_valid('diamond - crossing')=False then EXIT;  // 0.93.a  zero length

  try

    if half_diamond=False      // force a half-diamond...
       then begin
              if plain_track=True then insert_half_diamond
                                  else convert_to_regular_half_diamond;
              gocalc(0,0);
            end;

    pad_form.reset_peg_menu_entry.Click;
    gocalc(0,0);

    cos_new_k3:=COS(k3);   // keep compiler happy ...
    cos_new_hdk:=COS(hdk);
    new_k3:=k3;
    new_hdk:=hdk;

    if tradius_is_straight=False    // check we can make a valid irregular diamond..
       then begin
              cos_new_hdk:=(torgy-g)/(tradius-g);                             // irregular diamond has differing K-crossing angles.
              cos_new_k3:=cos_new_hdk-(th-h)/(g-tradius);                     // calc new V-crossing angle.
              if (ABS(cos_new_k3)>(1-minfp)) or (ABS(cos_new_hdk)>(1-minfp))  // invalid crossing angles (COS out of range - rails do not intersect?).
                   then begin
                          repeat
                            i:=alert(6,'php/101   impossible  diamond - crossing',
                                       'It is not possible to create a full diamond-crossing using the current settings.'
                                      +'||The rails would not intersect to create a new V-crossing.',
                                       '','','','?  help','cancel','',4);
                            if i=4 then alert_help(0,make_diamond_help_str,'');
                          until i<>4;
                          EXIT;
                        end;

              if (xing_type_i=0) and (irreg_crossings_msg_pref=False)
                 then begin

                        alert_box.preferences_checkbox.Checked:=False;       //%%%%
                        alert_box.preferences_checkbox.Visible:=True;

                        repeat
                          i:=alert(7,'php/102   make  irregular  diamond - crossing',
                                   'Using the current settings, you are about to make an irregular-type diamond-crossing having regular-type V-crossings.'
                                  +'||The result will be that the diagonal road will not follow a single ruling curve through the V-crossings.'
                                  +'||This is normally correct only when a half-diamond template is forming part of a crossover with a turnout also having a regular or generic type of V-crossing,'
                                  +' or the half-diamond is connected to a return curve.'
                                  +'||Otherwise it is generally better to use curviform V-crossings in an irregular diamond-crossing, so that the diagonal road follows a single curve through the full diamond.'
                                  +'||In special cases it is possible to mix different types of V-crossing in the same diamond-crossing, and/or to have a different diagonal radius in each half-diamond.'
                                  +' Make such changes after creating the full diamond-crossing, so that the K-crossing angles in each half always match.'
                                  +'||(This diamond-crossing will be irregular-type because the current V-crossing and K-crossing angles are unequal.)',
                                   '','','','more  information  about  types  of  V-crossing','cancel','continue  with  regular  V-crossings',4);

                          if i=4 then alert_help(0,v_xing_types_help_str,'');
                        until i<>4;

                        irreg_crossings_msg_pref:=alert_box.preferences_checkbox.Checked;    //%%%%
                        alert_box.preferences_checkbox.Visible:=False;

                        if i=5 then EXIT;
                      end;

              if (xing_type_i=-1) and (irreg_crossings_msg_pref=False)
                 then begin

                        alert_box.preferences_checkbox.Checked:=False;       //%%%%
                        alert_box.preferences_checkbox.Visible:=True;

                        repeat
                          i:=alert(7,'php/103   make  irregular  diamond - crossing',
                                   'Using the current settings, you are about to make an irregular-type diamond-crossing having generic-type type V-crossings.'
                                  +'||The result will be that the diagonal road will not follow a single ruling curve through the V-crossings.'
                                  +'||This is normally correct only when a half-diamond template is forming part of a crossover with a turnout also having a regular or generic type of V-crossing,'
                                  +' or the half-diamond is connected to a return curve.'
                                  +'||Otherwise it is generally better to use curviform V-crossings in an irregular diamond-crossing, so that the diagonal road follows a single curve through the full diamond.'
                                  +'||In special cases it is possible to mix different types of V-crossing in the same diamond-crossing, and/or to have a different diagonal radius in each half-diamond.'
                                  +' Make such changes after creating the full diamond-crossing, so that the K-crossing angles in each half always match.'
                                  +'||(This diamond-crossing will be irregular-type because the current V-crossing and K-crossing angles are unequal.)',
                                   '','','','more  information  about  types  of  V-crossing','cancel','continue  with  generic  V-crossings',4);

                          if i=4 then alert_help(0,v_xing_types_help_str,'');
                        until i<>4;

                        irreg_crossings_msg_pref:=alert_box.preferences_checkbox.Checked;    //%%%%
                        alert_box.preferences_checkbox.Visible:=False;

                        if i=5 then EXIT;
                      end;

              new_hdk:=ARCCOS(cos_new_hdk);
              new_hdkn:=1/TAN(new_hdk);

              new_k3:=ARCCOS(cos_new_k3);
              new_k3n:=1/TAN(new_k3);

              if (ABS(new_k3n)<0.5) or (ABS(new_hdkn)<0.5)
                   then begin
                          repeat
                            i:=alert(6,'php/107   invalid  diamond - crossing',
                                       'It is not possible to create a full diamond-crossing using the current settings.'
                                      +'||One of the new crossing angles would be shorter than 1:0.5',
                                       '','','','?  help','cancel','',4);
                            if i=4 then alert_help(0,make_diamond_help_str,'');
                          until i<>4;
                          EXIT;
                        end;

            end;//if irregular

    store_and_background(False,False);      // keep it (does a recalc) and copy to background.
    if keep_added=False then EXIT;          // he cancelled.

    if tradius_is_straight=False     // set new crossing angles if not a regular diamond ...
       then begin
              hdk:=new_hdk;
              hdkn:=1/TAN(hdk);

              k3:=new_k3;
              k3n:=1/TAN(k3);

              gocalc(0,0);
            end;

    making_a_diamond:=True;     // 215a global flag for retain_on_make  (retain slip adjustments)
    retain_on_make;             // do blanking, shoves, diffs, crossing entry straight, cancel platforms  213a

    turnout_road_i:=0;     // reset normal turnout road (in case first half-diamond is part of crossover).
    turnout_i:=0;          // and free overall length.

    snake_onto_bgnd_peg(keeps_list.Count-1,False,False);
    invert_handing;

    temp1:=hd_vcheck_rails;      // shortening code for half-diamond v-crossing check rails.
    case temp1 of
       1: hd_vcheck_rails:=2;    // swap shortened ms or ts v-crossing check rail for single-slip.
       2: hd_vcheck_rails:=1;
    end;//case

    temp1:=hd_timbers;           // extension code for slip timbers.
    case temp1 of
       1: hd_timbers:=2;         // swap extended sides for single-slip.
       2: hd_timbers:=1;
    end;//case

    temp2:=ccd.end_diff_mk.len_diff;                        // swap modified k-crossing check rail lengths.
    ccd.end_diff_mk.len_diff:=ccd.end_diff_dk.len_diff;
    ccd.end_diff_dk.len_diff:=temp2;

    RESULT:=True;
  except
    RESULT:=False;
    show_modal_message('Sorry, the diamond-crossing calculations have failed.');  // modal_message needed if math form is showing modal
  end;//try
end;
//____________________________________________________________________________________________

function check_grey_paper:boolean;     // return True if the paper colour is near to mid grey.

var
  red,green,blue:integer;

begin
  red:=paper_colour AND $000000FF;
  green:=paper_colour AND $0000FF00;
  blue:=paper_colour AND $00FF0000;

  if (red>$70) and (red<$90) and (green>$7000) and (green<$9000) and (blue>$700000) and (blue<$900000)
     then RESULT:=True
     else RESULT:=False;

end;
//____________________________________________________________________________________________

function check_dark_paper:boolean;     // return True if the paper colour is black or very dark.

var
  red,green,blue:integer;

begin
  red:=paper_colour AND $000000FF;
  green:=paper_colour AND $0000FF00;
  blue:=paper_colour AND $00FF0000;

  if (red<$60) and (green<$6000) and (blue<$600000)    // RGB ($650 = decimal 96).
     then RESULT:=True
     else RESULT:=False;
end;
//____________________________________________________________________________________________

function mouse_x(X:integer):extended;   // return x mm at this pad X pixels.

begin
  if ABS(fx)<minfp then RESULT:=0               // div by zero on startup.
                   else RESULT:=(X+gx-ex)/fx;
  if paper_bunching=True
     then begin
            if X<bunch_start then EXIT;
            if X<(bunch_start+bunch_gap) then RESULT:=0                     // in the bunch!
                                         else RESULT:=RESULT+bunching_jump;
          end;
end;
//______________________________________________________________________________________

function mouse_y(X,Y:integer):extended;   // return y mm at this pad Y pixels.

begin
  if ABS(fy)<minfp then RESULT:=0               // div by zero on startup.
                   else RESULT:=(Y+gy-by)/fy;

  if paper_bunching=True
     then begin
            if X<bunch_start then EXIT;
            if X<(bunch_start+bunch_gap) then RESULT:=0                     // in the bunch!
                                         else RESULT:=RESULT-bunching_shear;
          end;

end;
//__________________________________________________________________________________________

function pad_X(x:extended):integer;   // return pad X pixels at this x mm.

begin
  RESULT:=Round(x*fx-gx+ex);
end;
//______________________________________________________________________________________

function pad_Y(y:extended):integer;   // return pad Y pixels at this y mm.

begin
  RESULT:=Round(y*fy-gy+by);
end;
//__________________________________________________________________________________________

procedure update_rollback_register;    // maintain the roll-back register.
                                       // undo_index always points to last-entered data, i.e. matching the control template.

begin
  if turnoutx=0 then EXIT;   // 0.93.a  don't put invalid (zero-length) template in register.

  INC(undo_index);                                 // roll forward to next slot.
  if undo_index>undo_c then undo_index:=0;
  fill_kd(rollback_reg[undo_index].rollback_info); // put control template in slot.

  rollback_reg[undo_index].rollback_name_str:=current_name_str;   // 0.93.a
  rollback_reg[undo_index].rollback_memo_str:=current_memo_str;   // ...

  rollback_reg[undo_index].valid_flag:=True;       // and flag it valid.
end;
//_______________________________________________________________________________________

procedure init_rollbacks;    // init all roll-backs and parking bays

var
  i:integer;

begin
  for i:=0 to undo_c do begin
    rollback_reg[i].valid_flag:=False;        // initial flag no valid content.

    rollback_reg[i].rollback_name_str:='';    // 0.93.a
    rollback_reg[i].rollback_memo_str:='';    // ...

    rollback_reg[i].rollback_info:=hold_the_control;  // 227a  create stringlists
  end;

  undo_index:=0;
  pad_form.undo_changes_menu_entry.Enabled:=True;
  pad_form.redo_changes_menu_entry.Enabled:=True;


  for i:=0 to notch_c do begin     // also init the notch rollback...
    undo_notch[i].notch_x:=0;      // x
    undo_notch[i].notch_y:=0;      // y
    undo_notch[i].notch_k:=0;      // angle
  end;//for
  notch_index:=0-1;         // increments to zero on first use.


  for i:=0 to parking_c do parking_bay[i]:=hold_the_control;  // 227a   create stringlists   // added 0.93.a

  invalidated_zero_save:=hold_the_control;  // 227a   create stringlists

  org_template:=hold_the_control;  // 227a  create stringlists   // 224a

end;
//__________________________________________________________________________________________

procedure shift_all_group(ask_sync_shapes:boolean);  // add current xshift_keeps, yshift_keeps to all selected keeps,
                                                    // and then clear the shifts.

var
  now_kd:Tkeep_dims;
  n:integer;

begin
  try
    if keeps_list.Count<1 then EXIT;     // no keeps to shift.

    for n:=0 to (keeps_list.Count-1) do begin

      with Ttemplate(keeps_list.Objects[n]) do begin

        now_kd:=template_info.keep_dims;    // get the current keep data (don't need the shoves).

        with now_kd do begin

          if group_selected=False then CONTINUE;     // don't shift this one.

          with box_dims1.transform_info do begin
            x2_shift:=x2_shift+xshift_keeps;
            y2_shift:=y2_shift+yshift_keeps*box_dims1.turnout_info1.hand;
          end;//with

        end;//with

        with template_info.notch_info do begin             // update the stored pegging data..
          notch_x:=notch_x+xshift_keeps;
          notch_y:=notch_y+yshift_keeps;
        end;//with

        new_stamp_wanted:=True;            // True=has been shifted/rotated/mirrored, needs a new timestamp on rebuilding.
        template_info.keep_dims:=now_kd;   // update the keep record.

      end;//with
    end;//for next n

    if (bgnd_form.bgnd_shapes_listbox.Items.Count<1) or (bgnd_form.allow_sync_checkbox.Checked=False) then EXIT;

    if mouse_shift_sync_wanted=True  // 219a ..
       then begin
              shift_all_shapes(True,xshift_keeps,yshift_keeps);
              mouse_shift_sync_wanted:=False;                    // only once

              shapes_saved:=False;      // need a resave.
              shapes_current_state;

              do_rollback:=False;
              redraw(True);
            end
       else begin                                                      // added 229c
              if ask_sync_shapes=True
                 then begin
                        rebuild_group(False,False);   // needed to show shifted
                        
                        if alert(4,'    shift  background  shapes  in  sync ?',
                                   'The group templates have been shifted.'
                                  +'||Do you now want your background shapes to be shifted by the same amount?'
                                  +'||Any shapes which have their `0allow to sync with templates`1 option box unticked will not be shifted.',
                                   '','','','','no  thanks','yes  please',0)=5
                           then EXIT;

                        shift_all_shapes(True,xshift_keeps,yshift_keeps);      // True=sync 226b

                        shapes_saved:=False;      // need a resave.
                        shapes_current_state;

                        do_rollback:=False;
                        redraw(True);
                      end;
            end;

  finally
    xshift_keeps:=0;
    yshift_keeps:=0;
  end;//try
end;
//_______________________________________________________________________________________

procedure twist_all_group(ask_sync_shapes:boolean);  // rotate all group templates.
                                                     // and then clear kform_keeps.

var
  now_kd:Tkeep_dims;
  n:integer;

  x,y,hand:extended;

  pin,pout:Tpex;

begin
  try
    if keeps_list.Count<1 then EXIT;     // no keeps to rotate.

    for n:=0 to (keeps_list.Count-1) do begin

      with Ttemplate(keeps_list.Objects[n]) do begin

        now_kd:=template_info.keep_dims;    // get the current keep data (don't need the shoves).

        with now_kd do begin

          if group_selected=False then CONTINUE;     // don't rotate this one.

          with box_dims1.transform_info do begin

            hand:=box_dims1.turnout_info1.hand;
            k_shift:=k_shift-kform_keeps*hand;    // update angle.
            normalize_angle(k_shift);

            x:=x2_shift-notchx;                   // shift to origin
            y:=y2_shift*hand+datum_y-notchy;

            x2_shift:=x*COS(0-kform_keeps)-y*SIN(0-kform_keeps)+notchx;                // rotate and shift back onto notch.
            y2_shift:=(x*SIN(0-kform_keeps)+y*COS(0-kform_keeps)+notchy-datum_y)*hand;

            with template_info.notch_info do begin    // update the stored pegging data...

              x:=notch_x-notchx;                     // shift pegging data to origin.
              y:=notch_y-notchy;

              notch_x:=x*COS(0-kform_keeps)-y*SIN(0-kform_keeps)+notchx;      // rotate and get new data.
              notch_y:=x*SIN(0-kform_keeps)+y*COS(0-kform_keeps)+notchy;

              notch_k:=notch_k-kform_keeps;
              normalize_angle(notch_k);

            end;//with

            pin.x:=box_dims1.mod_text_x;      // template label position modifiers...
            pin.y:=box_dims1.mod_text_y;

            dotransform((0-kform_keeps),0,0,pin,pout);

            box_dims1.mod_text_x:=pout.x;
            box_dims1.mod_text_y:=pout.y;

          end;//with
        end;//with

        new_stamp_wanted:=True;           // True=has been shifted/rotated/mirrored, needs a new timestamp on rebuilding.
        template_info.keep_dims:=now_kd;  // update the keep record.

      end;//with
    end;//for next n

    if (bgnd_form.bgnd_shapes_listbox.Items.Count<1) or (bgnd_form.allow_sync_checkbox.Checked=False) then EXIT;

    if mouse_rotate_sync_wanted=True                        // 219a ..
       then begin
              bgnd_form.lock_notch_radiobutton.Checked:=True;
              rotate_centre_code:=1;

              rotate_all_shapes(True,True,0-kform_keeps);  //   True,True = doing sync, include pictures

              shapes_saved:=False;      // need a resave.
              shapes_current_state;

              do_rollback:=False;
              redraw(True);

              mouse_rotate_sync_wanted:=False;             // only once
             end
       else begin                       // added 229c
              if ask_sync_shapes=True
                 then begin
                        rebuild_group(False,False);     // needed to show rotated

                        if alert(4,'    rotate  background  shapes  in  sync ?',
                                   'The group templates have been rotated.'
                                  +'||Do you now want your background shapes to be rotated by the same amount?'
                                  +'||Any shapes which have their `0allow to sync with templates`1 option box unticked will not be rotated.',
                                   '','','','','no  thanks','yes  please',0)=5
                           then EXIT;

                        bgnd_form.lock_notch_radiobutton.Checked:=True;
                        rotate_centre_code:=1;

                        rotate_all_shapes(True,True,0-kform_keeps);  //   True,True = doing sync, include pictures

                        shapes_saved:=False;      // need a resave.
                        shapes_current_state;

                        do_rollback:=False;
                        redraw(True);
                      end;
            end;

  finally
    kform_keeps:=0;
  end;//try
end;
//______________________________________________________________________________

procedure get_ring_size;

const
  dia_help_str:string='    Spacing  Ring  Inner  Diameter'
  +'||Enter the required inner diameter for the spacing ring tool in mm.'
  +'||This dimension is often set to the "6ft way" minimum spacing (clear between the rails) for double track (6''-0.1/2" on GWR with rail 2.75" wide).'
  +'||For more notes about using the spacing-ring tool click MORE GENERAL INFORMATION below.';

var
  n:integer;
  od:Toutdim;

begin
  n:=putdim(dia_help_str,1,'spacing-ring  inner  diameter',ring_dia,True,False,False,False);   // no neg, preset ok, zero ok, don't terminate on zero.
  if n<>0 then EXIT;
  if getdims('spacing-ring  size ...',ring_help_str,grid_form,n,od)=True
     then begin
            ring_dia:=od[0];

            if ring_dia=def_req then ring_dia:=134*inscale-g-railtop*2;   // spacing ring dia. (6ft way)
            if ring_dia>screenx_max then ring_dia:=screenx_max;

            rings[0,2]:=ring_dia;
            rings[0,3]:=ring_dia+railtop*2;                               // outer diameter.

            grid_form.dia_label.Caption:='dia: '+round_str(ring_dia,2)+' mm';
            do_rollback:=False;
            redraw(True);
          end;
end;
//______________________________________________________________________________________

procedure get_ring_location;

var
  n:integer;
  od:Toutdim;

begin
     putdim('',1,'spacing-ring  position  X',rings[0,0],False,True,False,False);   // neg ok, no preset, zero ok, don't terminate on zero.
  n:=putdim('',1,'spacing-ring  position  Y',rings[0,1],False,True,False,False);   // neg ok, no preset, zero ok, don't terminate on zero.
  if n<>1 then EXIT;
  if getdims('spacing-ring  position ...',ring_help_str,grid_form,n,od)=True
     then begin
            rings[0,0]:=od[0];
            rings[0,1]:=od[1];
            do_rollback:=False;
            redraw(True);
          end;
end;
//______________________________________________________________________________________

procedure set_six_foot_ring;

begin
  rings[0,2]:=134*inscale-g-railtop*2;   // spacing ring dia. (6ft way);
  rings[0,3]:=rings[0,2]+railtop*2;      // outer diameter.

  ring_dia:=rings[0,2];
  grid_form.dia_label.Caption:='dia: '+round_str(ring_dia,2)+' mm';
  do_rollback:=False;
  redraw(True);
end;
//_______________________________________________________________________________________

procedure shift_rotate_group(x_move,y_move,k_rot:extended; egg_timer:boolean);

begin
    xshift_keeps:=x_move;
    yshift_keeps:=y_move;
    kform_keeps:=k_rot;

    if (xshift_keeps<>0) or (yshift_keeps<>0)      // update after mouse shift all keeps.
      then begin
             if egg_timer=True then Screen.Cursor:=crHourglass;
             shift_all_group(True);
             if kform_keeps=0 then rebuild_group(False,False);   // no need to do this if it will be done again below.
           end;

    if kform_keeps<>0      // update after mouse twist all keeps.
      then begin
             if egg_timer=True then Screen.Cursor:=crHourglass;
             twist_all_group(True);
             rebuild_group(False,False);
            end;

    if egg_timer=True then Screen.Cursor:=crDefault;
end;
//________________________________________________________________________________________

procedure unlink_group;

begin
  pad_form.unlink_group_from_notch_menu_entry.Enabled:=False;
  group_notch_linked:=False;
  pad_form.group_linked_warning_panel.Hide;
end;
//________________________________________________________________________________________

procedure enable_slewing(mode:integer; do_peg_calcs_first:boolean);

var
  dummy:extended;

begin
  if do_peg_calcs_first=True
     then begin
            kform_now:=kform;                                                   // before we change the mode..
            docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy); // current peg data for peg_curve calcs.
          end;

  with pad_form do begin

    case mode of
              1: begin
                   slew_mode1_menu_entry.Checked:=True;   // radio item.
                   slew_mode:=1;
                   adjust_slew2_factor_menu_entry.Enabled:=False;
                 end;

              2: begin
                   slew_mode2_menu_entry.Checked:=True;   // radio item.
                   slew_mode:=2;
                   adjust_slew2_factor_menu_entry.Enabled:=True;
                 end;

            else run_error(13);
    end;//case

    slewing_panel.Caption:='  caution :  template  contains  a  SLEW  ( mode  '+IntToStr(slew_mode)+' )';
    info_form.slew_caution_mode_label.Caption:='The control template contains a mode '+IntToStr(slew_mode)+'  SLEW . Smallest radius info is not available.';

    if (plain_track=False) or (slew_mode=2)     // min rad info not available for slewed turnouts or any mode 2.
       then begin
              with info_form do begin
                min_rad_box.Hide;               // so can't focus for min rad change button...
                limit_rad_box.Hide;
                slew_warn_panel.Show;           // show warning in info form.
              end;//with
            end;

    slew_nudge_menu_entry.Checked:=True;

    adjust_slew_start_menu_entry.Enabled:=True;
    adjust_slew_length_menu_entry.Enabled:=True;
    adjust_slew_amount_menu_entry.Enabled:=True;

    if ABS(slew_angle)>(Pi/9)  // 20 degrees
       then slewing_panel.Font.Color:=clYellow
       else slewing_panel.Font.Color:=clAqua;

    slewing_panel.Show;               // and on pad.

    if (info_form.Left<(slewing_panel.Left+slewing_panel.Width+4))
    and (info_form.Top<(slewing_panel.Top+slewing_panel.Height+4))
        then info_form.Top:=slewing_panel.Top+slewing_panel.Height+4;  // ensure warning not obscured by info.

  end;//with

  slewing:=True;
  peg_curve;        // slew curve onto peg.
  redraw(True);
end;
//________________________________________________________________________________________

procedure pad_mouse_up(mouse_button:TMouseButton; shift_state:TShiftState; X,Y:integer);

var
  draw_mouse_up_X, draw_mouse_up_Y:integer;
  mouse_up_x, mouse_up_y:extended;

  new_shape:Tbgnd_shape;
  i, n:integer;

  bgk:integer;
  X_left, X_right, Y_bottom, Y_top:integer;

  aq:integer;
  nk,array_max:integer;
  xint,yint:integer;
  this_is_contained:boolean;
  first_one_found:boolean;

  time_str:string;

  x1,y1,x2,y2:extended;
  wl_factor:extended;

begin

  if mouse_button=mbLeft
     then begin
            with pad_form do begin
              yellow_drag_panel.Visible:=False;   // 233b   drawing track with mouse, indicate done
{
              store_draw_button.Height:=yellow_shape.Height;
              store_draw_button.Width:=yellow_shape.Width;
              store_draw_button.Left:=yellow_shape.Left;
              store_draw_button.Top:=yellow_shape.Top;
}

            end;//with
          end;


  if (allow_left_button_pan=True) or (mouse_button=mbMiddle) then Screen.Cursor:=crDefault;  // 0.91.c
  allow_left_button_pan:=False;   // 0.91.c

  if (mouse_modify>0) and (mouse_action_button_down=True)       // mouse action in progress
     then begin
            mouse_action_release(False);
            EXIT;
          end;

  if (zoom_rectangle=True) and (rectangle_in_progress=True)
     then begin
            zoom_rectangle:=False;
            rectangle_in_progress:=False;
            rectangle_exists:=False;
            panning_form.zoom_rectangle_latching_toolbutton.Down:=False;
            pad_form.zoom_rectangle_latching_toolbutton.Down:=False;

                 // zoom to show the last rectangle drawn...

            if screeny<minfp then EXIT;         // ??
            wl_factor:=screenx/screeny;

            x1:=mouse_x(X);              // mm...
            y1:=mouse_y(X,Y);

            x2:=mouse_x(draw_mouse_down_X);
            y2:=mouse_y(draw_mouse_down_X,draw_mouse_down_Y);

            screenx:=ABS(x1-x2);

            if screenx<(ABS(y1-y2)*wl_factor) then screenx:=ABS(y1-y2)*wl_factor;

            if screenx<screenx_min then screenx:=screenx_min; // minimum for screen width (max zoom in).
            if screenx>screenx_max then screenx:=screenx_max; // maximum zoom out.

                   // centralize on pad..

            if x1>x2 then zoom_offsetx:=x2-(screenx-(x1-x2))/2
                     else zoom_offsetx:=x1-(screenx-(x2-x1))/2;

            if y1>y2
               then begin
                      if wl_factor>minfp then zoom_offsety:=y2-(screenx/wl_factor-(y1-y2))/2;
                    end
               else begin
                      if wl_factor>minfp then zoom_offsety:=y1-(screenx/wl_factor-(y2-y1))/2;
                    end;

            pad_form.lock_scaling_menu_entry.Click;    // lock pad zoom.
            gocalc(2,0);

            cancel_adjusts(False);   // to clear the pad caption.

            if bgnd_form.Showing=True then bgnd_form.BringToFront;   // clicking shapes were in progress when he clicked the zoom rectangle tool button.
            EXIT;
          end;

  if (group_fence_rectangle=True) and (rectangle_in_progress=True)
     then begin
            group_fence_rectangle:=False;
            rectangle_in_progress:=False;
            rectangle_exists:=False;
            pad_form.group_fence_latching_toolbutton.Down:=False;

                 // group select all templates inside the last rectangle drawn...

            if any_bgnd>0
               then begin

                      x1:=mouse_x(X);              // mm...
                      y1:=mouse_y(X,Y);

                      x2:=mouse_x(draw_mouse_down_X);
                      y2:=mouse_y(draw_mouse_down_X,draw_mouse_down_Y);

                      if pad_form.group_fence_whole_menu_entry.Checked=True  // wholly contained templates?
                         then begin
                                X_left:=  Round(min(x1,x2)*100);  // 1/100ths mm. Use actual clicked corners.
                                X_right:= Round(max(x1,x2)*100);
                                Y_bottom:=Round(min(y1,y2)*100);
                                Y_top:=   Round(max(y1,y2)*100);
                              end
                         else begin
                                X_left:=  Round((min(x1,x2)-g/2)*100);  // 1/100ths mm. Add a tolerance og g/2 (arbitrary),
                                X_right:= Round((max(x1,x2)+g/2)*100);  // so can just click on a template to select it.
                                Y_bottom:=Round((min(y1,y2)-g/2)*100);  // !!! n.b. g/2 is per the control template, not the background template (for simplicity).
                                Y_top:=   Round((max(y1,y2)+g/2)*100);
                              end;

                      first_one_found:=False; // init.

                      for bgk:=0 to (keeps_list.Count-1) do begin

                        with Ttemplate(keeps_list.Objects[bgk]) do begin

                          if bg_copied=False then CONTINUE;  // this one not a background template.

                          try
                            with template_info.bgnd_keep do begin

                            this_is_contained:=False;  // init flag for this template.

                            if pad_form.group_fence_whole_menu_entry.Checked=True  // wholly contained templates?

                               then begin   // yes, so only need to check the template's enclosing rectangle...

                                      if (xlist_min<X_left) or (xlist_max>X_right) or (ylist_min<Y_bottom) or (ylist_max>Y_top)
                                         then CONTINUE  // not this one, next template.
                                         else this_is_contained:=True;
                                    end
                               else begin  // no, to test for partially contained templates we must search the whole template...

                                      for aq:=29 downto 0 do begin    // 205e was 25    ignore FB foot lines.

                                        array_max:=intarray_max(list_bgnd_rails[aq,0]);
                                        if array_max=0 then CONTINUE;                       // empty rail, next aq.

                                        for nk:=0 to array_max do begin

                                          xint:=intarray_get(list_bgnd_rails[aq,0],nk);
                                          yint:=intarray_get(list_bgnd_rails[aq,1],nk);

                                          if (xint>X_left) and (xint<X_right) and (yint>Y_bottom) and (yint<Y_top)
                                             then begin
                                                    this_is_contained:=True;
                                                    BREAK;                      // ignore remainder of this rail.
                                                  end;
                                        end;//next nk

                                        if this_is_contained=True then BREAK;   // ignore remaining rails.
                                      end;//next aq
                                    end;

                                    if this_is_contained=True   // found a bgnd template for group select...
                                       then begin

                                              if (first_one_found=False) and (pad_form.group_fence_new_menu_entry.Checked=True) then clear_all_selections;

                                              first_one_found:=True;

                                              if template_info.keep_dims.box_dims1.bgnd_code_077=-1
                                                 then group_selected:=False      // library template???
                                                 else begin
                                                        if pad_form.group_fence_toggle_menu_entry.Checked=True
                                                           then group_selected:= NOT group_selected                                  // toggle.
                                                           else group_selected:= NOT pad_form.group_fence_remove_menu_entry.Checked; // add or new, or remove.
                                                      end;
                                            end;
                            end;//with bgnd_keep
                          except
                            EXIT;
                          end;//try
                        end;//with template
                      end;//next template.
                    end;//if any bgnd

            cancel_adjusts(False);   // to clear the pad caption.

            if bgnd_form.Showing=True then bgnd_form.BringToFront;   // clicking shapes were in progress when he clicked the zoom rectangle tool button.
            redraw_pad(True,False);
            EXIT;
          end;//if group rectangle

              // 0.93.a ...

  if (output_boundary_rectangle=True) and (rectangle_in_progress=True)
     then begin
            output_boundary_rectangle:=False;
            rectangle_in_progress:=False;
            rectangle_exists:=False;
            pad_form.output_rectangle_latching_toolbutton.Down:=False;

                       // set globals...

            output_rectangle_x1:=mouse_x(X);              // mm...
            output_rectangle_y1:=mouse_y(X,Y);

            output_rectangle_x2:=mouse_x(draw_mouse_down_X);
            output_rectangle_y2:=mouse_y(draw_mouse_down_X,draw_mouse_down_Y);

            output_rectangle_width:=ABS(output_rectangle_x2-output_rectangle_x1);
            output_rectangle_height:=ABS(output_rectangle_y2-output_rectangle_y1);

            draw_export_rectangle_flag:=True; // make sure it's visible.

            cancel_adjusts(False);   // to clear the pad caption and restore cursor.

            redraw_pad(True,False);

            if export_form_was_showing=True
               then begin
                      export_form_was_showing:=False;
                      pad_form.export_file_menu_entry.Click;
                    end;
            EXIT;
          end;

 if (drawn_shape_rectangle=True) and (rectangle_in_progress=True)  // add the shape...
     then begin
            drawn_shape_rectangle:=False;
            rectangle_in_progress:=False;
            rectangle_exists:=False;

                       // set globals...

            shape_rectangle_x1:=mouse_x(X);              // mm...
            shape_rectangle_y1:=mouse_y(X,Y);

            shape_rectangle_x2:=mouse_x(draw_mouse_down_X);
            shape_rectangle_y2:=mouse_y(draw_mouse_down_X,draw_mouse_down_Y);

            add_shape(0);   //   0 = by drawing
            EXIT;
          end;


  if ( (mouse_draw_lines=False) and (draw_template_direct=False) ) or (mouse_drawing_in_progress=False) then EXIT;

                // drawn mouse line terminates on mouse up...

  draw_mouse_up_X:=X;      // screen coords.
  draw_mouse_up_Y:=Y;

  mouse_up_x:=mouse_x(X);    // and in mm. for shape list
  mouse_up_y:=mouse_y(X,Y);

  mouse_drawing_in_progress:=False;

  if draw_template_direct=True    // 233b
     then begin
            do_draw_template_direct(mouse_down_x,mouse_down_y,mouse_up_x,mouse_up_y);   // create new control template
            draw_template_direct:=False;                                                // only one
          end
     else begin          // add it as shape ...

            with pad_form.Canvas do begin       // first draw it immediately on the pad, so no redraw of shapes needed to show it.
              Pen.Width:=1;
              Pen.Mode:=pmCopy;
              if bgnd_form.dotted_radio_button.Checked=True
                 then begin
                        Brush.Color:=paper_colour;  // for gaps in dotted lines
                        Brush.Style:=bsClear;

                        TextOut(0,0,'');    // Delphi bug?
                        Pen.Style:=psDot;
                      end
                 else Pen.Style:=psSolid;

              Pen.Color:=shapes_colour;

              MoveTo(draw_mouse_down_X,draw_mouse_down_Y);   // saved line starting point.
              LineTo(draw_mouse_up_X,draw_mouse_up_Y);       // line ends on mouse up.
            end;//with

            new_shape:=init_bgnd_shape;  // 234b

            with new_shape do begin

              shape_name:='';  // init

              if  (bgnd_form.Visible=True)
              and (bgnd_form.line_radio_button.Checked=True)
              and (bgnd_form.by_drawing_radiobutton.Checked=True)
                  then shape_name:=bgnd_form.name_editbox.Text;

              is_brick:=bgnd_form.add_brick_checkbox.Checked;  // 234b

              is_kerf:=bgnd_form.add_kerf_lines_checkbox.Checked;  // 244a

              if shape_name=''
                 then begin
                        if is_brick=True
                           then shape_name:='drawn brick splint'
                           else begin
                                  if is_kerf=True
                                     then shape_name:='drawn kerf line'
                                     else shape_name:='drawn line';
                                end;
                      end;

              hide_bits:=0;    // 214a  normal visibility
              option_bits:=0;  // 226b  allow sync

              shape_code:=0;                                                       // 0=line, 1=rectangle, 2=circle.
              if bgnd_form.dotted_radio_button.Checked=True then shape_style:=2    // dotted line.
                                                            else shape_style:=0;   // solid line.

              show_transparent:=False;  // 229a init

              p1.x:=mouse_down_x;  // saved line starting point..
              p1.y:=mouse_down_y;

              p2.x:=mouse_up_x;    // end point on mouse up..
              p2.y:=mouse_up_y;

              rect_rot:=0;  // 234b

              if is_brick=True // and (brick_form.store_using_marker_colour_checkbox.Checked=True)    // 234c
                 then wrap_offset:=brick_form.store_marker_colour_panel.Color;

              if (p1.x=p2.x) and (p1.y=p2.y)
                 then begin
                        alert(6,'php/301    invalid  line  shape',
                                'This shape can not appear because the length of the line is zero.'
                               +'||The most likely reason is that you simply clicked the trackpad instead of drawing a line.',
                                '','','','','cancel  shape','',0);
                        EXIT;
                      end;

              normalize_line(p1,p2);

              with bgnd_form.bgnd_shapes_listbox do begin
                n:=Items.AddObject(new_shape.shape_name,Tbgshape.Create);  // create and add a new line in the shapes list.

                Tbgshape(Items.Objects[n]).bgimage:=nil;                   // not bitmap image 3-2-01.
                Tbgshape(Items.Objects[n]).bgnd_shape:=new_shape;          // put data in list.
                ItemIndex:=n;                                              // make it current.
              end;//with

            end;//with new_shape

            shapes_saved:=False;   // need a fresh save.
            shapes_current_state;  // update the form.
          end;
          
  redraw(True); // 229a
end;
//_____________________________________________________________________________________________

function check_shove:boolean;    // check some timbers there for shove / select.

begin
  RESULT:=False;         // init

     // 208c mods -- bug fix

  if no_timbering=True
     then begin
            alert(3,'    no  timbers',
                    'There are no timbers to shove because the control template has been set to `0no timbering`1.'
                   +'||To restore the timbers, click one of the timbering styles in the `0real > timbering >`1 menu options.',
                    '','','','','','O K',0);
            EXIT;
          end;


  if cl_only=True
     then begin
            if alert(3,'    no  timbers  showing',
                       'There are no timbers to shove because the control template has been set to show track centre-lines only.'
                      +'||Do you want to restore the rails and timbers for this template?',
                       '','','','','no  -  cancel','yes  -  restore  rails  and  timbers  and  continue',0)=5
               then EXIT;
            pad_form.normal_track_centre_lines_menu_radio.Click;   // radio item.
          end;

  RESULT:=True;
end;
//__________________________________________________________________________________________

procedure shove_delete(index:integer; var shoved_timbers:Tshoved_timbers);    // 227a   // array of Tshoved_timber;

var
  n:integer;

begin
  if (Length(shoved_timbers)<1) or (index<0) or (index>(Length(shoved_timbers)-1)) then EXIT;

  if index<(Length(shoved_timbers)-1)    // not deleting the final element
     then begin
            for n:=index+1 to Length(shoved_timbers)-1 do shoved_timbers[n-1]:=shoved_timbers[n];  // shift up
          end;


  SetLength(shoved_timbers,Length(shoved_timbers)-1);  // truncate final element

end;
//______________________________________________________________________________

procedure delete_null_shove_entries;   // remove any unshoved entries from current shove list.

var
  n:integer;

begin
  n:=0;
  while n<Length(current_shoved_timbers) do begin

    with current_shoved_timbers[n].shove_data do begin

      if (    (sv_x<>0)        // xtb modifier.
           or (sv_k<>0)        // angle modifier.
           or (sv_o<>0)        // offset modifier (near end).
           or (sv_l<>0)        // length modifier (far end).
           or (sv_w<>0)        // width modifier (per side).
           or (sv_c<>0)        // crab modifier (per side).

           or (sv_use_tcol=True)   // 226a   using modified colours
           or (sv_use_ocol=True)   // 226a

           or (sv_bgnd=True)       // 226a   also use tcol for bgnd templates on pad
           or (sv_export=True)     // 226a   also use ocol for exported images

           or (sv_option_bits<>0)    // 229b

           or (any_heaving_shoves(n)=True)      // 244a  any 3D chair heaving or snibs set?

           or (sv_code=-1)  )  // omitted.

      and (sv_code<>0)        // valid slot.

          then begin
                 INC(n);
                 CONTINUE; // leave this one.
               end;
    end;//with

    shove_delete(n,current_shoved_timbers);

  end;//while    // no need to increment n, it is now pointing to the next entry.
end;
//___________________________________________________________________________________________

function find_shove(str:string; create_new:boolean):integer;     // find str in current shove list, or create an empty slot for it.
                                                                 // return -1 if not present and not creating new.
var
  n,r,o,w:integer;


begin
  RESULT:=-1;                   // init.

  if create_new=True then delete_null_shove_entries;    // 244b  first remove any unshoved entries unless only searching  -  bug-fix for mouse-action shoves for chair heaving calls from drawtimber

  if Length(current_shoved_timbers)>0
     then begin
            for n:=0 to Length(current_shoved_timbers)-1 do begin

              if current_shoved_timbers[n].sv_str=str
                 then begin
                        RESULT:=n;
                        EXIT;           // already in list.
                      end;
            end;//next
          end;

  if create_new=False then EXIT;

  SetLength(current_shoved_timbers,Length(current_shoved_timbers)+1);  // create a new entry in shoved timbers array

  n:=HIGH(current_shoved_timbers);   // index to it

  with current_shoved_timbers[n].shove_data do begin

           sv_code:=0;     // 0=empty slot, -1=omit this timber,  1=shove this timber.
           sv_x:=0;        // xtb modifier.
           sv_k:=0;        // angle modifier.
           sv_o:=0;        // offset modifier (near end).
           sv_l:=0;        // length modifier (far end).
           sv_w:=0;        // width modifier (per side).
           sv_c:=0;        // crab modifier (per side).

           sv_tcol:=default_shoved_tcol;     // 226a modified trackpad infill colour
           sv_ocol:=default_shoved_ocol;     // 226a modified output colour

           sv_tcol_infill:=default_shoved_tcol_infill;      // 226a   infill code
           sv_ocol_infill:=default_shoved_ocol_infill;      // 226a

           sv_use_tcol:=False;     // 226a   use it flag
           sv_use_ocol:=False;     // 226a

           sv_bgnd:=False;         // 226a   also use tcol for bgnd templates on pad
           sv_export:=False;       // 226a   also use ocol for exported images

           //sv_dxf:=False;      // 242a

           sv_option_bits:=0;  // 229b

  end;//with

  with current_shoved_timbers[n] do begin

    for r:=1 to 4 do begin    // rails

      with heave_rail_chairs[r] do begin

         hv_omit:=False;    // omit chair
         hv_ch:=0;          // modified chair code

         hv_sc_outlong:=0;         // custom SC chair dims ..
         hv_sc_inlong:=0;
         hv_sc_halfwide:=0;

         hv_flip:=False;        // key flipped
         hv_omit_key:=False;    // key omitted

         hv_jaws:=False;        // jaws customized
         hv_plug:=0;            // plug customized   0=no change, 1=force clip-fit  2=force snap-fit  3=force press-fit

         for o:=0 to HIGH(hv_jaw_options) do begin        // 0..3 number of rails 4 in customizable chair
           for w:=0 to HIGH(hv_jaw_options[o]) do begin   // 0..5 options 6 per jaw  Tjaw_option

             hv_jaw_options[o][w]:=False;

           end;//next
         end;//next

      end;//with

    end;//next rail

  end;//with shoved timber


  with current_shoved_timbers[n].snibs_data do begin

    omit_tsn_snibs:=False;
    omit_tsf_snibs:=False;
    omit_msn_snibs:=False;
    omit_msf_snibs:=False;

    add_near_link:=False;
    add_far_link:=False;

  end;//with

  with current_shoved_timbers[n].webs_data do begin   //555a  MW 17-AUG-2024

    omit_tsn_web:=False;
    omit_tsf_web:=False;
    omit_msn_web:=False;
    omit_msf_web:=False;

  end;//with

  RESULT:=n;
end;
//______________________________________________________________________________________

procedure shove_along_mouse_action;

begin
  cancel_adjusts(True);
  shove_index:=find_shove(current_shove_str,True);

  if shove_index<>-1
     then begin
            current_shoved_timbers[shove_index].sv_str:=current_shove_str;
            with current_shoved_timbers[shove_index].shove_data do begin

              sv_code:=1;                           // might be an empty slot, or omitted.
              sv_x:=shovex;                         // enter (or re-enter) current data.

              mouse_action_selected('    shove  timber  along ...','shove  timber  along',current_shove_str+'  along  by : '+captext(shovex)+' mm');
              shove_along_mod:=1;
              full_draw:=True;     //  otherwise can't see the timbers.
              EXIT;
            end;//with
          end;

  shove_index:=0;   // safety - don't leave it invalid.
end;
//_________________________________________________________________________________________

procedure shove_throw_mouse_action;

begin
  cancel_adjusts(True);
  shove_index:=find_shove(current_shove_str,True);

  if shove_index<>-1
     then begin
            current_shoved_timbers[shove_index].sv_str:=current_shove_str;
            with current_shoved_timbers[shove_index].shove_data do begin
              sv_code:=1;
              sv_o:=shoveo;

              mouse_action_selected('    throw  timber  endways ...','throw  timber  endways',current_shove_str+'  throw  by : '+captext(shoveo)+' mm');
              shove_throw_mod:=1;
              full_draw:=True;         //  otherwise can't see the timbers.
              EXIT;
            end;//with
          end;

  shove_index:=0;   // safety - don't leave it invalid.
end;
//__________________________________________________________________________________________

procedure shove_crab_mouse_action;

begin
  cancel_adjusts(True);
  shove_index:=find_shove(current_shove_str,True);

  if shove_index<>-1
     then begin
            current_shoved_timbers[shove_index].sv_str:=current_shove_str;
            with current_shoved_timbers[shove_index].shove_data do begin
              sv_code:=1;
              sv_c:=shovec;

              mouse_action_selected('    crab  timber  sideways ...','crab  timber  sideways',current_shove_str+'  crab  by : '+captext(shovec)+' mm');
              shove_crab_mod:=1;
              full_draw:=True;         //  otherwise can't see the timbers.
              EXIT;
            end;//with
          end;

  shove_index:=0;   // safety - don't leave it invalid.
end;
//__________________________________________________________________________________________

procedure shove_length_mouse_action;

begin
  cancel_adjusts(True);
  shove_index:=find_shove(current_shove_str,True);

  if shove_index<>-1
     then begin
            current_shoved_timbers[shove_index].sv_str:=current_shove_str;
            with current_shoved_timbers[shove_index].shove_data do begin

              sv_code:=1;                           // might be an empty slot, or omitted.
              sv_l:=shovel;

              mouse_action_selected('    lengthen  timber ...','lengthen  timber',current_shove_str+'  lengthened  by : '+captext(shovel)+' mm');
              shove_length_mod:=1;
              full_draw:=True;      //  otherwise can't see the timbers.
              EXIT;
            end;//with
          end;

  shove_index:=0;   // safety - don't leave it invalid.
end;
//_________________________________________________________________________________________

procedure shove_width_mouse_action;

begin
  cancel_adjusts(True);
  shove_index:=find_shove(current_shove_str,True);

  if shove_index<>-1
     then begin
            current_shoved_timbers[shove_index].sv_str:=current_shove_str;
            with current_shoved_timbers[shove_index].shove_data do begin

              sv_code:=1;              // might be an empty slot, or omitted.
              sv_w:=shovew;

              mouse_action_selected('    widen  timber ...','widen  timber',current_shove_str+'  widened  by : '+captext(shovew*2)+' mm');
              shove_width_mod:=1;
              full_draw:=True;     //  otherwise can't see the timbers.
              EXIT;
            end;//with
          end;

  shove_index:=0;   // safety - don't leave it invalid.
end;
//__________________________________________________________________________________________

procedure shove_twist_mouse_action;

begin
  cancel_adjusts(True);
  shove_index:=find_shove(current_shove_str,True);

  if shove_index<>-1
     then begin
            current_shoved_timbers[shove_index].sv_str:=current_shove_str;
            with current_shoved_timbers[shove_index].shove_data do begin

              sv_code:=1;                           // might be an empty slot, or omitted.
              sv_k:=shovek;

              mouse_action_selected('    twist  timber ...','twist  timber',current_shove_str+'  twisted  by : '+captext(shovek*180/Pi)+' degrees'+k_ram_str(shovek));
              shove_twist_mod:=1;
              full_draw:=True;      //  otherwise can't see the timbers.
              EXIT;
            end;//with
          end;

  shove_index:=0;   // safety - don't leave it invalid.
end;
//__________________________________________________________________________________________

procedure fix_approach_length(snap:boolean; lmod:extended);

var
  xorg_old,t_only,dummy1:extended;

begin
  startx:=0;                                 // cancel any blanking.

  if snap=True then pad_form.snap_approach_to_nearest_menu_entry.Click;   // must first snap to nearest.

  gocalc(0,0);                               // and do initial pegging for.

  xorg_old:=xorg;           // for pegging calcs.
  t_only:=turnoutx-xorg;    // length of turnout portion.

  xorg:=xorg+lmod;          // mod xorg within limits...
  if xorg<0 then xorg:=0;
  if xorg>(turnoutx_max-t_only) then xorg:=turnoutx_max-t_only;

  turnoutx:=xorg+t_only;    // correct overall length.

                            // belt and braces keep turnoutx within limits...

  if turnoutx<0 then turnoutx:=0;
  if turnoutx>turnoutx_max then turnoutx:=turnoutx_max;
  if plain_track=True then turnoutx:=xorg;               //  keep lengths equal for plain track.

  udpegx:=udpegx+xorg-xorg_old;   // user-defined position.

  if peg_code<>0
     then begin
            kform_now:=kform;
            docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy1);    // get current peg data for peg_curve calcs.

            pegx:=pegx+xorg-xorg_old;       //  pegx changes with xorg unless peg is reset on rail-end.

            if spiral=True then os:=os+xorg-xorg_old;           //  os transition start changes with xorg ditto.
            if slewing=True then slew_s:=slew_s+xorg-xorg_old;  //  slewing ditto
            peg_curve;                                          //  keep turnout on the peg.
          end;

  gocalc(1,0);                                                           // re-calc with new lengths for new last_xtb, but don't draw it yet.
  if snap=True then pad_form.snap_approach_to_nearest_menu_entry.Click;  // then snap to sleeper centre again.
end;
//_____________________________________________________________________________________________

procedure fix_exit_length(lmod:extended);

var
  dummy1:extended;

begin
  pad_form.snap_exit_to_nearest_menu_entry.Click;   // first snap to nearest sleeper.
  gocalc(0,0);                                      // and do pegging for.

  turnoutx:=turnoutx+lmod;                  // modify the length.

  if turnoutx<0 then turnoutx:=0;           // but keep within limits...
  if turnoutx<xorg then turnoutx:=xorg;
  if turnoutx>turnoutx_max then turnoutx:=turnoutx_max;

  if peg_code=11
     then begin               // peg is on length...
            kform_now:=kform;
            docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy1);    // get current peg data for peg_curve calcs.

            pegx:=turnoutx;
            peg_curve;        // keep peg constant.
          end;
  gocalc(1,0);                                      // re-calc with new lengths for new last_xtb, but don't draw it yet.
  pad_form.snap_exit_to_nearest_menu_entry.Click;   // then snap to sleeper centre again.
  redraw(False);                                    // force draw in case he holds the shortcut key down.
end;
//__________________________________________________________________________________________

procedure normalize_keep_transforms(t_data:Ttransform_info);   // update keep shifts so that x1_shift, y1_shift can be zero,
                                                               // i.e. any subsequent rotation is about the template origin,
var
  x,y:extended;

begin
  with t_data do begin
    x:=x1_shift-x1_shift*COS(k_shift)+y1_shift*SIN(k_shift)+x2_shift;
    y:=y1_shift-x1_shift*SIN(k_shift)-y1_shift*COS(k_shift)+y2_shift;

    x2_shift:=x;                       // then normalise the data...
    y2_shift:=y;

    x1_shift:=0;
    y1_shift:=0;

    normalize_angle(k_shift);
  end;//with
end;
//___________________________________________________________________________________________

procedure mirror_keeps_x;     // X mirror a group of templates about the notch.

var
  now_keep:Tkeep_dims;
  n:integer;

begin
  if keeps_list.Count<1 then EXIT;     // no keeps to mirror.

  for n:=0 to (keeps_list.Count-1) do begin

    with Ttemplate(keeps_list.Objects[n]) do begin

      now_keep:=template_info.keep_dims;    // get the current keep data.

      with now_keep do begin

        if group_selected=False then CONTINUE;     // don't mirror this one.

        normalize_keep_transforms(box_dims1.transform_info);

        with box_dims1.transform_info do begin

          k_shift:=k_shift+Pi;                    // rotate 180 degs.
          normalize_angle(k_shift);

          x2_shift:=notchx+(notchx-x2_shift);     // mirror X on notch.  15-7-01.

          y2_shift:=0-y2_shift;                   // because of changed hand.

        end;//with

        box_dims1.mod_text_x:=0-box_dims1.mod_text_x;       // modified template label position.

        box_dims1.turnout_info1.hand:=0-box_dims1.turnout_info1.hand;   // swap hand.

      end;//with
      
      new_stamp_wanted:=True;              // True=has been shifted/rotated/mirrored, needs a new timestamp on rebuilding.
      template_info.keep_dims:=now_keep;   // update the keep record.

      with template_info.notch_info do begin  // update the stored pegging data.. !!! bug fix 15-7-01.
        notch_x:=notchx+(notchx-notch_x);
        notch_k:=Pi*2-notch_k;
        normalize_angle(notch_k);
      end;//with

    end;//with
  end;//for next n
end;
//_______________________________________________________________________________________

procedure mouse_symbol_click;  // symbol clicked on action form.

begin
  if mouse_modify<>0 then EXIT;    // 205c not while actually adjusting.

        // swap r1 or r2...

  if ( (curving_mod=1) or (orbit_mod=1) or (both_mod=1) ) and (spiral=True)     // curving mouse action already active for transition?
     then begin
            if adjust_trans_rad=1
               then pad_form.adjust_rad2_menu_entry.Click
               else pad_form.adjust_rad1_menu_entry.Click;
            EXIT;
          end;

        // ruler swap ends...

  if ruler1_mod=1
     then begin
            pad_form.move_ruler_2nd_end_menu_entry.Click;
            EXIT;
          end;

  if ruler2_mod=1
     then begin
            pad_form.move_ruler_1st_end_menu_entry.Click;
            EXIT;
          end;


        // swap F9 - F10

  if half_diamond=True
     then begin
            if vee_mod=1
               then begin
                      pad_form.adjust_k_crossing_angle_menu_entry.Click;
                      EXIT;
                    end;

            if hdk_mod=1
               then begin
                      pad_form.adjust_v_crossing_angle_menu_entry.Click;
                      EXIT;
                    end;
          end;


        // 205c  swap F9 - SHIFT-F12 ...

  if gaunt=True
     then begin
            if vee_mod=1
               then begin
                      pad_form.adjust_gaunt_radius_menu_entry.Click;
                      EXIT;
                    end;

            if (gaunt_offset_mod=1) or (gaunt_radius_mod=1)
               then begin
                      pad_form.adjust_v_crossing_angle_menu_entry.Click;
                      EXIT;
                    end;
          end;


      // 205c  shift group - rotate group ...

  if shift_keeps_mod=1
     then begin
            pad_form.rotate_keeps_menu_entry.Click;
            EXIT;
          end;

  if twist_keeps_mod=1
     then begin
            pad_form.shift_keeps_menu_entry.Click;
            EXIT;
          end;


      // 205c  trans start - trans length ...

  if trans_start_mod=1
     then begin
            pad_form.adjust_trans_length_menu_entry.Click;
            EXIT;
          end;

  if trans_length_mod=1
     then begin
            pad_form.adjust_trans_start_menu_entry.Click;
            EXIT;
          end;


      // 205c  F7 - F8 ...

  if shift_mod=1
     then begin
            pad_form.rotate_menu_entry.Click;
            EXIT;
          end;

  if twist_mod=1
     then begin
            pad_form.shift_menu_entry.Click;
            EXIT;
          end;


        // or repeat the last...

  pad_form.repeat_last_action_menu_entry.Click;  // normal repeat.
end;
//__________________________________________________________________________________________

procedure insert_turnout;

begin
  if plain_track=False then EXIT;     // ? menu should be disabled when drawing turnouts.

  if (pegx<0) or (pegx>(turnoutx-g*4))    // reset peg if not in sensible position. (g*4 arbitrary).
     then begin
            pad_form.reset_peg_menu_entry.Click;
            redraw(False);
          end;

  plain_track:=False;                     // swap to turnouts..
  set_plain_track(False,True);

  xorg:=pegx;                             // put the toe joint at the current peg position.
  if xorg<0 then xorg:=0;                 // keep within limits.
  if xorg>turnoutx then xorg:=turnoutx;

  turnout_road_i:=retpar_i;   // 206e bug fix -- need long turnout road for parallel crossing

  startx:=0;           // cancel any previous blanking
  tb_roll_percent:=0;  // cancel any previous timber rolling (in case he reverts to plain track).
  if peg_code=-2 then peg_code:=-1; // so peg on joints can re-initialise.

  retain_on_make;    // do blanking, shoves, diffs, crossing entry straight, cancel platforms  213a

  delete_all_symbols;     // 227a..

  if cl_only=True then railedges(gauge_faces,outer_edges,centre_lines);  // 28-10-99 this is a bug fix to put aq 25 back on. Why is it sometimes off???
end;
//_______________________________________________________________________________________

procedure swap_end_for_end;

var
  saved_notch:Tnotch;
  saved_pegx,saved_pegy:extended;
  saved_peg_code:integer;
  saved_peg_rail:integer;

begin
  cancel_adjusts(False);  // 0.93.a may be from toolbutton

  saved_notch:=get_current_notch;   // we need to use the current notch.

  saved_pegx:=pegx;
  saved_pegy:=pegy;
  saved_peg_code:=peg_code;
  saved_peg_rail:=peg_rail;

  startx:=0;                        // cancel any blanking.

  do_rollback:=False;
  pad_form.reset_peg_menu_entry.Click;
  gocalc(0,0);

  new_notch(get_peg_for_notch,False);
  gocalc(0,0);

  do_rollback:=False;
  pad_form.rotate_current_180_menu_entry.Click;
  gocalc(0,0);

  if plain_track=True then pad_form.peg_on_joint_end_menu_entry.Click
                      else pad_form.peg_on_overall_length_menu_entry.Click;
  gocalc(0,0);

  if slewing=True       // slewing, swap end positions..
     then begin
            slew_s:=turnoutx-(slew_s+slew_l);   // neg slew_s is OK.
            slew:=0-slew;
            gocalc(0,0);
          end;

  if spiral=True        // transition, swap end positions and rads...
     then begin
            os:=turnoutx-(os+tst);         // neg os is OK.

            clrad1:=nomrad1;         // centre-line 1st radius.
            clrad2:=nomrad2;         // centre-line 2nd radius.

            nomrad2:=clrad1;         // swap the radii.
            nomrad1:=clrad2;

            if adjust_trans_rad=1 then adjust_trans_rad:=2      // and the one he was adjusting.
                                  else adjust_trans_rad:=1;

            gocalc(0,0);
          end;

  swap_hand;
  gocalc(0,0);

  do_rollback:=False;
  shift_onto_notch(False,False);

  do_rollback:=False;
  pad_form.rotate_current_180_menu_entry.Click;

  set_current_notch(saved_notch);   // restore the current notch.

  pegx:=saved_pegx;
  pegy:=saved_pegy;
  peg_code:=saved_peg_code;
  peg_rail:=saved_peg_rail;
end;
//________________________________________________________________________________________

procedure set_trans_position_from_ctrl_0(os_offset,zone_len:extended);

var
  dummy:extended;

begin
  if zone_len<0 then zone_len:=0; // ??? menus should be disabled.

  kform_now:=kform;
  docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy);    // save current peg data for peg_curve calcs.

  os:=os_offset;
  tst:=zone_len;

  peg_curve;             //  adjust shifts and rotates for current peg position.
  redraw(True);
end;
//____________________________________________________________________________________________

procedure set_slew_position_from_ctrl_0(start_offset, zone_len:extended);

var
  dummy:extended;

begin
  if zone_len<ABS(slew) then zone_len:=ABS(slew);    // ??? arbitrary minimum. (can't go neg).
  if zone_len<1 then zone_len:=1;                    // 1 mm safety minimum (div by zero).

  kform_now:=kform;
  docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy);    // save current peg data for peg_curve calcs.

  slew_s:=start_offset;
  slew_l:=zone_len;

  peg_curve;             //  adjust shifts and rotates for current peg position.
  redraw(True);
end;
//____________________________________________________________________________________________

function normalize_transition:boolean; // normalize to template extents if zone outside them.

var
  dummy:extended;
  new_tst, new_os, new_r1, new_r2, rad_diff, temp_ktrans:extended;

begin
  RESULT:=False;              // default init.

  if spiral=False then EXIT;  // ???

  if (os>=0) and ((os+tst)<=turnoutx)
     then begin
            RESULT:=True;
            EXIT;         // zone is completely within template (already normalized).
          end;

  if (os+tst)<0   // template is all in r2...
     then begin
            if pegx<(os+tst)      // but peg not in r2..
               then begin
                      pad_form.reset_peg_menu_entry.Click;   // peg on datum
                      gocalc(0,0);                           // calc it.
                    end;
            fix_radius(nomrad2,False);    // set fixed radius curving.
            RESULT:=True;
            EXIT;
          end;

  if os>turnoutx   // template is all in r1...
     then begin
            if pegx>os     // but peg not in r1..
               then begin
                      pad_form.reset_peg_menu_entry.Click;   // peg on datum
                      gocalc(0,0);                           // calc it.
                    end;
            fix_radius(nomrad1,False);    // set fixed radius curving.
            RESULT:=True;
            EXIT;
          end;

  if (pegx<0) or (pegx>turnoutx)   // peg not in template extents...
     then begin
            pad_form.reset_peg_menu_entry.Click;   // peg on datum
            gocalc(0,0);                           // calc it.
          end;

  new_tst:=tst;     // init zone adjusts (don't change trans until both rads calced)...
  new_os:=os;
  new_r1:=nomrad1;
  new_r2:=nomrad2;

  if os<0            // trans starts before template...
     then begin
            new_r1:=clrad_at_x(0);  // set new r1 from centre-line radius at datum.
            new_tst:=tst+os;        // new zone is shorter.
            new_os:=0;
          end;

  if (os+tst)>turnoutx    // trans ends beyond template...
     then begin
            new_r2:=clrad_at_x(turnoutx);  // set new r2 from centre-line radius at template end.
            new_tst:=turnoutx-new_os;      // new zone is shorter.
          end;

    // check for error conditions... ???

  rad_diff:=ABS(new_r1-new_r2);
  if rad_diff>minfp then temp_ktrans:=new_r1*new_r2*new_tst/rad_diff   // new spiral constant???
                    else temp_ktrans:=max_spiral_constant*2;           // force error.

  if (ABS(temp_ktrans)>max_spiral_constant)
  or (rad_diff<minfp)
  or (ABS(new_r1)<(g*2))    // min rad (arbitrary).
  or (ABS(new_r2)<(g*2))
  or (new_tst<0)
     then EXIT;

    // can now change settings...

  kform_now:=kform;
  docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy);    // save current peg data for peg_curve calcs.

  os:=new_os;
  tst:=new_tst;
  nomrad1:=new_r1;
  nomrad2:=new_r2;

  peg_curve;       // fix template on peg.
  RESULT:=True;
end;
//_____________________________________________________________________________________

procedure crop_approach;

begin
  if plain_track=True then EXIT;      // otherwise sets zero length.

  if peg_code=0 then pad_form.peg_on_joint_end_menu_entry.Click;

  fix_approach_length(False,0-xorg);                           // crop off all of xorg.
end;
//_______________________________________________________________________________________

procedure peg_indicator_click;

begin
  with pad_form do begin
                                   // toggle peg end to end.
    if peg_code<>0
       then reset_peg_menu_entry.Click
       else begin
              if plain_track=True then peg_on_joint_end_menu_entry.Click
                                  else peg_on_overall_length_menu_entry.Click;
            end;
  end;//with
end;
//_____________________________________________________________________________________

procedure new_label_position;      // modify position of  bgnd name label

var
  mod_X,mod_Y:integer;

begin
  if (any_bgnd=0) or (clicked_keep_index<0) or (clicked_keep_index>(keeps_list.Count-1)) then EXIT;

  with Ttemplate(keeps_list.Objects[clicked_keep_index]) do begin

    if bg_copied=False then EXIT;  // ??? not on background.

    with template_info.bgnd_keep do begin

      with pad_form.dummy_label_panel do begin        // copy the dummy label position...

        mod_X:=Left-text_begin_X+22;        // distance label moved (pixels).
        mod_Y:=Top-text_begin_Y+2;

        text_begin_X:=text_begin_X+mod_X;
        text_begin_Y:=text_begin_Y+mod_Y;
        text_end_X:=text_end_X+mod_X;
        text_end_Y:=text_end_Y+mod_Y;
      end;//with panel.
    end;//with bgnd_keep.

    with template_info.keep_dims.box_dims1 do begin
      mod_text_x:=mod_text_x+mod_X*ffx;
      mod_text_y:=mod_text_y+mod_Y*ffy;
    end;//with

  end;//with template

  save_done:=False;
  backup_wanted:=True;
  redraw_pad(False,False);  // force immediate redraw to move the label.
end;
//____________________________________________________________________________________________

procedure cancel_paper_bunching;

begin
  if (bunch_mod=1) or (mouse_modify>0) then cancel_adjusts(False);

  paper_bunching:=False;
  pad_form.paper_bunching_off_menu_entry.Checked:=True;           // radio item.
  pad_form.adjust_paper_bunching_menu_entry.Enabled:=False;
end;
//________________________________________________________________________________________

procedure clear_current_name;

begin
  current_name_str:='';    // 208a         // control template reference.

  info_form.ref_name_label.Caption:=current_name_str;
end;
//_______________________________________________________________________________________

procedure mint_new_current(locked_length:integer; show_it:boolean);  // 208d locked_length added

  // locked_length: 0=free length, 1=locked at existing turnoutx

(*
  A "mint" template is a new control template with the following settings:

  Regular crossing.
  Normal length turnout road.
  Normal length main road.
  No approach or exit track (208d if locked_length=0).
  Fixed radius curving (no slew or transition curve).
  No blanking.
  No bonus timbers.
  No timber randomizing.
  No gaunt turnout.
  Rails on.
  Timbering on.
  Normal rail joints.
  Bent flares.  213a
  No timber rolling.
  No platforms.
  No trackbed edges.
  No adjacent tracks.
  Fixing peg in the CTRL-0 reset position.
  Centre-lines only cancelled.
  Name label position restored when stored.
  Memo: cleared.
  No curviform timbering

  If the switch is REA, any customized crossing is cancelled.
  Half-diamond cancelled and timbering restored to equalized incremental.

  Other settings remain unchanged.

  If not on the datum, it is put centre-pad, with zoom settings not changed.
  If on the datum, leave it there and zoom to fit.
*)

var
  on_datum:boolean;

begin
  cancel_adjusts(False);

  timbering_brick:=False;         // 234a
  inherited_use_marker:=False;    // no marker colour

  pad_form.reset_tandem_turnout_menu_entry.Click;      // 218a

  isolated_crossing:=False;   // 217a

  normalize_transforms;

  if (xshift=0) and (yshift=0) and (kform=0)
     then begin
            clear_transform_data;  // clear shifts if not already.
            on_datum:=True;        // set this now - routines below include a peg_curve.
          end
     else on_datum:=False;

  with pad_form do begin

    do_rollback:=False;

    normal_track_centre_lines_menu_radio.Click;    // 0.93.a mods

    cl_only:=False;                         // in case it was out of step with the menu check.

    track_centre_lines_flag:=True;

    switch_drive_flag:=True;     // 0.82.a

    do_rollback:=False;
    if (no_timbering=True) or (half_diamond=True)
       then begin
              equalized_incremental_menu_entry.Enabled:=True;
              equalized_incremental_menu_entry.Click;         // radio item.
            end;

    do_rollback:=False;
    cancel_blanking_menu_entry.Click;

    nomrad:=ABS(nomrad);                    // cancel any contraflexure.

    if (spiral=True) or (nomrad>max_rad_test)   // transition or straight.
       then begin
              spiral:=False;                // no transition.
              nomrad:=660*scale;            // fixed 10 chains curve.
            end;

    do_rollback:=False;
    disable_slewing_menu_entry.Click;

    bontimb:=0;                                // no bonus timbers 0.76.a  23-10-01.
    tb_roll_percent:=0;                        // no timber rolling 0.76.a 13-5-02.
    rjcode:=0;                                 // normal rail joints 0.76.a  23-10-01.
    half_diamond:=False;                       // normal switch calcs.
    hd_switch_timbering:=False;

    if peg_code=-2 then peg_code:=-1;          // so peg on joints can re-initialise.
    if rail_section=0 then rail_section:=1;    // rails on, bullhead default.

    if rail_section=1 then flare_type:=0;      // 213a bent flares

    if retain_shoves_on_mint=False then clear_shovedata;  // 0.94.a mods

    if retain_diffs_on_mint=False then clear_check_diffs; // 0.94.a clear any check rail diffs

    if retain_entry_straight_on_mint=False then entry_straight_code:=0;  // 213a  re-set auto fit straight.

    if gaps_form.symbols_retain_on_mint_checkbox.Checked=False      // 227a
       then begin
              delete_all_symbols;
            end;

    do_rollback:=False;

    if locked_length=0  // length free
       then begin
              if plain_track=False
                 then begin
                        xorg:=0;         // remove any aproach track;
                        turnout_i:=0;    // 0=length free. default overall turnout length.
                      end
                 else begin
                        xorg:=132*scale;     // give him 2 chains (arbitrary). 0.93.a
                        turnoutx:=xorg;
                        turnout_i:=1;        // length always locked at turnoutx for plain track.
                      end;
            end
       else turnout_i:=1;        // 1 = length locked at turnoutx. xorg unchanged.   // 208 mods

    reset_peg_menu_entry.Click;  // Ctrl-0 position.

    turnout_road_i:=0;     // standard length.
    main_road_i:=0;        // 217a

    xing_type_i:=0;        // regular crossing (normal)...

    retpar_i:=0;           // no return curve.

    gaunt:=False;  // 0.93.a

    cpi.random_end_pi:=0;            // no end randomizing.
    cpi.random_angle_pi:=0;          // equalizing angle not randomised.
    random_flag:=False;              // and randomising turned off anyway.

    cpi.ts_joint_offset_pi:=0;    // 227a    no TS stock rail joint offset
    cpi.ms_joint_offset_pi:=0;    // 227a    no MS stock rail joint offset

    cpi.tv_joint_offset_pi:=0;    // 227a    no vee splice rail joint offset
    cpi.mv_joint_offset_pi:=0;    // 227a    no vee point rail joint offset

    snap_exit_to_return_curve_menu_entry.Enabled:=False;

    cl_options_code:=0;              // 214a    normal centre-line
    cl_options_custom_offset:=0;     // 214a

    curviform_timbering:=False;   // 215a

    simplified_short_angle:=True;  // 228a

    gocalc(0,0);                                      // get turnoutx

    clear_transform_data;         // do this last (routines above do a peg_curve),

    copied_ref_str:=current_name_str;

    clear_current_name;
    current_memo_str:=' your memo notes for this template ...|';  // 5-08-01.

    do_rollback:=False;
    if on_datum=True
       then begin
              xshift:=0;                  // cancel any shifts.
              yshift:=0;
              gocalc(2,0);                            // need screen calcs to set y_datum.
              set_y_datum;                            // in case RH.
              fit_current_only_menu_entry.Click;      // 0.93.a     //fit_org_extents_current_menu_entry.Click;    // zoom to fit on datum.
            end
       else begin
              xshift:=zoom_offsetx+(screenx-turnoutx)/2;              // or put it across the centre of the pad,
              yshift:=(zoom_offsety+screeny/2.5-y_datum)*hand_i-g/2;  // 0.93.a was /2.0  // and on main centre-line (if straight turnout).
            end;

    xshift_quickset:=xshift;  // 0.93.a   save these for quick-set store check next time
    yshift_quickset:=yshift;  // 0.93.a

    if show_it=True then show_and_redraw(True,False);   // in case curent hidden.

    do_rollback:=False;

    if plain_track=True
       then begin
              redraw(False);                              // force sleeper calcs.
              snap_approach_to_nearest_menu_entry.Click;  // to nearest sleeper.
            end;

  end;//with pad_form

  if (csi.group_code=2) and (plain_track=False)       // REA switch, so re-set REA bullhead crossing...
     then begin
            bn_wide:=0.75;      // full-size inches.  3/4" blunt nose BH default.
            bn_to_a:=4.0;       // full-size inches - 4" to A timber centre.
            wingj_sp:=25;       // full-size inches - 25" timber spacing at wing rail joint.
            mvj_sp:=12.5;       // full-size inches - 12.5" spacing to vee point rail joint.

            wingtimb_sp:=30;    // full-size inches - 30" timber spacing for wing rail front part of crossing (up to "A").
            veetimb_sp:=30;     // full-size inches - 30" timber spacing for vee point rail part of crossing (on from "A").

            hd_vcheck_rails:=0;   // no shortening of check rails for slips.
            hd_timbers:=0;        // no extending of timbers for slips.

                       // number of timbers spanned by vee rail including the "A" timber...

            vee_spco1:=4;  // less than 1:4         // these are byte values...
            vee_spco2:=5;  // up to 1:6.3/4
            vee_spco3:=6;  // up to 1:10
            vee_spco4:=7;  // up to 1:15
            vee_spco5:=8;  // up to 1:20
            vee_spco6:=9;  // over 1:20 improvised.

                       // number of timbers spanned by wing rail front from joint but excluding the "A" timber...

            wing_spco1:=1;    // less than 1:4
            wing_spco2:=3;    // up to 1:6.3/4
            wing_spco3:=3;    // up to 1:10
            wing_spco4:=4;    // up to 1:15
            wing_spco5:=5;    // up to 1:20
            wing_spco6:=6;    // over 1:20

                     // restore default REA check rails...

            with cpi do begin

              ck_ms_working1_pi:=def_req;  // full-size 78 inches - size 1 MS check rail working length (back from "A").    228a was 78"   reverted 228b
              ck_ms_working2_pi:=def_req;  // full-size 108 inches - size 2 MS check rail working length (back from "A").
              ck_ms_working3_pi:=def_req;  // full-size 138 inches - size 3 MS check rail working length (back from "A").

              // 0.94.a no longer used, mouse diffs instead.  ck_ts_working_mod_pi:=def_req;   // full-size 0 inches - TS check rail working length modifier.

              ck_ms_ext1_pi:=def_req;   // full-size 48 inches - size 1 MS check rail extension length (forward from "A").
              ck_ms_ext2_pi:=def_req;   // full-size 78 inches - size 2 MS check rail extension length (forward from "A").

              // 0.94.a no longer used, mouse diffs instead.  ck_ts_ext_mod_pi:=def_req;   // full-size 0 inches - TS check rail extension length modifier.

              wing_ms_reach1_pi:=def_req;    // short wing rails 48" default from centre of "A" timber.
              wing_ms_reach2_pi:=def_req;    // long wing rails 78" default from centre of "A" timber.

              // 0.94.a no longer used, mouse diffs instead.  wing_ts_reach_mod_pi:=def_req; // full-size 0 inches - TS wing rail reach length modifier.

            end;//with
          end;

  turnout_road_stock_rail_flag:=True;
  turnout_road_check_rail_flag:=True;
  turnout_road_crossing_rail_flag:=True;
  crossing_vee_flag:=True;
  main_road_crossing_rail_flag:=True;
  main_road_check_rail_flag:=True;
  main_road_stock_rail_flag:=True;

  k_main_side_check_rail_flag:=True;      // 0.93.a
  k_diagonal_side_check_rail_flag:=True;  // 0.93.a

         // joint switches 0.79.a ...    19-05-06

  omit_tspt_marks:=False;    // 227a  plain track joints, also for turnout approach and exit track
  omit_mspt_marks:=False;    // 227a  plain track joints, also for turnout approach and exit track

  omit_swfj_marks:=False;
  omit_swrj_marks:=False;
  omit_skj_marks:=False;
  omit_wj_marks:=False;
  omit_vj_marks:=False;

  omit_tsxj_marks:=False;    // 227a
  omit_msxj_marks:=False;    // 227a

  omit_kx_marks:=False;


  adjacent_edges:=True;    // 0.93.a   False=adjacent tracks,  True=trackbed edges and platform edges.

           // trackbed ...

  trackbed_ms_width_ins:=90;     // 215a
  trackbed_ts_width_ins:=90;     // 215a

  draw_ms_trackbed_edge:=False;
  draw_ts_trackbed_edge:=False;

  cess_ms_width_ins:=27;       // 215a
  cess_ts_width_ins:=27;       // 215a

  draw_ms_trackbed_cess_edge:=False;     // 215a
  draw_ts_trackbed_cess_edge:=False;     // 215a

           // platforms ...

  platform_ms_front_edge_ins:=57;  // 215a
  platform_ts_front_edge_ins:=57;

  platform_ms_start_width_ins:=144;    // 12ft default
  platform_ms_end_width_ins:=144;      // 12ft default

  platform_ts_start_width_ins:=144;    // 12ft default
  platform_ts_end_width_ins:=144;      // 12ft default

  platform_ms_start_mm:=0;
  platform_ms_length_mm:=def_req;

  platform_ts_start_mm:=0;
  platform_ts_length_mm:=def_req;


  platform_ms_start_skew_mm:=0;      // 207a
  platform_ms_end_skew_mm:=0;        // 207a

  platform_ts_start_skew_mm:=0;      // 207a
  platform_ts_end_skew_mm:=0;        // 207a


  draw_ms_platform:=False;

  draw_ms_platform_rear_edge:=True;
  draw_ms_platform_start_edge:=True;
  draw_ms_platform_end_edge:=True;

  draw_ts_platform:=False;

  draw_ts_platform_rear_edge:=True;
  draw_ts_platform_start_edge:=True;
  draw_ts_platform_end_edge:=True;

  railedges(gauge_faces,outer_edges,centre_lines);

  if want_timbers_back_on=False  // 237c ..
     then cl_only:=True;

  if show_it=True then redraw_pad(True,True);
end;
//________________________________________________________________________________________

procedure adjust_direct(code:integer);

begin
  cancel_adjusts(False);
  with pad_form do begin

    case code of
           0: lock_scaling_at_menu_entry.Click;   //if spot_mod=1
           1: lock_scaling_at_menu_entry.Click;   //if zoom_mod=1
           2: set_datum_menu_entry.Click;         //if datum_mod=1
           3: set_peg_menu_entry.Click;           //if peg_mod=1
           4: set_notch_menu_entry.Click;         //if notch_mod=1
           5: set_output_size_menu_entry.Click;   //if out_factor_mod=1

           6: shove_xtb_panel_click;      // shove_timber_form.data_button.Click;  //if shove_along_mod=1
           7: shove_throw_panel_click;    //shove_timber_form.data_button.Click;   //if shove_throw_mod=1

           8: curving_data_menu_entry.Click;        //if trans_length_mod=1
           9: curving_data_menu_entry.Click;        //if trans_start_mod=1

          10: slewing_data_menu_entry.Click;      //if slew_amount_mod=1
          11: slewing_data_menu_entry.Click;      //if slew_length_mod=1
          12: slewing_data_menu_entry.Click;      //if slew_start_mod=1

          13: lengths_mm_menu_entry.Click;        //if length_mod=1
          14: lengths_mm_menu_entry.Click;        //if approach_mod=1

          15: transform_data_menu_entry.Click;    //if shift_mod=1
          16: set_peg_menu_entry.Click;           //if slide_mod=1
          17: get_ring_location;                  //if ring_mod=1

          18: enter_shift_keeps_menu_entry.Click;    //if shift_keeps_mod=1
          19: enter_rotation_keeps_menu_entry.Click; //if twist_keeps_mod=1

          20: lock_scaling_at_menu_entry.Click;      //if zoffset_mod=1
          21: transform_data_menu_entry.Click;       //if twist_mod=1
          22: transform_data_menu_entry.Click;       //if orbit_mod=1
          23: curving_data_menu_entry.Click;         //if both_mod=1
          24: crossing_menu_entry.Click;             //if sx_mod=1
          25: curving_data_menu_entry.Click;         //if curving_mod=1
          26: lengths_mm_menu_entry.Click;           //if roam_mod=1

          27: slewing_data_menu_entry.Click;         //if slew_factor_mod=1
          28: EXIT;                                  //if bunch_mod=1
          29: set_print_origin_menu_entry.Click;     //if porg_mod=1

          30: bgnd_form.modify_button.Click;         //if corner1_mod=1
          31: bgnd_form.modify_button.Click;         //if corner2_mod=1
          32: bgnd_form.shift_one_to_button.Click;   //if oneshape_shift_mod=1
          33: bgnd_form.shift_all_by_button.Click;   //if allshapes_shift_mod=1

          34: set_peg_menu_entry.Click;              //if snake_mod=1

          35: bgnd_form.scale_all_by_button.Click;   //if allshapes_scale_mod=1
          36: bgnd_form.rotate_all_by_button.Click;  //if allshapes_rotate_mod=1

          37: bgnd_form.scale_one_to_button.Click;   //if oneshape_scale_mod=1

          38: shove_length_panel_click;  // shove_timber_form.data_button.Click;  //if shove_length_mod=1
          39: shove_width_panel_click;   // shove_timber_form.data_button.Click;  //if shove_width_mod=1
          40: shove_twist_panel_click;   // shove_timber_form.data_button.Click;  //if shove_twist_mod=1

          41: lengths_mm_menu_entry.Click;          //if blanking_mod=1

          42: roll_rails_by_menu_entry.Click;       //if roll_mod=1

          43: get_ring_size;                        //if ringdia_mod=1

       44,45: set_ruler_ends_menu_entry.Click;      //if ruler1_mod=1 or ruler2_mod=1

          46: curving_data_menu_entry.Click;        //if swell_mod=1

          47: shove_crab_panel_click;               // if shove_crab_mod=1

          48: background_template_detail_menu_entry.Click;    // if move_labels_mod=1    // 0.82.d

          49: crossing_menu_entry.Click;            // if entry_straight_mod=1    // 0.91.b

                        // 0.93.a platforms ...

          50: platform_form.ts_position_button.Click; // if plat_ts_start_mod=1   then repeat_last_index:=50;
          51: platform_form.ts_position_button.Click; // if plat_ts_end_mod=1     then repeat_last_index:=51;
          52: platform_form.ts_width_button.Click;    // if plat_ts_width1_mod=1  then repeat_last_index:=52;
          53: platform_form.ts_width_button.Click;    // if plat_ts_width2_mod=1  then repeat_last_index:=53;

          54: platform_form.ms_position_button.Click; // if plat_ms_start_mod=1   then repeat_last_index:=54;
          55: platform_form.ms_position_button.Click; // if plat_ms_end_mod=1     then repeat_last_index:=55;
          56: platform_form.ms_width_button.Click;    // if plat_ms_width1_mod=1  then repeat_last_index:=56;
          57: platform_form.ms_width_button.Click;    // if plat_ms_width2_mod=1  then repeat_last_index:=57;



          58: gaunt_offset_menu_entry.Click;     //  gaunt_offset_mod SHIFT+F12  // 0.93.a ex 081
          59: crossing_menu_entry.Click;         //  vee_mod F9                  // 0.93.a ex 081
          60: k_crossing_angle_menu_entry.Click; //  hdk_mod F10                 // 0.93.a ex 081

  61,62,63,64,65: set_boundary_rectangle_dims(pad_form);  // rect_left_mod etc.    // 0.93.a

        66,67,68: check_diffs_form.data_button.Click;     // if check_diffs_len_mod=1 etc.  check rail diffs // 0.94.a

       69,70: grid_form.dummy_vehicle_dimensions_button.Click;   //  dv_mod, dv_clear_mod  dummy vehicle 0.98.a

       71,72: platform_form.ms_position_button.Click; //  if plat_ms_skew1_mod=1 then repeat_last_index:=71;    // 207a
                                                      //  if plat_ms_skew2_mod=1 then repeat_last_index:=72;    // 207a
       73,74: platform_form.ts_position_button.Click; //  if plat_ts_skew1_mod=1 then repeat_last_index:=73;    // 207a
                                                      //  if plat_ts_skew2_mod=1 then repeat_last_index:=74;    // 207a

       75,82: lengths_mm_menu_entry.Click;            //  if turnout_road_mod=1  or  main_road_mod=1    // 209a  217a

          76: adjacent_spacing_ts_menu_entry.Click;   // if adjacent_ts_mod=1
          77: adjacent_spacing_ms_menu_entry.Click;   // if adjacent_ms_mod=1

                // trackbed edges 215a ...

       78,79: trackbed_form.ts_position_button.Click;       // edge_ts_start_mod    edge_ts_length_mod
       80,81: trackbed_form.ms_position_button.Click;       // edge_ms_start_mod    edge_ms_length_mod

       // 82 see 75

          83: gaunt_radius_menu_entry.Click;           //  gaunt_radius_mod   217b

        //84: pad_form.set_locator_menu_entry.Click;   // if locator_mod=1  227a

       85,86: bgnd_form.modify_button.Click;     // clip_size_mod, clip_angle_mod,   slab angle   229a

          87: lengths_mm_menu_entry.Click;       //if pt_blanking_mod=1

    end;//case
  end;//with
end;
//__________________________________________________________________________________________

procedure action_label_click;

       // cancel adjusts and go adjust dimension by direct entry...
begin
  if spot_mod=1         then begin adjust_direct(0); EXIT; end;
  if zoom_mod=1         then begin adjust_direct(1); EXIT; end;
  if datum_mod=1        then begin adjust_direct(2); EXIT; end;
  if peg_mod=1          then begin adjust_direct(3); EXIT; end;
  if notch_mod=1        then begin adjust_direct(4); EXIT; end;
  if out_factor_mod=1   then begin adjust_direct(5); EXIT; end;
  if shove_along_mod=1  then begin adjust_direct(6); EXIT; end;
  if shove_throw_mod=1  then begin adjust_direct(7); EXIT; end;
  if trans_length_mod=1 then begin adjust_direct(8); EXIT; end;
  if trans_start_mod=1  then begin adjust_direct(9); EXIT; end;
  if slew_amount_mod=1  then begin adjust_direct(10); EXIT; end;
  if slew_length_mod=1  then begin adjust_direct(11); EXIT; end;
  if slew_start_mod=1   then begin adjust_direct(12); EXIT; end;
  if length_mod=1       then begin adjust_direct(13); EXIT; end;
  if approach_mod=1     then begin adjust_direct(14); EXIT; end;
  if shift_mod=1        then begin adjust_direct(15); EXIT; end;
  if slide_mod=1        then begin adjust_direct(16); EXIT; end;
  if ring_mod=1         then begin adjust_direct(17); EXIT; end;
  if shift_keeps_mod=1  then begin adjust_direct(18); EXIT; end;
  if twist_keeps_mod=1  then begin adjust_direct(19); EXIT; end;
  if zoffset_mod=1      then begin adjust_direct(20); EXIT; end;
  if twist_mod=1        then begin adjust_direct(21); EXIT; end;
  if orbit_mod=1        then begin adjust_direct(22); EXIT; end;
  if both_mod=1         then begin adjust_direct(23); EXIT; end;
  if sx_mod=1           then begin adjust_direct(24); EXIT; end;
  if curving_mod=1      then begin adjust_direct(25); EXIT; end;
  if roam_mod=1         then begin adjust_direct(26); EXIT; end;
  if slew_factor_mod=1  then begin adjust_direct(27); EXIT; end;
  if bunch_mod=1        then begin adjust_direct(28); EXIT; end;
  if porg_mod=1         then begin adjust_direct(29); EXIT; end;

  if corner1_mod=1      then begin adjust_direct(30); EXIT; end;
  if corner2_mod=1      then begin adjust_direct(31); EXIT; end;

  if oneshape_shift_mod=1    then begin adjust_direct(32); EXIT; end;
  if allshapes_shift_mod=1   then begin adjust_direct(33); EXIT; end;

  if snake_mod=1        then begin adjust_direct(34); EXIT; end;

  if allshapes_scale_mod=1   then begin adjust_direct(35); EXIT; end;
  if allshapes_rotate_mod=1  then begin adjust_direct(36); EXIT; end;
  if oneshape_scale_mod=1    then begin adjust_direct(37); EXIT; end;

  if shove_length_mod=1  then begin adjust_direct(38); EXIT; end;
  if shove_width_mod=1   then begin adjust_direct(39); EXIT; end;
  if shove_twist_mod=1   then begin adjust_direct(40); EXIT; end;

  if blanking_mod=1     then begin adjust_direct(41); EXIT; end;
  if pt_blanking_mod=1  then begin adjust_direct(87); EXIT; end;  // 229a

  if roll_mod=1       then begin adjust_direct(42); EXIT; end;

  if ringdia_mod=1    then begin adjust_direct(43); EXIT; end;

  if ruler1_mod=1     then begin adjust_direct(44); EXIT; end;
  if ruler2_mod=1     then begin adjust_direct(45); EXIT; end;

  if swell_mod=1      then begin adjust_direct(46); EXIT; end;

  if shove_crab_mod=1 then begin adjust_direct(47); EXIT; end;

  if move_labels_mod=1 then begin adjust_direct(48); EXIT; end;  // 0.82.d

  if entry_straight_mod=1 then begin adjust_direct(49); EXIT; end;  // 0.91.b

     // 0.93.a ...

  if plat_ts_start_mod=1   then begin adjust_direct(50); EXIT; end;
  if plat_ts_length_mod=1  then begin adjust_direct(51); EXIT; end;
  if plat_ts_width1_mod=1  then begin adjust_direct(52); EXIT; end;
  if plat_ts_width2_mod=1  then begin adjust_direct(53); EXIT; end;

  if plat_ms_start_mod=1   then begin adjust_direct(54); EXIT; end;
  if plat_ms_length_mod=1  then begin adjust_direct(55); EXIT; end;
  if plat_ms_width1_mod=1  then begin adjust_direct(56); EXIT; end;
  if plat_ms_width2_mod=1  then begin adjust_direct(57); EXIT; end;

  if gaunt_offset_mod=1    then begin adjust_direct(58); EXIT; end;  // SHIFT+F12  // 0.93.a ex 081
  if vee_mod=1             then begin adjust_direct(59); EXIT; end;  // F9         // 0.93.a ex 081
  if hdk_mod=1             then begin adjust_direct(60); EXIT; end;  // F10        // 0.93.a ex 081

  if rect_left_mod=1       then begin adjust_direct(61); EXIT; end;  // output boundary rectangle 0.93.a ...
  if rect_right_mod=1      then begin adjust_direct(62); EXIT; end;
  if rect_top_mod=1        then begin adjust_direct(63); EXIT; end;
  if rect_bottom_mod=1     then begin adjust_direct(64); EXIT; end;
  if rect_move_mod=1       then begin adjust_direct(65); EXIT; end;

  if check_diffs_len_mod=1    then begin adjust_direct(66); EXIT; end;    // 0.94.a  mouse check rails
  if check_diffs_flare_mod=1  then begin adjust_direct(67); EXIT; end;    // 0.94.a
  if check_diffs_gap_mod=1    then begin adjust_direct(68); EXIT; end;    // 0.94.a

  if dv_mod=1                 then begin adjust_direct(69); EXIT; end;    // 0.98.a
  if dv_clear_mod=1           then begin adjust_direct(70); EXIT; end;    // 0.98.a

  if plat_ms_skew1_mod=1      then begin adjust_direct(71); EXIT; end;    // 207a
  if plat_ms_skew2_mod=1      then begin adjust_direct(72); EXIT; end;    // 207a
  if plat_ts_skew1_mod=1      then begin adjust_direct(73); EXIT; end;    // 207a
  if plat_ts_skew2_mod=1      then begin adjust_direct(74); EXIT; end;    // 207a

  if turnout_road_mod=1       then begin adjust_direct(75); EXIT; end;    // 209a

  if adjacent_ts_mod=1        then begin adjust_direct(76); EXIT; end;    // 213a
  if adjacent_ms_mod=1        then begin adjust_direct(77); EXIT; end;    // 213a

  if edge_ts_start_mod=1      then begin adjust_direct(78); EXIT; end;    // 215a ...
  if edge_ts_length_mod=1     then begin adjust_direct(79); EXIT; end;

  if edge_ms_start_mod=1     then begin adjust_direct(80); EXIT; end;
  if edge_ms_length_mod=1    then begin adjust_direct(81); EXIT; end;

  if main_road_mod=1         then begin adjust_direct(82); EXIT; end;    // 217a

  if gaunt_radius_mod=1      then begin adjust_direct(83); EXIT; end;    // 217b

  if locator_mod=1           then begin adjust_direct(84); EXIT; end;    // 227a

  if clip_size_mod=1         then begin adjust_direct(85); EXIT; end;    // 229a
  if clip_angle_mod=1        then begin adjust_direct(86); EXIT; end;    // 229a

end;
//______________________________________________________________________________

procedure update_rail_section(var box_kd:Tkeep_dims); // update stored rail-section data to match the control template.

     // 0.94.a

begin
  with box_kd.box_dims1 do begin
    rail_info.flared_ends_ri:=flare_type;  // 0=straight bent, 1=straight machined

    rail_info.knuckle_code_ri:=knuckle_code;       // 214a  integer;   0=normal, -1=sharp, 1=use knuckle_radius_ri
    rail_info.knuckle_radius_ri:=knuckle_radius;   // 214a  extended;

    rail_type:=rail_section;               // rail head only or head+foot(BH/FB).
    uninclined_rails:=vertical_rails;      // True = rails vertical.

    proto_info.railtop_pi:=cpi.railtop_pi;
    proto_info.railbottom_pi:=cpi.railbottom_pi;
    proto_info.rail_height_pi:=cpi.rail_height_pi;

    proto_info.rail_seat_height_pi:=cpi.rail_seat_height_pi;  // 235a

    proto_info.rail_inclination_pi:=cpi.rail_inclination_pi;

  end;//with
end;
//______________________________________________________________________________

procedure update_centre_lines(var box_kd:Tkeep_dims); // update stored track centre-line settings to match the control template.

     // 0.93.a

begin
  box_kd.box_dims1.rail_info.track_centre_lines_sw:=track_centre_lines_flag;
  box_kd.box_dims1.align_info.cl_only_flag:=cl_only;
  box_kd.box_dims1.align_info.dummy_template_flag:=dummy_template;   // 212a
end;
//______________________________________________________________________________

procedure update_centre_line_offset_options(var box_kd:Tkeep_dims); // update stored centre-line offset options to match the control template.

    // 214a

begin
  box_kd.box_dims1.align_info.cl_options_code_int:=cl_options_code;
  box_kd.box_dims1.align_info.cl_options_custom_offset_ext:=cl_options_custom_offset;
end;
//______________________________________________________________________________

procedure update_radius_warning(var box_kd:Tkeep_dims);  // update stored radius warning limit to match the control template.

              // 206e
begin
  box_kd.box_dims1.proto_info.min_radius_pi:=min_radius;
end;
//______________________________________________________________________________

procedure update_chair_options(var keep_info:Ttemplate_info);  // update stored chair options to match the control template.     221a

begin
  keep_info.keep_dims.turnout_info2.chairing_flag:=exp_chairing;

  with keep_info.keep_dims.box_dims1.proto_info do begin

    s1j_on_wing_pi:=cpi.s1j_on_wing_pi;

    s1j_on_j1_pi:=cpi.s1j_on_j1_pi;

    slab_bracket_pi:=cpi.slab_bracket_pi;

    ccl_ccr_variable_pi:=cpi.ccl_ccr_variable_pi;  // 243a  CCL CCR check end chairs fixed size

    nose_clamp_pi:=cpi.nose_clamp_pi;
    xing_filler_pi:=cpi.xing_filler_pi;


    temp_dd_parallel_wing_pi:=cpi.temp_dd_parallel_wing_pi;  // 244a

    temp_wrong_k_xing_pi:=cpi.temp_wrong_k_xing_pi;   // 244a temporary for customizing

    keys_on_outer_jaws_pi:=cpi.keys_on_outer_jaws_pi;

    rail_seats_pi:=cpi.rail_seats_pi;

    ms_snibs_pi:=cpi.ms_snibs_pi;   // 244a    MS snibs  2-D DXF kerf
    ts_snibs_pi:=cpi.ts_snibs_pi;   // 244a    TS snibs  2-D DXF kerf

  end;//with

  keep_info.file_blocks.keep_jaw_options:=Copy(current_jaw_options_set);  // 239a

end;
//______________________________________________________________________________

procedure update_trackbed_edges(var box_kd:Tkeep_dims);  // update stored trackbed edges settings to match the control template.

      // 0.93.a

begin
  with box_kd.box_dims1.platform_trackbed_info do begin

    adjacent_edges_keep:=True;  // False=adjacent tracks,  True=trackbed edges and platform edges.

    draw_ms_trackbed_edge_keep:=draw_ms_trackbed_edge;
    draw_ts_trackbed_edge_keep:=draw_ts_trackbed_edge;

    trackbed_ms_width_ins_keep:=trackbed_ms_width_ins;    // 215a
    trackbed_ts_width_ins_keep:=trackbed_ts_width_ins;    // 215a

    cess_ms_width_ins_keep:=cess_ms_width_ins;     // 215a
    cess_ts_width_ins_keep:=cess_ts_width_ins;     // 215a

    draw_ms_trackbed_cess_edge_keep:=draw_ms_trackbed_cess_edge;   // 215a
    draw_ts_trackbed_cess_edge_keep:=draw_ts_trackbed_cess_edge;   // 215a

    trackbed_ts_start_mm:=0;          // 215a
    trackbed_ts_length_mm:=def_req;   // 215a

    trackbed_ms_start_mm:=0;          // 215a
    trackbed_ms_length_mm:=def_req;   // 215a

  end;//with
end;
//______________________________________________________________________________

procedure update_customize_xing(var box_kd:Tkeep_dims);   // 214b  update V-crossing customizing

begin

  with box_kd.turnout_info2.crossing_info do begin

    blunt_nose_width:=bn_wide;         // full-size inches.
    blunt_nose_to_timb:=bn_to_a;       // full-size inches - to A timber centre.

    vee_timber_spacing:=veetimb_sp;    // full-size inches - timber spacing for vee point rail part of crossing (on from "A").
    wing_timber_spacing:=wingtimb_sp;  // full-size inches - timber spacing for wing rail front part of crossing (up to "A").

    vee_joint_half_spacing:=mvj_sp;    // full-size inches - rail overlap at vee point rail joint.
    wing_joint_spacing:=wingj_sp;      // full-size inches - timber spacing at wing rail joint.

               // number of timbers spanned by vee rail incl. "A" timber...

    vee_joint_space_co1:=vee_spco1;
    vee_joint_space_co2:=vee_spco2;
    vee_joint_space_co3:=vee_spco3;
    vee_joint_space_co4:=vee_spco4;
    vee_joint_space_co5:=vee_spco5;
    vee_joint_space_co6:=vee_spco6;

               // number of timbers spanned by wing rail front excl. "A" timber...

    wing_joint_space_co1:=wing_spco1;
    wing_joint_space_co2:=wing_spco2;
    wing_joint_space_co3:=wing_spco3;
    wing_joint_space_co4:=wing_spco4;
    wing_joint_space_co5:=wing_spco5;
    wing_joint_space_co6:=wing_spco6;

  end;//with

  with box_kd.box_dims1 do begin     // check and wing rails...

    proto_info.wing_ms_reach1_pi:=cpi.wing_ms_reach1_pi;
    proto_info.wing_ms_reach2_pi:=cpi.wing_ms_reach2_pi;

    proto_info.ck_ms_working1_pi:=cpi.ck_ms_working1_pi;
    proto_info.ck_ms_working2_pi:=cpi.ck_ms_working2_pi;
    proto_info.ck_ms_working3_pi:=cpi.ck_ms_working3_pi;

    proto_info.ck_ms_ext1_pi:=cpi.ck_ms_ext1_pi;
    proto_info.ck_ms_ext2_pi:=cpi.ck_ms_ext2_pi;

    proto_info.xing_fl_pi:=cpi.xing_fl_pi;

  end;//with
end;
//______________________________________________________________________________

procedure update_lengths(var box_kd:Tkeep_dims);  // update stored template timbering and plain track settings to match the control template.

     // split from update_timbering  214c

var
  n:integer;

begin
  with box_kd.turnout_info2.plain_track_info do begin

    if pt_i>4 then pt_custom:=True       // list index for custom plain track.
              else pt_custom:=False;

    list_index:=pt_i;
    rail_length:=railen[pt_i];                                    // rail length in inches (only used for custom lengths).
    sleepers_per_length:=sleeper_count[pt_i];                     // number of sleepers per length.
    for n:=0 to psleep_c do sleeper_centres[n]:=psleep[pt_i,n];   // spacings (only used for custom spacings).

    pt_spacing_name_str:=Copy(plain_track_form.plain_track_spacings_listbox.Items.Strings[pt_i],1,198);   // get name from the list.

    rail_joints_code:=rjcode;  // 0=normal, 1=staggered, -1=none (cwr).

  end;//with
end;
//______________________________________________________________________________

procedure update_timbering(var box_kd:Tkeep_dims);  // update stored template timbering to match the control template.

begin
  with box_kd do begin

    with turnout_info2 do begin

      timber_length_inc:=timbinc;                          // length increments  212a
      diamond_proto_timbering_flag:=hd_proto_timbering;    // 212a

      diamond_switch_timbering_flag:=hd_switch_timbering;  // 213a

      equalizing_fixed_flag:=equalizing_fixed;          // {spare_flag1:boolean;}  equalizing style 1-4-00
      no_timbering_flag:=no_timbering;                  // {spare_flag2:boolean;}  7-9-00
      angled_on_flag:=square_on_angled;                 // True = angled-on timbering; 29-7-01

      crossing_info.curviform_timbering_keep:=curviform_timbering;   // 215a

      crossing_info.simplified_short_angle_keep:=simplified_short_angle;  // 228a

    end;//with

    with box_dims1 do begin

      proto_info.tbwide_pi:=tbwide;           // inches full-size width of turnout timbers.
      proto_info.slwide_pi:=slwide;           // inches full-size width of plain sleepers.

      proto_info.jt_slwide_pi:=jt_slwide;     // inches full-size width of joint sleepers. 212a

      proto_info.tb_end_offset_pi:=tb_end_offset;  // 232a

      proto_info.ftimbspmax_pi:=ftimbspmax;   // inches full-size max timber-spacing for closure space.
      proto_info.tb_pi:=tb;                   // plain sleepers length.

      proto_info.mainside_ends_pi:=ms_ends;    //  True=main side ends in line, False=ends centralized.
      proto_info.random_end_pi:=randend;       //  amount of timber-end randomising.
      proto_info.random_angle_pi:=randangle;   //  amount of timber_angle randomising.

      with turnout_info1 do begin

        timbering_flag:=timbers_equalized;  //  True = equalized timbering.
        exit_timbering:=exittb_i;           //  exit timbering style.

        front_timbers_flag:=include_front_timbers;      //  218a
        switch_timbers_flag:=include_switch_timbers;    //  218a
        closure_timbers_flag:=include_closure_timbers;  //  218a
        xing_timbers_flag:=include_xing_timbers;        //  218a

        approach_rails_only_flag:=approach_rails_only;  // 218a

        rolled_in_sleepered_flag:=rolled_in_sleepered;  // 223a

      end;//with

        // for use in files when reloaded by versions prior to 0.75.a..

      proto_info.xtimbsp_pi:=(veetimb_sp+wingtimb_sp)/2;  // full-size inches - timber spacing for crossing.

    end;//with
  end;//with
end;
//___________________________________________________________________________________________

function shove_number_clicked(X,Y:integer):boolean;  // screen co-ords of a click, is it on a timber number?

var
  i,n:integer;
  code:integer;
  ptr_1st:^Tmark;         // pointer to a Tmark record..
  markmax:integer;
  num_str,tbnum_str:string;
  num_X,num_Y,half_width,half_height:integer;

begin

  RESULT:=False;   // 0.91.c  init.

  with enter_timber_form.Canvas do begin               // use as dummy canvas
    Font.Assign(pad_form.pad_timber_font_label.Font);

     // 0.91.b was Screen.Width ..

    if pad_form.ClientWidth>1000 then half_width:=TextWidth('MM') div 2    // (=10 for default font) clicking area (arbitrary - bitmap fonts).
                                 else half_width:=TextWidth('FF') div 2;   // (=7 for ditto).

    half_height:=TextHeight('A') div 2;
  end;//with

      // find number string clicked on in control template marks list...

  if Length(marks_list)=0 then EXIT;        // pointer to marks list not valid.

  markmax:=High(marks_list);  // max index for the present list.

  if mark_index>markmax then mark_index:=markmax;  // ??? shouldn't be.

  tbnum_str:=timb_numbers_str;      // the full string of timber numbering for the control template.

  for i:=0 to (mark_index-1) do begin     // (mark_index is always the next free slot)
    try
      ptr_1st:=@marks_list[i];  // pointer to the next Tmark record.

      code:=ptr_1st^.code;

      if code<>99 then CONTINUE;   // we are only looking for timber number entries.

      num_str:=extract_tbnumber_str(tbnum_str);   // get next timber numbering string from the acummulated string.
      if num_str='' then CONTINUE;                // no string available??

      num_X:=Round(ptr_1st^.p1.X*sx+ex-gx);       // and the centre co-ords for it.
      num_Y:=Round((ptr_1st^.p1.Y+yd)*sy+by-gy);

      if (X>=(num_X-half_width)) and
         (X<=(num_X+half_width)) and
         (Y>=(num_Y-half_height)) and
         (Y<=(num_Y+half_height))
         then begin

                RESULT:=True;   // 0.91.c  clicked on a timber number.

                highlighted_timbnum_str:='';       // prevent de-highlighting of this one.

                num_str:=timb_num_strip(num_str);
                if num_str=current_shove_str then EXIT;       // not if already on it.

                current_shove_str:=num_str;
                heave_selected_tb_str:='';   // 556b de-select clicked chair label

                n:=find_shove(current_shove_str,True); // find it or an empty slot, or warn him if no room.
                if n>=0                                // found a slot.
                   then begin
                          with current_shoved_timbers[n].shove_data do begin
                            if sv_code=0
                               then begin
                                      sv_code:=1;                                         // flag to shove this timber if not already in list.
                                      current_shoved_timbers[n].sv_str:=current_shove_str;
                                    end;
                            shove_buttons(True,sv_code,n);
                          end;//with
                        end
                   else begin
                          current_shove_str:='';         // !!! no room for it ?
                          heave_selected_tb_str:='';     // 556b de-select clicked chair label
                          shove_buttons(False,0,-1);
                        end;
                redraw(True);
                EXIT;
              end;
    except
      CONTINUE;
    end;//try

  end;//next i timber number
end;
//____________________________________________________________________________________________

procedure mouse_on_timber_number(X,Y:integer);   // highlight timber number if mouse currently over it.

var
  i:integer;
  code:integer;
  ptr_1st:^Tmark;         // pointer to a Tmark record..
  markmax:integer;
  num_str,tbnum_str:string;
  num_x,num_y:extended;
  int_num_X,int_num_Y:integer;
  half_width,half_height:integer;
  move_to:TPoint;
  half_stringwidth,half_stringheight:integer;

  dummy_i:integer;


begin

  with enter_timber_form.Canvas do begin
    Font.Assign(pad_form.pad_timber_font_label.Font);

    // 0.91.b was Screen.Width

    if pad_form.ClientWidth>1000 then half_width:=TextWidth('MM') div 2    // (=10 for default font) clicking area (arbitrary - bitmap fonts).
                                 else half_width:=TextWidth('FF') div 2;   // (=7 for ditto).

    half_height:=TextHeight('A') div 2;
  end;//with


  if Length(marks_list)=0 then EXIT;        // pointer to marks list not valid.

  markmax:=High(marks_list);  // max index for the present list.

  if mark_index>markmax then mark_index:=markmax;  // ??? shouldn't be.

  tbnum_str:=timb_numbers_str;      // the full string of timber numbering for the control template.

  for i:=0 to (mark_index-1) do begin     // (mark_index is always the next free slot)
    try
      ptr_1st:=@marks_list[i];  // pointer to the next Tmark record.

      code:=ptr_1st^.code;

      if code<>99 then CONTINUE;   // we are only looking for timber number entries.

      num_str:=extract_tbnumber_str(tbnum_str);   // get next timber numbering string from the acummulated string.
      if num_str='' then CONTINUE;                // no string available??

      num_x:=ptr_1st^.p1.X*sx+ex-gx;       // and the centre co-ords for it.
      num_y:=(ptr_1st^.p1.Y+yd)*sy+by-gy;

      int_num_X:=Round(num_x);
      int_num_Y:=Round(num_y);

      if (X>=(int_num_X-half_width)) and
         (X<=(int_num_X+half_width)) and
         (Y>=(int_num_Y-half_height)) and
         (Y<=(int_num_Y+half_height))
         then begin

                if timb_num_strip(num_str)=current_shove_str then EXIT;  // do nothing if over the currently selected timber number.

                move_to.X:=Round(limits(h_minint,h_maxint,num_x,dummy_i));
                move_to.Y:=Round(limits(h_minint,h_maxint,num_y,dummy_i));

                if check_limit(True,True,move_to)=True
                   then begin
                          if num_str[1]='!'               // it's an omitted timber
                             then Delete(num_str,1,1);    // remove the omit indicator.

                          with pad_form.Canvas do begin     // direct overdraw.

                            Font.Assign(pad_form.pad_timber_font_label.Font);

                            if (highlighted_timbnum_str<>'') and (highlighted_timbnum_str<>num_str) and (timb_num_strip(highlighted_timbnum_str)<>current_shove_str) // first de-highlight any previous number (but not this one)...
                               then begin
                                         Brush.Color:=paper_colour;
                                         Brush.Style:=bsSolid;

                                         TextOut(highlighted_timbnum_X,highlighted_timbnum_Y,highlighted_timbnum_str+' ');
                                    end;

                            half_stringwidth:=TextWidth(num_str) div 2;
                            half_stringheight:=TextHeight(num_str) div 2;

                                 // save new locations of highlighted timber, so can de-highlight next time...

                            highlighted_timbnum_X:=move_to.X-half_stringwidth;
                            highlighted_timbnum_Y:=move_to.Y-half_stringheight;
                            highlighted_timbnum_str:=num_str;

                            if paper_colour=clLime
                               then begin
                                      Brush.Color:=clBlack;      // highlight selected timber number for shoving.
                                      Font.Color:=clWhite;
                                    end
                               else begin
                                      Brush.Color:=clLime;
                                      Font.Color:=clBlack;
                                    end;

                            Brush.Style:=bsSolid;

                            TextOut(highlighted_timbnum_X,highlighted_timbnum_Y,highlighted_timbnum_str+' ');  // looks better widened.
                            Font.Assign(pad_form.Font);
                            Brush.Color:=paper_colour;
                          end;//with
                        end;
                EXIT;
              end;
    except
      CONTINUE;
    end;//try

  end;//next i timber number

     // no match found, mouse moved off, so de-highlight any previous...

  if highlighted_timbnum_str<>''
     then begin
            with pad_form.Canvas do begin     // direct overdraw.

              Font.Assign(pad_form.pad_timber_font_label.Font);

              Brush.Color:=paper_colour;
              Brush.Style:=bsSolid;

              TextOut(highlighted_timbnum_X,highlighted_timbnum_Y,highlighted_timbnum_str+' ');

              Font.Assign(pad_form.Font);
            end;//with
          highlighted_timbnum_str:='';
        end;
end;
//______________________________________________________________________________

function chair_label_clicked(X,Y:integer):boolean;  // screen co-ords of a click, is it on an unslected chair label?  MW 03-08-2024  555a

var
  i,n:integer;
  code:integer;
  ptr_1st:^Tmark;         // pointer to a Tmark record..
  markmax:integer;
  label_str:string;
  label_X,label_Y,half_width,half_height:integer;

  tb_encoded:double;
  tb_id:integer;
  tb_str,timber_str:string;

  rail_number:integer;

begin
  RESULT:=False;     //  init..

  heave_chairs_form.rail_highlight_shape.Visible:=False;   // yellow groupbox outline

      // find label string clicked on...

  if Length(marks_list)=0 then EXIT;        // pointer to marks list not valid.

  markmax:=High(marks_list);  // max index for the present list.

  if mark_index>markmax then mark_index:=markmax;  // ??? shouldn't be.

  for i:=0 to (mark_index-1) do begin     // (mark_index is always the next free slot)
    try
      ptr_1st:=@marks_list[i];  // pointer to the next Tmark record.

      code:=ptr_1st^.code;

      case code of
        461..478: do_nothing;     // not 441..458 = already selected
             else CONTINUE;
      end;//case

      tb_encoded:=ptr_1st^.tb_code/10000;

      tb_str:=Chr(Trunc(tb_encoded));        // 237a
      tb_id:=Round(Frac(tb_encoded)*10000);

      timber_str:=tb_str+IntToStr(tb_id);  // 241f

      label_str:=get_chair_str(ptr_1st^.dxf_chair_code);

      rail_number:=(ptr_1st^.mark_bits AND $00000003)+1; // in 2 ls bits

      with enter_timber_form.Canvas do begin               // use as dummy canvas
        Font.Assign(pad_form.pad_timber_font_label.Font);  // for font name

        Font.Style:=[fsBold];

        Font.Height:=Round(0-4.5*inscale/ffx);     // scale 4.5" arbitrary    ffx mm per screen dot

        half_width:=Round((TextWidth(label_str)+ABS(Font.Height))/2);
        half_height:=Round(TextHeight(label_str)/2)+2;
      end;//with

      label_X:=Round(ptr_1st^.p1.X*sx+ex-gx);       // and the centre co-ords for it.
      label_Y:=Round((ptr_1st^.p1.Y+yd)*sy+by-gy);

      if (X>=(label_X-half_width)) and
         (X<=(label_X+half_width)) and
         (Y>=(label_Y-half_height)) and
         (Y<=(label_Y+half_height))
         then begin

                RESULT:=True;   // clicked on a chair label

                pad_form.shove_timbers_menu_entry.Click;

                enter_timber_form.shove_combo.Text:=timber_str;

                if select_entered_timber=False
                   then begin
                          show_modal_message('error - timber not found'+#13+#13+'rail: '+inttostr(rail_number)+'  '+timber_str+'  '+label_str);
                          EXIT;
                        end;

                shove_timber_form.heave_chairs_button.Click;  // must do first - updates form

                do_chair_selected(rail_number);     // heave_chairs_unit

                redraw(True);
                EXIT;
              end;
    except
      CONTINUE;
    end;//try

  end;//next i
end;
//______________________________________________________________________________

function shape_clicked(X,Y:integer):boolean;  // 229a is it on a background shape?

   // p1 location tested only

var
  n:integer;
  clip_proximity:integer;

  pad_clip_point:TPoint;

begin
  RESULT:=False;   // init.

  clip_proximity:=Round(scale*100*sx);  // arbitrary 1ft

  with bgnd_form.bgnd_shapes_listbox.Items do begin

    if Count<1 then EXIT; // no shapes

    for n:=0 to Count-1 do begin

      with Tbgshape(Objects[n]).bgnd_shape do begin

        pad_clip_point.X:=Round((p1.x*100+re_org_x)*sx+ex-gx);
        pad_clip_point.Y:=Round((p1.y*100+re_org_y)*sy+by-gy);

       if (X>(pad_clip_point.X-clip_proximity)) and (X<(pad_clip_point.X+clip_proximity))
      and (Y>(pad_clip_point.Y-clip_proximity)) and (Y<(pad_clip_point.Y+clip_proximity))
          then begin
                 bgnd_form.bgnd_shapes_listbox.ItemIndex:=n;
                 RESULT:=True;
                 redraw(True);  // show it red
                 BREAK;
               end;
      end;//with

    end;//next
  end;//with
end;
//______________________________________________________________________________

function symbol_clicked(X,Y:integer):boolean;  // 227a is it on a symbol?

var
  n:integer;

begin
  RESULT:=False;   // init.

  if Length(current_symbols)<1 then EXIT;  // no symbols

  for n:=0 to Length(current_symbols)-1 do begin

    if n>(gaps_form.symbols_visible_listbox.Items.Count-1) then EXIT;  // ???

    with current_symbols[n].drawn_symbol do begin

    if (X>(pad_symbol_point.X-pad_proximity)) and (X<(pad_symbol_point.X+pad_proximity))
   and (Y>(pad_symbol_point.Y-pad_proximity)) and (Y<(pad_symbol_point.Y+pad_proximity))
       then begin
              gaps_form.symbols_visible_listbox.ItemIndex:=n;
              symbols_visible_listbox_clicked(n);               // show highlighted

              RESULT:=True;
              BREAK;
            end;

    end;//with
  end;//next
end;
//______________________________________________________________________________

function checkrail_label_clicked(X,Y:integer):boolean;  // 0.94.a screen co-ords of a click, is it on a check-rail label?

var
  i,n:integer;
  code:integer;
  ptr_1st:^Tmark;         // pointer to a Tmark record..
  markmax:integer;
  num_X,num_Y,half_width,half_height:integer;

begin

  RESULT:=False;   // 0.91.c  init.

  with enter_timber_form.Canvas do begin
    Font.Assign(pad_form.pad_timber_font_label.Font);

     // 0.91.b was Screen.Width ..

    if pad_form.ClientWidth>1000 then half_width:=TextWidth('MM') div 2    // (=10 for default font) clicking area (arbitrary - bitmap fonts).
                                 else half_width:=TextWidth('FF') div 2;   // (=7 for ditto).

    half_height:=TextHeight('A') div 2;
  end;//with

      // find number string clicked on in control template marks list...

  if Length(marks_list)=0 then EXIT;        // pointer to marks list not valid.

  markmax:=High(marks_list);  // max index for the present list.

  if mark_index>markmax then mark_index:=markmax;  // ??? shouldn't be.

  for i:=0 to (mark_index-1) do begin     // (mark_index is always the next free slot)
    try
      ptr_1st:=@marks_list[i];  // pointer to the next Tmark record.

      code:=ptr_1st^.code;

      if code<501 then CONTINUE;   // we are only looking for check-rail label entries.



      num_X:=Round(ptr_1st^.p1.X*sx+ex-gx);       // and the centre co-ords for it.
      num_Y:=Round((ptr_1st^.p1.Y+yd)*sy+by-gy);

      if (X>=(num_X-half_width)) and
         (X<=(num_X+half_width)) and
         (Y>=(num_Y-half_height)) and
         (Y<=(num_Y+half_height))
         then begin

                RESULT:=True;   // 0.91.c  clicked on a timber number.

                highlighted_timbnum_str:='';       // prevent de-highlighting of this one.

                if code=current_diff_code then EXIT;       // not if already on it.

                current_diff_code:=code;          // will now be highlighted

                redraw(True);
                EXIT;
              end;
    except
      CONTINUE;
    end;//try

  end;//next i
end;
//____________________________________________________________________________________________

procedure mouse_on_check_label(X,Y:integer);   // 0.94.a highlight check_label if mouse currently over it.

var
  i:integer;
  code:integer;
  ptr_1st:^Tmark;         // pointer to a Tmark record..
  markmax:integer;
  num_str,tbnum_str:string;
  num_x,num_y:extended;
  int_num_X,int_num_Y:integer;
  half_width,half_height:integer;
  move_to:TPoint;
  half_stringwidth,half_stringheight:integer;

  dummy_i:integer;


begin
  with enter_timber_form.Canvas do begin
    Font.Assign(pad_form.pad_timber_font_label.Font);

    if pad_form.ClientWidth>1000 then half_width:=TextWidth('MM') div 2    // (=10 for default font) clicking area (arbitrary - bitmap fonts).
                                 else half_width:=TextWidth('FF') div 2;   // (=7 for ditto).

    half_height:=TextHeight('A') div 2;
  end;//with

  if Length(marks_list)=0 then EXIT;        // pointer to marks list not valid.

  markmax:=High(marks_list);  // max index for the present list.

  if mark_index>markmax then mark_index:=markmax;  // ??? shouldn't be.

  tbnum_str:=timb_numbers_str;      // the full string of timber numbering for the control template.

  for i:=0 to (mark_index-1) do begin     // (mark_index is always the next free slot)
    try
      ptr_1st:=@marks_list[i];  // pointer to the next Tmark record.

      code:=ptr_1st^.code;

      case code of

          501: num_str:='MS1';
          502: num_str:='MS2';
          503: num_str:='MS3';
          504: if half_diamond=True then num_str:='DS1' else num_str:='TS1';
          505: if half_diamond=True then num_str:='DS2' else num_str:='TS2';
          506: if half_diamond=True then num_str:='DS3' else num_str:='TS3';
          507: num_str:='MS4';
          508: num_str:='DS4';

          else CONTINUE; // we are looking only for check label entries

      end;//case

      num_x:=ptr_1st^.p1.X*sx+ex-gx;       // and the centre co-ords for it.
      num_y:=(ptr_1st^.p1.Y+yd)*sy+by-gy;

      int_num_X:=Round(num_x);
      int_num_Y:=Round(num_y);

      if (X>=(int_num_X-half_width)) and
         (X<=(int_num_X+half_width)) and
         (Y>=(int_num_Y-half_height)) and
         (Y<=(int_num_Y+half_height))
         then begin

                if code=current_diff_code then EXIT;  // do nothing if over the currently selected label.

                move_to.X:=Round(limits(h_minint,h_maxint,num_x,dummy_i));
                move_to.Y:=Round(limits(h_minint,h_maxint,num_y,dummy_i));

                if check_limit(True,True,move_to)=True
                   then begin
                          with pad_form.Canvas do begin     // direct overdraw.

                            Font.Assign(pad_form.pad_timber_font_label.Font);

                               // 0.94.a re-use shove timber variables ...

                            if (highlighted_timbnum_str<>'') and (highlighted_timbnum_str<>num_str) // first de-highlight any previous label (but not this one)...
                               then begin
                                      Brush.Color:=paper_colour;
                                      Brush.Style:=bsSolid;

                                      TextOut(highlighted_timbnum_X,highlighted_timbnum_Y,highlighted_timbnum_str+' ');
                                    end;

                            half_stringwidth:=TextWidth(num_str) div 2;
                            half_stringheight:=TextHeight(num_str) div 2;

                                 // save new locations of highlighted label, so can de-highlight next time...

                            highlighted_timbnum_X:=move_to.X-half_stringwidth;
                            highlighted_timbnum_Y:=move_to.Y-half_stringheight;
                            highlighted_timbnum_str:=num_str;

                            if paper_colour=$00FF0060   // violet
                               then begin
                                      Brush.Color:=clBlack;      // highlight selected label for diffing.
                                      Font.Color:=clWhite;
                                    end
                               else begin
                                      Brush.Color:=$00FF0060;
                                      Font.Color:=clWhite;
                                    end;

                            Brush.Style:=bsSolid;

                            Pen.Width:=1;       // draw the rectangle first...
                            Pen.Mode:=pmCopy;
                            Pen.Style:=psSolid;
                            Pen.Color:=Font.Color;

                            RoundRect(move_to.X-half_stringwidth-3,move_to.Y-half_stringheight-2,move_to.X+half_stringwidth+4,move_to.Y+half_stringheight+3,6,6);

                            highlighted_round_rect_x1:=move_to.X-half_stringwidth-3;
                            highlighted_round_rect_y1:=move_to.Y-half_stringheight-2;
                            highlighted_round_rect_x2:=move_to.X+half_stringwidth+4;
                            highlighted_round_rect_y2:=move_to.Y+half_stringheight+3;

                            TextOut(highlighted_timbnum_X,highlighted_timbnum_Y,highlighted_timbnum_str);
                            Font.Assign(pad_form.Font);
                            Brush.Color:=paper_colour;
                          end;//with
                        end;
                EXIT;
              end;
    except
      CONTINUE;
    end;//try

  end;//next i

           // no match found, mouse moved off, so de-highlight any previous...

  if highlighted_timbnum_str<>''
     then begin
            with pad_form.Canvas do begin     // direct overdraw.

              Font.Assign(pad_form.pad_timber_font_label.Font);

              Brush.Color:=paper_colour;
              Brush.Style:=bsSolid;

              Pen.Width:=1;       // draw the rectangle first...
              Pen.Mode:=pmCopy;
              Pen.Style:=psSolid;
              Pen.Color:=Font.Color;
              RoundRect(highlighted_round_rect_x1,highlighted_round_rect_y1,highlighted_round_rect_x2,highlighted_round_rect_y2,6,6);

              TextOut(highlighted_timbnum_X,highlighted_timbnum_Y,highlighted_timbnum_str);

              Font.Assign(pad_form.Font);
            end;//with
          highlighted_timbnum_str:='';
        end;
end;
//______________________________________________________________________________

procedure mouse_on_chair_label(X,Y:integer);    // flag mark index if mouse currently over it   MW 05-08-2024  555a

var
  i:integer;
  code:integer;
  ptr_1st:^Tmark;         // pointer to a Tmark record..
  markmax:integer;
  num_x,num_y:extended;
  half_width,half_height:integer;
  int_num_X,int_num_Y:integer;

  label_str:string;

  mouse_was_over:boolean;

begin   // find if on chair label...

  mouse_was_over:=(mouse_is_over_chair_label_mark>-1);  // to prevent flickering

  mouse_is_over_chair_label_mark:=-1;   // init

  if Length(marks_list)=0 then EXIT;        // pointer to marks list not valid.

  markmax:=High(marks_list);  // max index for the present list.

  if mark_index>markmax then mark_index:=markmax;  // ??? shouldn't be.

  for i:=0 to (mark_index-1) do begin     // (mark_index is always the next free slot)

    ptr_1st:=@marks_list[i];  // pointer to the next Tmark record.

    code:=ptr_1st^.code;

    case code of
      461..478: do_nothing;    // not 441..458 = already selected
           else CONTINUE;
    end;//case

    label_str:=get_chair_str(ptr_1st^.dxf_chair_code);

     if ptr_1st^.dxf_chair_code=14      // over-ride check flare-in from "CCL/R"
        then case code of
               461: label_str:='CCL';     // MS LH TEMPLATE - TS RH TEMPLATE
               471: label_str:='CCR';     // TS LH TEMPLATE - MS RH TEMPLATE
             end;

     if ptr_1st^.dxf_chair_code=16      // over-ride check flare-out from "CCR/L"
        then case code of
               461: label_str:='CCR';     // MS LH TEMPLATE - TS RH TEMPLATE
               471: label_str:='CCL';     // TS LH TEMPLATE - MS RH TEMPLATE
             end;

    with enter_timber_form.Canvas do begin               // use as dummy canvas
      Font.Assign(pad_form.pad_timber_font_label.Font);  // for font name

      Font.Style:=[fsBold];

      Font.Height:=Round(0-4.5*inscale/ffx);     // scale 4.5" arbitrary    ffx mm per screen dot

      half_width:=Round((TextWidth(label_str)+ABS(Font.Height))/2);
      half_height:=Round(TextHeight(label_str)/2)+2;
    end;//with

    num_x:=ptr_1st^.p1.X*sx+ex-gx;        // label centre
    num_y:=(ptr_1st^.p1.Y+yd)*sy+by-gy;

    int_num_X:=Round(num_x);
    int_num_Y:=Round(num_y);

    if (X>=(int_num_X-half_width)) and
       (X<=(int_num_X+half_width)) and
       (Y>=(int_num_Y-half_height)) and
       (Y<=(int_num_Y+half_height))
       then begin                                 // mouse is over the label
              mouse_is_over_chair_label_mark:=i;  // which mark

              redraw(True);
              EXIT;
          end;

  end;//next i

  if mouse_was_over=True then redraw(True);  // remove any previous highlight
end;
//______________________________________________________________________________

procedure enable_peg_positions;    // enable/disable the peg options (for Ctrl-# KB shortcuts).

begin
  with pad_form do begin
    case peg_code of

      -2: peg_on_joints_menu_entry.Checked:=True;       // radio item.  'J'  on plain track joints
      -1: set_peg_menu_entry.Checked:=True;             // radio item.  'S'  as set
       0: reset_peg_menu_entry.Checked:=True;           // radio item.  '0'   datum
       1: peg_on_joint_end_menu_entry.Checked:=True;    // radio item.  '1'   joint
       2: peg_on_tp_menu_entry.Checked:=True;           // radio item.  '2'   TP
       3: peg_on_dp_menu_entry.Checked:=True;           // radio item.  'N'   DP  234d was '3'
       4: peg_on_fp_menu_entry.Checked:=True;           // radio item.  '4'   FP

       5: peg_on_tcp_menu_entry.Checked:=True;          // radio item. 'N'   TCP
       6: peg_on_txp_menu_entry.Checked:=True;          // radio item. '5'   TXP
       7: peg_on_trp_menu_entry.Checked:=True;          // radio item. 'N'   TRP

       8: peg_on_mcp_menu_entry.Checked:=True;          // radio item. 'N'   MCP
       9: peg_on_mxp_menu_entry.Checked:=True;          // radio item. '7'   MXP
      10: peg_on_mrp_menu_entry.Checked:=True;          // radio item. 'N'   MRP
      11: peg_on_overall_length_menu_entry.Checked:=True; // radioitem '9'   length
      12: peg_on_adjacent_ts_menu_entry.Checked:=True;  // radio item. 'N'   TS   adjacent turnout-side track centre
      13: peg_on_adjacent_ms_menu_entry.Checked:=True;  // radio item. 'N'   MS   adjacent main-side track centre
      14: peg_on_TORG_menu_entry.Checked:=True;         // radio item. 'N'   TORG turnout rad centre
      15: peg_on_trans_start_menu_entry.Checked:=True;  // radio item. 'N'        transition start
      16: peg_on_trans_length_menu_entry.Checked:=True; // radio item. 'N'        transition end (length)
      17: peg_on_MVJP_menu_entry.Checked:=True;         // radio item. '8'   MVJP vee joint (point rail end)
      18: peg_on_TVJP_menu_entry.Checked:=True;         // radio item. '6'   TVJP vee joint (splice rail end)
      19: peg_on_midlength_menu_entry.Checked:=True;    // radio item  'N'   mid-length

      20: set_peg_menu_entry.Checked:=True;             // radio item. 'M' OR 'F'  (mouse or free)

      22: peg_on_tp_menu_entry.Checked:=True;           // radio item. '2'   TP   toggle CTRL-2 options
      33: peg_on_dp_menu_entry.Checked:=True;           // radio item. '3'   DP   toggle CTRL-3 options
      44: peg_on_fp_menu_entry.Checked:=True;           // radio item. '4'   FP   toggle CTRL-4 options

     100: peg_on_planing_menu_entry.Checked:=True;      // radio item. 'N'   planing end
     104: peg_on_heel_menu_entry.Checked:=True;         // radio item. 'N'   switch heel
     108: peg_on_CESP_menu_entry.Checked:=True;         // radio item. 'N'   CESP (crossing entry straight point=end of turnout curve)

     200: peg_on_EGTP_menu_entry.Checked:=True;         // radio item. 'N'   EGTP external geometrical radius tangent point.
     201: peg_on_EGORG_menu_entry.Checked:=True;        // radio item. 'N'   EGORG external geometrical radius origin.
     220: peg_on_IGTP_menu_entry.Checked:=True;         // radio item. 'N'   IGTP internal geometrical radius tangent point.
     221: peg_on_IGORG_menu_entry.Checked:=True;        // radio item. 'N'   IGORG internal geometrical radius tangent point.

     222: peg_on_split_deflection_menu_entry.Checked:=True;   // radio item. 'N'   split deflection

     240: peg_on_TMINP_menu_entry.Checked:=True;        // radio item. 'N'   TMINP minimum turnout road  213b
     241: peg_on_TEXITP_menu_entry.Checked:=True;       // radio item. 'E'   TEXITP turnout road exit  213b

     260: peg_on_MMINP_menu_entry.Checked:=True;        // radio item. 'N'   MMINP minimum main road  217a
     261: peg_on_MEXITP_menu_entry.Checked:=True;       // radio item. 'E'   MEXITP main road exit    217a

     300: peg_on_slew_start_menu_entry.Checked:=True;   // radio item. 'N'   slewing start
     301: peg_on_slew_length_menu_entry.Checked:=True;  // radio item. 'N'   slewing end (length)

     400: peg_on_trans_org_menu_entry.Checked:=True;    // radio item. 'N'   transition calcs origin

     600: peg_on_tolp_menu_entry.Checked:=True;         // radio item. 'N'   turnout-road opposite full-length position.  0.79.a  29-05-06
     610: peg_on_blanked_length_menu_entry.Checked:=True; // radio item. '3' blanking length. 0.82.a    234d was 'N'

     615: peg_on_ip_menu_entry.Checked:=True;           // radio item. 'N'   IP  Intermediate  // 208a

     620: peg_on_osmp_menu_entry.Checked:=True;          // radio item. 'N'   OSMP   outside-slip K match mirror position   226a

     625: peg_on_osxp_menu_entry.Checked:=True;          // radio item. 'N'   OSXP   outside-slip K match intersection position   226a

     700: peg_on_dummy_vehicle_menu_entry.Checked:=True;  // 223a radio item. 'N' dummy vehicle pin

     801: peg_on_kp1_menu_entry.Checked:=True;  // 227a radio item. 'N' KP1 toex rail 1   K-crossing
     802: peg_on_kp2_menu_entry.Checked:=True;  // 227a radio item. 'N' KP2 toex rail 2   K-crossing
     803: peg_on_kp3_menu_entry.Checked:=True;  // 227a radio item. 'N' KP3 0-toex rail 3 K-crossing
     804: peg_on_kp4_menu_entry.Checked:=True;  // 227a radio item. 'N' KP4 0-toex rail 4 K-crossing

     999: peg_on_user_defined_menu_entry.Checked:=True; // radio item. 'U'   user-defined position.
    end;//case

    peg_on_slew_start_menu_entry.Enabled:= slewing;
    peg_on_slew_length_menu_entry.Enabled:=slewing;

    peg_on_trans_start_menu_entry.Enabled:= spiral;
    peg_on_trans_length_menu_entry.Enabled:=spiral;
    peg_on_trans_org_menu_entry.Enabled:=   spiral;

    peg_on_trp_menu_entry.Enabled:={(retpar_i=1) and} NOT plain_track;
    peg_on_mrp_menu_entry.Enabled:={(retpar_i=1) and} NOT plain_track;

    peg_on_EGTP_menu_entry.Enabled:=  NOT (spiral or slewing or plain_track);
    peg_on_EGORG_menu_entry.Enabled:= NOT (spiral or slewing or plain_track);
    peg_on_IGTP_menu_entry.Enabled:=  NOT (spiral or slewing or plain_track);
    peg_on_IGORG_menu_entry.Enabled:= NOT (spiral or slewing or plain_track);

    peg_on_tp_menu_entry.Enabled:=        NOT plain_track;

    peg_on_split_deflection_menu_entry.Enabled:= NOT plain_track;  // 213a

    peg_on_kp1_menu_entry.Enabled:= NOT plain_track;  // 227a
    peg_on_kp2_menu_entry.Enabled:= NOT plain_track;  // 227a
    peg_on_kp3_menu_entry.Enabled:= NOT plain_track;  // 227a
    peg_on_kp4_menu_entry.Enabled:= NOT plain_track;  // 227a

    peg_on_planing_menu_entry.Enabled:=   NOT (plain_track or ((half_diamond=True) and (fixed_diamond=True)));  // not for a fixed-diamond.
    peg_on_heel_menu_entry.Enabled:=      NOT (plain_track or (half_diamond=True));                             // not for fixed-diamond or switch-diamond
    peg_on_dp_menu_entry.Enabled:=        NOT plain_track;

    peg_on_ip_menu_entry.Enabled:=        NOT plain_track;   // 208a

    peg_on_osmp_menu_entry.Enabled:=       NOT plain_track;   // 226a
    peg_on_osxp_menu_entry.Enabled:=       NOT plain_track;   // 226a

    peg_on_CESP_menu_entry.Enabled:=      NOT (plain_track or (tradius_is_straight=True) or (xing_type_i<>0));  // no CESP  205e

    peg_on_fp_menu_entry.Enabled:=        NOT plain_track;
    peg_on_tcp_menu_entry.Enabled:=       NOT plain_track;
    peg_on_txp_menu_entry.Enabled:=       NOT plain_track;
    peg_on_mcp_menu_entry.Enabled:=       NOT plain_track;
    peg_on_mxp_menu_entry.Enabled:=       NOT plain_track;

    peg_on_TORG_menu_entry.Enabled:=      NOT (plain_track or (ABS(nomrad)<max_rad_test) or (spiral=True) or (xing_type_i<>1));
    peg_on_MVJP_menu_entry.Enabled:=      NOT plain_track;
    peg_on_TVJP_menu_entry.Enabled:=      NOT plain_track;

    peg_on_TMINP_menu_entry.Enabled:=      NOT plain_track;      // 213b
    peg_on_TEXITP_menu_entry.Enabled:=     NOT plain_track;      // 213b

    peg_on_MMINP_menu_entry.Enabled:=      NOT plain_track;      // 217a
    peg_on_MEXITP_menu_entry.Enabled:=     NOT plain_track;      // 217a

    peg_on_tolp_menu_entry.Enabled:=      NOT plain_track;        //   0.79.a  29-05-06

    //peg_on_blanked_length_menu_entry.Enabled:= NOT plain_track;   //   0.82.a  14-10-06     out 234e

    peg_on_joints_menu_entry.Enabled:=    plain_track;

    case peg_rail of                                           // radio items...
                  0: peg_is_free_menu_entry.Checked:=True;
                  1: peg_on_rail1_menu_entry.Checked:=True;
                  2: peg_on_rail2_menu_entry.Checked:=True;
                  3: peg_on_rail3_menu_entry.Checked:=True;
                  4: peg_on_rail4_menu_entry.Checked:=True;
                  5: peg_on_rail5_menu_entry.Checked:=True;
                  6: peg_on_rail6_menu_entry.Checked:=True;
                  7: peg_on_rail7_menu_entry.Checked:=True;
                  8: peg_on_rail8_menu_entry.Checked:=True;
                  9: peg_on_rail9_menu_entry.Checked:=True;
                else peg_on_rail8_menu_entry.Checked:=True;   // ms centre-line.
    end;//case

    if plain_track=True    // added 205c
       then peg_on_joint_end_menu_entry.Caption:='peg  on  rail  joint           Ctrl-1'
       else peg_on_joint_end_menu_entry.Caption:='peg  on  switch  front      Ctrl-1';

  end;//with
end;
//__________________________________________________________________________________________

procedure copy_template_info_from_to(clear_arrays:boolean; var from_info, to_info:Ttemplate_info);

      // v:0.71.a 29-4-01    copy template info, including the shove list and symbol list, and 3D chair options

begin
  to_info.keep_dims:=from_info.keep_dims;       // copy template data

  to_info.notch_info:=from_info.notch_info;     // 227d

  to_info.file_blocks.keep_jaw_options:=Copy(from_info.file_blocks.keep_jaw_options);  // 239a

  copy_shove_array(clear_arrays,from_info.file_blocks.keep_shoved_timbers,to_info.file_blocks.keep_shoved_timbers);  // copy the shoved timber data

  copy_symbols_array(clear_arrays,from_info.file_blocks.keep_symbols,to_info.file_blocks.keep_symbols);              // and the symbols

  copy_timbers_array(clear_arrays,from_info.keep_timber_counts,to_info.keep_timber_counts);  // and the timber counts

  copy_jaws_array(clear_arrays,from_info.keep_jaws,to_info.keep_jaws);           // and the jaws    237c
  copy_seats_array(clear_arrays,from_info.keep_seats,to_info.keep_seats);        // and the seats   237c
  copy_keys_array(clear_arrays,from_info.keep_keys,to_info.keep_keys);           // and the keys    237c
  copy_jmods_array(clear_arrays,from_info.keep_jmods,to_info.keep_jmods);        // and the jmods   237c

end;
//________________________________________________________________________________________

procedure warn_group_colour;

var
  i:integer;

begin
  if (pad_form.show_group_menu_entry.Checked=False)
     or (group_colour_msg_pref=True)
     or (any_selected<1)
        then EXIT;

  alert_box.preferences_checkbox.Checked:=False;       //%%%%
  alert_box.preferences_checkbox.Show;


  i:=alert(3,'    group  showing  in  selected  group  colour',
             'Selected groups of templates are normally shown in the SELECTED GROUP colour.'
            +'||To see this group in their MARKER colours, de-select the group (GROUP > GROUP SELECT NONE menu item).',
             '','','','de-select  the  group  now','','continue',0);

  group_colour_msg_pref:=alert_box.preferences_checkbox.Checked;    //%%%%
  alert_box.preferences_checkbox.Hide;

  if i=4
     then begin
            clear_all_selections;
            redraw(True);
          end;
end;
//______________________________________________________________________________

procedure show_switch_info(full_size_mm,already_showing:boolean);

var
  info_str, swnum_str, front_str:string;
  fs_unit_str:string;   // 208a
  fs_factor:extended;   // 208a
  i:integer;
  sw_info:Tswitch_info;

                              /////////////////////////////////////////

                              function val_str(d:extended):string;   // mods 208a

                                  // d is in full-size inches

                              begin
                                if ABS(d*inscale)>=max_rad_test
                                   then RESULT:=' = straight'
                                   else RESULT:=' = '+round_str(d*fs_factor,2)+'  ( '+round_str(d*inscale,2)+' model mm )';
                              end;
                              //////////////////////////////////////////


begin
  with switch_select_form.switch_selector_listbox do sw_info:=Tswitch(Items.Objects[ItemIndex]).list_switch_info;

  if full_size_mm=True
     then begin
            fs_factor:=25.4;
            fs_unit_str:='MM';
          end
     else begin
            fs_factor:=1.0;
            fs_unit_str:='INCHES';
          end;

  info_str:='Dimensions  for  '+sw_info.sw_name_str+'  Switch :|--------------------------------'
           +'||Dimensions are in FULL-SIZE prototype '+fs_unit_str+','
           +' with model conversions at '+round_str(scale,2)+' mm/ft.'
           +'||This switch is a ';

  case sw_info.sw_pattern of   // type of switch.  0 = curved planing or straight switch; -1 = semi-curved switch;  1 = double-curved switch.

      -1: begin   // semi-curved switch ...

            info_str:=info_str+'SEMI-CURVED pattern switch:';

            info_str:=info_str+'||straight planing length (along blade)'            +val_str(sw_info.planing);
            info_str:=info_str+'|straight planing unit angle = '                    +ram_clm_str(sw_info.planing_angle);  //   1 : '+round_str(sw_info.planing_angle,2);
            info_str:=info_str+'|lead length to heel (incl. planing)'               +val_str(sw_info.heel_lead_inches);
            info_str:=info_str+'||switch radius'                                    +val_str(sw_info.switch_radius_inchormax);
            info_str:=info_str+'||length of switch-front (stock-rail joint to toe)' +val_str(sw_info.switch_front_inches);
            info_str:=info_str+'|length of switch-rail (blade)'                     +val_str(sw_info.switch_rail);
            info_str:=info_str+'|length of stock-rail from joint'                   +val_str(sw_info.stock_rail);
          end;

       0: begin
            if sw_info.switch_radius_inchormax<max_rad_test  // not a straight switch.

               then begin       // curved switch...
                      info_str:=info_str+'CURVED pattern switch:';

                      info_str:=info_str+'||lead length to heel (incl. planing)'              +val_str(sw_info.heel_lead_inches);
                      info_str:=info_str+'|offset at the heel (heel spread)'                  +val_str(sw_info.heel_offset_inches);
                      info_str:=info_str+'||switch radius'                                    +val_str(sw_info.switch_radius_inchormax);
                      info_str:=info_str+'||length of switch-front (stock-rail joint to toe)' +val_str(sw_info.switch_front_inches);
                      info_str:=info_str+'|length of switch-rail (blade)'                     +val_str(sw_info.switch_rail);
                      info_str:=info_str+'|length of stock-rail from joint'                   +val_str(sw_info.stock_rail);
                    end
               else begin       // straight switch...
                      info_str:=info_str+'STRAIGHT pattern switch:';

                      info_str:=info_str+'||lead length to heel (incl. planing)'              +val_str(sw_info.heel_lead_inches);
                      info_str:=info_str+'|offset at the heel (heel spread)'                  +val_str(sw_info.heel_offset_inches);
                      info_str:=info_str+'||length of switch-front (stock-rail joint to toe)' +val_str(sw_info.switch_front_inches);
                      info_str:=info_str+'|length of switch-rail (blade)'                     +val_str(sw_info.switch_rail);
                      info_str:=info_str+'|length of stock-rail from joint'                   +val_str(sw_info.stock_rail);
                    end;
          end;

       1: begin
            info_str:=info_str+'DOUBLE-CURVED pattern switch (not yet implemented)';
          end;

    end;//case

  if calc_switch(sw_info,False,False)=0    // no error
     then begin
            info_str:=info_str+'||deflection angle at blade tip = '+ram_clm_str(k1n);

            info_str:=info_str+'|planing length (along stock-rail)'+val_str((plox-toex)/inscale);

            info_str:=info_str+'|heel angle = '+ram_clm_str(k2n);

            if sw_info.sw_pattern=-1 then info_str:=info_str+'|heel offset'+val_str(h/inscale);
          end;

  info_str:=info_str+'||FB switch-rail foot-width at blade tip (from stock-rail gauge-face)' +val_str(sw_info.fb_tip_offset);

  info_str:=info_str+'||sideways depth of joggle'            +val_str(sw_info.joggle_depth);
  info_str:=info_str+'|joggle-length in front of blade tips' +val_str(sw_info.joggle_length);

  if sw_info.front_timbered=True
     then front_str:='timber'
     else front_str:='sleeper';

  info_str:=info_str+'|-------------------|Timber spacings (to timber centres) :';
  info_str:=info_str+'||switch-front is '+front_str+'ed:';
  info_str:=info_str+'|spacing back from toe (blade tips) to first front '+front_str+' (J1)' +val_str(0-sw_info.sleeper_j1);
  info_str:=info_str+'|spacing back to next front '+front_str+' (J2)'                        +val_str(0-sw_info.sleeper_j2);
  info_str:=info_str+'|spacing back to next front '+front_str+' (J3)'                        +val_str(0-sw_info.sleeper_j3);
  info_str:=info_str+'|spacing back to next front '+front_str+' (J4)'                        +val_str(0-sw_info.sleeper_j4);
  info_str:=info_str+'|spacing back to next front '+front_str+' (J5)'                        +val_str(0-sw_info.sleeper_j5);

  info_str:=info_str+'||spacing forward from toe (blade tips) to first timber (S1)'   +val_str(sw_info.timber_centres[0])+'|';

  i:=1;

  while sw_info.timber_centres[i]>minfp do begin

    if sw_info.timber_centres[i+1]<minfp then swnum_str:='(T1)'                         // timber number.
                         else swnum_str:='(S'+IntToStr(i+1)+')';

    info_str:=info_str+'|spacing forward to next timber '+swnum_str+val_str(sw_info.timber_centres[i]);
    INC(i);

  end;//while

  if already_showing=False
     then begin

            data_child_form.Close; // if showing elsewhere

            data_child_form.Parent:=switch_select_form;

            data_child_form.Top:=8;

            switch_select_form.ClientWidth:=switch_select_form.datestamp_label.Width+data_child_form.Width+switch_select_form.datestamp_label.Height;

            if switch_select_form.ClientHeight<(data_child_form.Height+16) then switch_select_form.ClientHeight:=data_child_form.Top+data_child_form.Height+switch_select_form.datestamp_label.Height;

            data_child_form.Left:=switch_select_form.datestamp_label.Width;
          end;

  data_child_form.data_memo.Text:=insert_crlf_str(info_str);  //  replace embedded | chars with a CR.

  if (already_showing=False) or (data_child_form.Visible=False) then data_child_form.Show;
end;
//______________________________________________________________________________________

function set_csi_from_switch_info(sw_info:Tswitch_info):boolean;  // set current switch from supplied info.

var
  cu_sw_info:Tswitch_info;

begin
  RESULT:=False;        // default init.

  if sw_info.valid_data=False then EXIT;

  csi:=sw_info;         // set control template switch data.

  if csi.group_code<1   // custom switch, put a copy in bottom slot in list...
     then begin
            cu_sw_info:=csi;            // copy the data.

            cu_sw_info.group_code:=0;   // custom - in the info (for the bottom slot).
            cu_sw_info.size_code:=1;    // custom can only be one size (should be already 1).
            cu_sw_info.group_count:=1;  // ditto.

            with switch_select_form.switch_selector_listbox.Items do begin
              if Count>0
                 then begin
                        Tswitch(Objects[Count-1]).list_switch_info:=cu_sw_info;  // put custom data in selector list.
                        Strings[Count-1]:='  custom :  '+csi.sw_name_str;        // put custom name in selector list.
                      end;
            end;//with
          end;

  RESULT:=True;
end;//func
//________________________________________________________________________________________

procedure convert_to_regular_half_diamond;  // change turnout or IRREGULAR half-diamond to a REGULAR half-diamond

var
  dummy:extended;
  new_len:extended;

begin
  if plain_track=True then EXIT;

  if gaunt=True then convert_to_or_from_gaunt(False);  // 0.93.a cancel any gaunt

  startx:=0;       // cancel any blanking.

  if half_diamond=False  // converting from turnout
     then begin

            if xorg>0 then crop_approach;

            gocalc(0,0);

            pad_form.peg_on_dp_menu_entry.Click;    // put peg at DP Ctrl-3.
            gocalc(0,0);

            kform_now:=kform;
            docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy);    // save current peg data for peg_curve calcs.

            new_len:=turnoutx-dpx;                  // calc new length to end from DP.
            if new_len<0 then new_len:=mvjpx-dpx;

            if spiral=True then os:=os-dpx;
            if slewing=True then slew_s:=slew_s-dpx;

            half_diamond:=True;             // to half-diamond
            hd_switch_timbering:=False;     // init

            hdkn:=k3n;             // 0.93.a force regular pattern

            gocalc(0,0);

            xorg:=0;                          // should have been cropped anyway.
            turnoutx:=dpx+new_len;            // dpx has been re-calced for half-diamond.
            if turnoutx<0 then turnoutx:=0;
            if turnoutx>turnoutx_max then turnoutx:=turnoutx_max;

            turnout_i:=1;      // length locked at turnoutx.
            gocalc(0,0);

            peg_curve;
          end
     else begin

            kform_now:=kform;
            docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy);    // save current peg data for peg_curve calcs.

            hdkn:=k3n;             // 0.93.a force regular pattern

            gocalc(0,0);

          end;

  peg_curve;

  pad_form.reset_peg_menu_entry.Click;  // to CTRL-0
end;
//______________________________________________________________________________

procedure convert_to_turnout;

var
  dummy:extended;
  new_len:extended;

begin
  if (half_diamond=False) or (plain_track=True) then EXIT;

  osk_match:=hdkn;   // 226a  K match for outside-slip

  pad_form.peg_on_dp_menu_entry.Click;    // put peg at DP Ctrl-3.
  gocalc(0,0);

  kform_now:=kform;
  docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy);    // save current peg data for peg_curve calcs.

  new_len:=turnoutx-dpx;                 // calc new length to end from DP.
  if new_len<0 then new_len:=mvjpx-dpx;

  half_diamond:=False;
  hd_switch_timbering:=False;

  gocalc(0,0);

  if spiral=True then os:=os+dpx;
  if slewing=True then slew_s:=slew_s+dpx;

  xorg:=0;                          // should be 0 anyway.
  turnoutx:=dpx+new_len;            // dpx has been re-calced for turnout.
  if turnoutx<0 then turnoutx:=0;
  if turnoutx>turnoutx_max then turnoutx:=turnoutx_max;

  if startx<0 then startx:=0;       // cancel the negative blanking.

  turnout_i:=1;      // length locked at turnoutx.
  gocalc(0,0);

  peg_curve;

  pad_form.reset_peg_menu_entry.Click;  // to CTRL-0
end;
//________________________________________________________________________________________

procedure insert_half_diamond;

begin
  if plain_track=True
     then begin
            if gaunt=True then convert_to_or_from_gaunt(False);  // 0.93.a cancel any gaunt
            pad_form.reset_peg_menu_entry.Click;
            gocalc(0,0);
            insert_turnout;
            gocalc(0,0);
            half_diamond:=True;
            hd_switch_timbering:=False;   // init
          end;
end;
//____________________________________________________________________________________

procedure obtain_switch(n:integer);   // obtain control template switch from template in list.

var
  dummy:extended;

begin

  if (keeps_list.Count<1) or (n<0) or (n>(keeps_list.Count-1)) then EXIT;

  kform_now:=kform;
  docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy);    // save current peg data for peg_curve calcs.

  with Ttemplate(keeps_list.Objects[n]).template_info.keep_dims.turnout_info2 do begin

    if set_csi_from_switch_info(switch_info)=False  // set current switch from supplied info.
       then begin
              if set_csi_data(2,2)=False     // set REA B default if copied data invalid.
                 then run_error(82);         // ?????? no B switch in list?
            end;
  end;//with

  gocalc(0,0);     // calc new pegx.

  peg_curve;            // adjust shifts and rotates for current peg position.
  redraw_pad(True,True);
end;
//_____________________________________________________________________________________

procedure obtain_plain_track(n:integer);   // obtain control template plain-track settings from template in list.

begin
  if (keeps_list.Count<1) or (n<0) or (n>(keeps_list.Count-1)) then EXIT;

  with Ttemplate(keeps_list.Objects[n]).template_info.keep_dims.turnout_info2.plain_track_info do begin

    if (pt_custom=True) or (list_index>4)  // put data in bottom slot.
       then begin
              pt_i:=plain_track_form.plain_track_spacings_listbox.Items.Count-1;     // list index for current custom plain track.
              railen[pt_i]:=rail_length;                                    // custom rail length in inches.
              sleeper_count[pt_i]:=sleepers_per_length;                     // number of sleepers per length.
              for n:=0 to psleep_c do psleep[pt_i,n]:=sleeper_centres[n];   // custom spacings.

              plain_track_form.plain_track_spacings_listbox.Items.Strings[pt_i]:='  '+Trim(pt_spacing_name_str);   // put name in the list.
            end
       else pt_i:=list_index;           // copy data if custom, otherwise use index into existing list.


    rjcode:=rail_joints_code;   // 0=normal, 1=staggered, -1=none (cwr).

    tb_roll_percent:=pt_tb_rolling_percent;
  end;//with

  redraw_pad(True,True);
end;
//______________________________________________________________________________

procedure do_info_colours; // indicate if control template visible and accessible...

begin
  if (current_is_showing=True) and (keep_form.Active=False)
     then begin
            if turnoutx=0                                        // 0.93.a
               then begin
                      info_form.info_gauge_panel.Color:=$00D0D0FF;  // pale red
                      info_form.gauge_label.Font.Color:=clRed;
                    end
               else begin
                      info_form.gauge_label.Font.Color:=clBlack;

                      if ((sx_mod=1) or (both_mod=1)) and ((switch_free=False) or (xing_free=False))
                           then info_form.info_gauge_panel.Color:=$00FFA0FF   // clFuchsia
                           else info_form.info_gauge_panel.Color:=$00FFFFA0;  // clAqua;
                    end;
            end
       else info_form.info_gauge_panel.Color:=$00E0E0E0;  // grey - hidden or pad not active.
end;
//______________________________________________________________________________

function calc_snap_peg_data(code:integer):Tnotch;      // 0.79.a  27-05-06

   // using the control template data, calculate the F7 snap_peg positions.
   // called from copy_keep_to_background (keep_select unit).

   //  !!!  217b   don't call from control template calcs,  interferes with transforms.

var
  dummy_str:string;
  x,y,k:extended;
  temp_y:extended;
  notch_data:Tnotch;
  turn_k,angle:extended;
  curving_rad:extended;
  tanx,shrink_factor:extended;
  modk:extended;

begin

  with RESULT do begin     // defalt inits.
    notch_x:=0;
    notch_y:=0;
    notch_k:=0;
  end;//with

  x:=pegx;    //  default inits (for peg_code 20, peg free) ...
  y:=pegy;
  k:=pegangle;

  normalize_transforms;

  dummy_str:=calc_peg_dims(code,x,y,k);     // code is the peg_code.

  with notch_data do begin
    normalize_transforms;

    docurving(True,True,x,y,notch_x,temp_y,turn_k,curving_rad);   // get notch data from current peg position.
    notch_y:=temp_y*hand_i+y_datum;

      // 217b  gradient correction for radial shrink/enlarge ...

    if ABS(k)<minfp
       then modk:=k
       else begin
              try
                shrink_factor:=(curving_rad-(y-g/2))/curving_rad;
                tanx:=1/TAN(k);
                tanx:=tanx*shrink_factor;
                modk:=ARCTAN(1/tanx);
              except
                modk:=k;
              end;//try
            end;

    angle:=modk+turn_k+kform;     // arm angle (actual on pad including curving and transforms).
    normalize_angle(angle);
    notch_k:=angle*hand_i;
  end;//with

  RESULT:=notch_data;
end;
//______________________________________________________________________________________

procedure snap_current_to_bgnd(notch_pos:Tnotch; facing_to_trailing:boolean);   // 0.79.a

begin
  if facing_to_trailing=False
     then notch_pos.notch_k:=notch_pos.notch_k+Pi;   // facing to facing, or trailing to trailing, so rotate angle 180 degs

  set_current_notch(notch_pos);                      // move notch to bgnd template.

  INC(notch_index);                                  // to next notch rollback slot.
  if notch_index>notch_c then notch_index:=0;
  undo_notch[notch_index]:=notch_pos;                // save notch in this slot...
  pad_form.cycle_notch_menu_entry.Enabled:=True;

  shift_onto_notch(False,False);       // shift control template to it.

  show_and_redraw(True,True);    // in case control template hidden.
end;
//______________________________________________________________________________________

function snap_onto_bgnd_pegs(always:boolean):boolean;   // 0.79.a   27-05-06

   // F7 SHIFT mouse action release -- find a near background template and snap onto it.

var
  n,nc,nb:integer;
  cur_pegs,bgnd_pegs:array[0..4] of Tnotch;
  x1,y1,x2,y2,proximity:extended;
  readout_str:string;

begin
  RESULT:=False;  // init  //233b

  if keeps_list.Count<1 then EXIT;   // no stored templates

  if (always=False) and (pad_form.snap_always_on_background_templates_menu_entry.Checked=False) and ( (f7_snap_allow=False) or (pad_form.snap_on_background_templates_menu_entry.Checked=False) ) then EXIT;  // 218a mod

  proximity:=snap_proximity_limit*inscale;  // snap range

    // control template peg positions for snap checks...

  cur_pegs[0]:=calc_snap_peg_data(0);   // ctrl-0
  cur_pegs[1]:=calc_snap_peg_data(1);   // ctrl-1
  cur_pegs[2]:=calc_snap_peg_data(18);  // ctrl-6
  cur_pegs[3]:=calc_snap_peg_data(11);  // ctrl-9
  cur_pegs[4]:=calc_snap_peg_data(600); // TOLP

  for n:=0 to (keeps_list.Count-1) do begin

    with Ttemplate(keeps_list.Objects[n]) do begin

      if template_info.keep_dims.box_dims1.disable_f7_snap=True then CONTINUE;  // 0.82.a disabled for this template.

      if bg_copied=False then CONTINUE;  // not on background.

      bgnd_pegs[0]:=snap_peg_positions.ctrl_0_pos;
      bgnd_pegs[1]:=snap_peg_positions.ctrl_1_pos;
      bgnd_pegs[2]:=snap_peg_positions.ctrl_6_pos;
      bgnd_pegs[3]:=snap_peg_positions.ctrl_9_pos;
      bgnd_pegs[4]:=snap_peg_positions.ctrl_tolp_pos;

      for nc:=0 to 4 do begin

           // check valid snap position on control template...

        if (half_diamond=True) and (nc<2) then CONTINUE;    // only Ctrl-6, Ctrl-9, TOLP valid for a half-diamond.
        if (plain_track=True) and (nc>1) then CONTINUE;     // only Ctrl-0 and Ctrl-1 valid for plain track.

        if (half_diamond=False) and (plain_track=False) and (nc=1) then CONTINUE;  // Ctrl-1 not valid for a turnout.

        if (retpar_i<>1) and (nc=4) then CONTINUE;  // TOLP valid only for parallel crossing.

        for nb:=0 to 4 do begin

            // check valid snap position on background template...

          if (bgnd_half_diamond=True) and (nb<2) then CONTINUE;    // only Ctrl-6, Ctrl-9, TOLP valid for a half-diamond.
          if (bgnd_plain_track=True) and (nb>1) then CONTINUE;     // only Ctrl-0 and Ctrl-1 valid for plain track.

          if (bgnd_half_diamond=False) and (bgnd_plain_track=False) and (nb=1) then CONTINUE;  // Ctrl-1 not valid for a turnout.

          if (bgnd_retpar=False) and (nb=4) then CONTINUE;  // TOLP valid only for parallel crossing.

            // calc proximity...

          x1:=cur_pegs[nc].notch_x;
          y1:=cur_pegs[nc].notch_y;

          x2:=bgnd_pegs[nb].notch_x;
          y2:=bgnd_pegs[nb].notch_y;

          if (ABS(x1-x2)<proximity) and (ABS(y1-y2)<proximity)
             then begin

                    with pad_form do begin
                                        // move current peg to snapping position...
                      case nc of
                          0: begin                                 // set Ctrl-0
                               peg_on_rail8_menu_entry.Click;      // main-road centre-line (sets peg_code=20, so do first),
                               peg_code:=0;                        // then set it.
                               peg_indicator_panel.Caption:='0';   // show him Ctrl-0
                             end;

                          1: begin                                 // set ctrl-1
                               peg_on_rail8_menu_entry.Click;      // main-road centre-line (sets peg_code=20, so do first),
                               peg_code:=1;                        // then set it.
                               peg_indicator_panel.Caption:='1';   // show him Ctrl-1
                             end;

                          2: begin                                 // set ctrl-6
                               peg_on_rail9_menu_entry.Click;      // turnout-road centre-line (sets peg_code=20, so do first),
                               peg_code:=18;                       // then set it.
                               peg_indicator_panel.Caption:='6';   // show him Ctrl-6
                             end;

                          3: begin                                 // set ctrl-9
                               peg_on_rail8_menu_entry.Click;      // main-road centre-line (sets peg_code=20, so do first),
                               peg_code:=11;                       // then set it.
                               peg_indicator_panel.Caption:='9';   // show him Ctrl-9
                             end;

                          4: begin                                 // set TOLP
                               peg_on_rail9_menu_entry.Click;      // main-road centre-line (sets peg_code=20, so do first),
                               peg_code:=600;                      // then set it.
                               peg_indicator_panel.Caption:='N';   // show him TOLP
                             end;

                        else EXIT; // ???
                      end;//case

                    end;//with

                    gocalc(0,0);  // calc new peg.

                       // rotate bgnd angle for facing-facing or trailing-trailing connections...

                    if (nc=nb)
                    or ( (nc=1) and (nb=2) )
                    or ( (nc=1) and (nb=3) )
                    or ( (nc=1) and (nb=4) )

                    or ( (nc=2) and (nb=1) )
                    or ( (nc=2) and (nb=3) )
                    or ( (nc=2) and (nb=4) )

                    or ( (nc=3) and (nb=1) )
                    or ( (nc=3) and (nb=2) )
                    or ( (nc=3) and (nb=4) )

                    or ( (nc=4) and (nb=1) )
                    or ( (nc=4) and (nb=2) )
                    or ( (nc=4) and (nb=3) )

                       then bgnd_pegs[nb].notch_k:=bgnd_pegs[nb].notch_k+Pi;   // rotate angle 180 degs

                    set_current_notch(bgnd_pegs[nb]);                // move notch to bgnd template.

                    INC(notch_index);                                // to next notch rollback slot.
                    if notch_index>notch_c then notch_index:=0;
                    undo_notch[notch_index]:=bgnd_pegs[nb];          // save notch in this slot...
                    pad_form.cycle_notch_menu_entry.Enabled:=True;

                    shift_onto_notch(False,False);

                              // 0.82.a  update readouts on F7 snap...

                    redraw_pad(False,True);  // immediate redraw, so we can update the readouts

                    readout_str:='X : '+captext(xshift)+' mm      Y : '+captext(yshift)+' mm';
                    caption_add(readout_str);
                    action_update(readout_str);

                    RESULT:=True;  // 233b

                    EXIT;   // found a snap.
                  end;

          end;//next nb
        end;//next nc

    end;//with
  end;//next template
end;
//__________________________________________________________________________________________

procedure match_rolled_lengths(ctrl:integer);

        // 0.82.c  match rolled rail lengths across template boundaries.
        // ctrl=0 for Ctrl-0 , ctrl=1 for Ctrl-1 , boundary on background.
        //
        // control template boundary must be Ctrl-1.

var
  bgnd_template_len_mm,
  bgnd_rail_len_in,
  bgnd_rail_len_mm,
  rolled_out_mm,
  match_percent,
  bgnd_roll_percent:extended;

begin
  try
    if (clicked_keep_index<0) or (clicked_keep_index>(keeps_list.Count-1)) or (keeps_list.Count<1) then EXIT;

    with Ttemplate(keeps_list.Objects[clicked_keep_index]).template_info.keep_dims do begin

      if (box_dims1.turnout_info1.plain_track_flag=False) or (plain_track=False)
         then begin
                alert(6,'      roll  rails  to  match  background  template',
                      '|Templates are not both plain track.'
                     +'||The ROLL RAILS TO MATCH BACKGROUND TEMPLATE function requires that both the control template and the selected background template must be plain track templates.'
                     +'||For approach or exit tracks on a turnout or half-diamond template, these should first be split off as separate plain track templates.'
                     +' Click the TOOLS > MAKE SPLIT > menu items.',
                      '','','','','cancel','',0);
                EXIT;
              end;

      if ABS(scale-box_dims1.proto_info.scale_pi)>minfp
         then begin
                alert(6,'      roll  rails  to  match  background  template',
                      '|Template scales differ.'
                     +'||The ROLL RAILS TO MATCH BACKGROUND TEMPLATE function requires that both the control template and the selected background template must be to the same scale.'
                     +' (The track gauges may differ, where gauge-widening is wanted on one of them.)',
                      '','','','','cancel','',0);
                EXIT;
              end;

                        // on bgnd template...

      bgnd_template_len_mm:=ABS(box_dims1.turnout_info1.turnout_length);                       //  mm overall length.
      bgnd_rail_len_in:=ABS(turnout_info2.plain_track_info.rail_length);             // proto inches
      bgnd_roll_percent:=ABS(turnout_info2.plain_track_info.pt_tb_rolling_percent);  // rolled in percent.

    end;//with

    if ABS(railen[pt_i]-bgnd_rail_len_in)>minfp
       then begin
              alert(6,'      roll  rails  to  match  background  template',
                      '|Template rail length settings differ.'
                     +'||The ROLL RAILS TO MATCH BACKGROUND TEMPLATE function requires that both the control template and the selected background template must be set for the same prototype rail length.'
                     +'||To change the settings, click the REAL > PLAIN TRACK OPTIONS > RAIL LENGTHS AND SLEEPER SPACINGS... menu item.',
                      '','','','','cancel','',0);
              EXIT;
            end;

    while (bgnd_roll_percent>100) do bgnd_roll_percent:=bgnd_roll_percent-100;   // ???

             // calc percent of rail needed to be matched...

    match_percent:=0;  // keep compiler happy.

    case ctrl of

           0: begin   // Ctrl-0 on background
                bgnd_rail_len_mm:=bgnd_rail_len_in*inscale;  // rail length in mm.
                if ABS(bgnd_rail_len_mm)<minfp then EXIT;    //  ??? div 0

                rolled_out_mm:=bgnd_template_len_mm-bgnd_roll_percent*bgnd_rail_len_mm/100;  // length beyond first (rolled in) joint.
                match_percent:=do_fracx(rolled_out_mm/bgnd_rail_len_mm)*100;
              end;

           1: match_percent:=bgnd_roll_percent;  // Ctrl-1 on background

    end;//case

        // set roll percent on control template...

    if (match_percent<minfp) or (match_percent>100) or (ABS(match_percent-100)<minfp) // 0 or 100%
       then tb_roll_percent:=0                                 // full length needed on current.
       else tb_roll_percent:=100-match_percent;                // remainder needed on current.

  finally
    clicked_keep_index:=-1;       // so can popup again.

    rolled_in_sleepered:=True;    // need sleepers present    223a
    show_and_redraw(True,True);   // show current and redraw pad when ready. (allow rollback).
  end;
end;
//____________________________________________________________________________________

procedure tick_not_normal;   // not printing 100% or fit single, change menu tick.

begin
  pad_form.enlarge_reduce_size_menu_entry.Checked:=True; // radio item.
  fit_single_sheet:=False;
  redraw(True);                                         // for the page outlines on the pad.
end;
//_______________________________________________________________________________________

procedure normal_adjust_menu_entry_click;

begin
  cancel_adjusts(False);                                // need a recalc from now_X etc.
  pad_form.normal_adjust_menu_entry.Checked:=True;      // radio item.
  fine_adjust:=1;                                       // modify mouse response factors.
end;
//______________________________________________________________________________

function check_limit(bunch,shear:boolean; var p:TPoint):boolean;    // safety check on one pair of drawing co-ordinates.

    // at present this routine is called for timber numbering, grid lines, and grid and scalebar labels on the pad, and name label locations.
var
  bunch_end, bunch_length, X:integer;       // in pixels for paper bunching calcs 13-11-99.
  n:integer;
  m:extended;

begin
  RESULT:=False;                            // init default.

  if slow_run<>0      // do slow-running delay...
     then begin
            try
              for n:=0 to Round(slow_run*100) do m:=SIN(SQR(Pi));
            except
              EXIT;
            end;//try
          end;

  if paper_bunching=True
     then begin
            bunch_length:=bunch_gap+bunch_jump_i;
            if bunch_length<1 then EXIT;          // div zero or neg!!!

            bunch_end:=bunch_start+bunch_length;

            if p.X>bunch_end
               then begin
                      if bunch=True then p.X:=p.X-bunch_jump_i;
                      if shear=True then p.Y:=p.Y+bunch_shear_i;
                    end
               else begin
                      if p.X>bunch_start
                         then begin
                                X:=p.X;
                                if bunch=True then p.X:=bunch_start+( bunch_gap*(X-bunch_start) div bunch_length );
                                if shear=True then p.Y:=p.Y+    ( bunch_shear_i*(X-bunch_start) div bunch_length );
                              end;               // must do multiply first - these are integers!
                    end;
          end;

  if export_limits=True      // 0.93.a
     then begin
            if p.x>max_export_y then EXIT;
            if p.x<min_export_y then EXIT;

            if p.y>max_export_x then EXIT;
            if p.y<min_export_x then EXIT;
          end;

  if graphics_limits=True      // 0.76.a 25-5-02.
     then begin
            if p.x>max_draw_int then EXIT;
            if p.x<min_draw_int then EXIT;

            if p.y>max_draw_int then EXIT;
            if p.y<min_draw_int then EXIT;
          end;

  RESULT:=True
end;
//_____________________________________________________________________________________

function check_limits(var p1,p2:TPoint):boolean;    // limit checks on both pairs of drawing co-ordinates.

var
  bunch_end, X:integer;       // for paper bunching calcs 13-11-99.
  bunch_length:integer;       // pixels.
  n:integer;
  m:extended;

begin
  RESULT:=False;                            // init default.

  if slow_run<>0    // do slow-running delay...
     then begin
            try
              for n:=0 to Round(slow_run*100) do m:=SIN(SQR(Pi));
            except
              EXIT;
            end;//try
          end;

  if paper_bunching=True
     then begin
            bunch_length:=bunch_gap+bunch_jump_i;
            if bunch_length<1 then EXIT;          // div zero or neg!!!

            bunch_end:=bunch_start+bunch_length;

            if p1.X>bunch_end
               then begin
                      p1.X:=p1.X-bunch_jump_i;
                      p1.Y:=p1.Y+bunch_shear_i;
                    end
               else begin
                      if p1.X>bunch_start
                         then begin
                                X:=p1.X;
                                p1.X:=bunch_start+( bunch_gap*(X-bunch_start) div bunch_length );
                                p1.Y:=p1.Y+   ( bunch_shear_i*(X-bunch_start) div bunch_length );
                              end;             // must do multiply first - these are integers!
                    end;

            if p2.X>bunch_end
               then begin
                      p2.X:=p2.X-bunch_jump_i;
                      p2.Y:=p2.Y+bunch_shear_i;
                    end
               else begin
                      if p2.X>bunch_start
                         then begin
                                X:=p2.X;
                                p2.X:=bunch_start+( bunch_gap*(X-bunch_start) div bunch_length );
                                p2.Y:=p2.Y+   ( bunch_shear_i*(X-bunch_start) div bunch_length );
                              end;             // must do multiply first - these are integers!
                    end;

          end;//if paper_bunching.


  if export_limits=True      // 0.93.a
     then begin
            if p1.x>max_export_y then EXIT;
            if p1.x<min_export_y then EXIT;

            if p1.y>max_export_x then EXIT;
            if p1.y<min_export_x then EXIT;

            if p2.x>max_export_y then EXIT;
            if p2.x<min_export_y then EXIT;

            if p2.y>max_export_x then EXIT;
            if p2.y<min_export_x then EXIT;
          end;

  if graphics_limits=True      // 0.76.a 25-5-02.
     then begin
            if p1.x>max_draw_int then EXIT;
            if p1.x<min_draw_int then EXIT;

            if p1.y>max_draw_int then EXIT;
            if p1.y<min_draw_int then EXIT;

            if p2.x>max_draw_int then EXIT;
            if p2.x<min_draw_int then EXIT;

            if p2.y>max_draw_int then EXIT;
            if p2.y<min_draw_int then EXIT;
          end;

  RESULT:=True
end;
//_____________________________________________________________________________________

function check_draw_dim_l(d:integer):boolean;    // length limit check on a single drawing dimension.

begin
  RESULT:=False;  //init

  if export_limits=True      // 0.93.a
     then begin
            if d>max_export_x then EXIT;
            if d<min_export_x then EXIT;
          end;

  if graphics_limits=True      // !!! Bug Fix 0.78.b 10-12-02.
     then begin
            if d>max_draw_int then EXIT;
            if d<min_draw_int then EXIT;
          end;

  RESULT:=True;
end;
//______________________________________________________________________________

function check_draw_dim_w(d:integer):boolean;    // width limit check on a single drawing dimension.

begin
  RESULT:=False;  //init

  if export_limits=True      // 0.93.a
     then begin
            if d>max_export_y then EXIT;
            if d<min_export_y then EXIT;
          end;

  if graphics_limits=True      // !!! Bug Fix 0.78.b 10-12-02.
     then begin
            if d>max_draw_int then EXIT;
            if d<min_draw_int then EXIT;
          end;

  RESULT:=True;
end;
//______________________________________________________________________________

function SGN(x:extended):extended;

                              //  return sign of x. (+1,  -1,  0)
begin
  RESULT:=0;
  if x=0 then EXIT;
  if x>0 then RESULT:=1.0;
  if x<0 then RESULT:=0-1.0;
end;
//_________________________________________________________________________________________

function SGZ(x:extended):extended;

                              //  return sign of x. (+1,  -1)   (Zero returns +1)
begin
  RESULT:=1;
  if x<0 then RESULT:=0-1.0;
end;
//_________________________________________________________________________________________

function round_str(x:extended; n:integer):string;

                             //  round x to n decimal places and return as a string.
begin
  if n<0 then run_error(56);
  if x=0 then RESULT:='0'
         else begin         //  use Delphi string formatting for up to 4 decimal places
                case n of
                    0: RESULT:=FormatFloat('0',x);
                    1: RESULT:=FormatFloat('0.0',x);       // show at least one decimal place, even if zero...
                    2: begin
                         case zero_supp of
                                     2: RESULT:=FormatFloat('0.##',x);
                                     1: RESULT:=FormatFloat('0.0#',x);
                                     0: RESULT:=FormatFloat('0.00',x);
                                   else run_error(33);
                         end;//case
                       end;

                  else RESULT:=FormatFloat('0.0'+StringOfChar('#',n-1),x)   // 206e

                end;//case
              end;
  if (omit_neg_brackets=False) and (x<0) then RESULT:='[ '+RESULT+' ]';

end;
//______________________________________________________________________________

function round_float(x:extended; n:integer):extended;

        //  round x to n decimal places and return as a float.
var
  mul,mulx:extended;

begin
  if n<0 then run_error(57);
  if x=0 then RESULT:=0
         else begin
                mul:=POWER(10,n);
                mulx:=INT(x*mul+0.5*SGN(x));
                RESULT:=mulx/mul;
              end;
end;
//_________________________________________________________________________________________

function limits(min,max,d:extended; var return_code:integer):extended;  // return d within limits min and max

  // float limits

begin
  return_code:=0;             // default, no change.

  if max<min then d:=0;       // ???

  if d>max
     then begin
            d:=max;
            return_code:=1;   // set to max.
          end;

  if d<min
     then begin
            d:=min;
            return_code:=-1;  // set to min.
          end;

  RESULT:=d
end;
//_____________________________________________________________________________________

function limits_i(min,max,i:integer):integer;    // return i within limits min and max

  // integer limits

begin
if max<min then run_error(50);       // abandon ship

if i>max then i:=max;
if i<min then i:=min;

RESULT:=i
end;
//_______________________________________________________________________________________

function max_i(n,m:integer):integer;                    // return greatest of 2 integers.

begin
  RESULT:=m;                 // init.
  if n>m then RESULT:=n;
end;
//_______________________________________________________________________________________

function max(a,b:extended):extended;              // return greatest of 2 floats.

begin
  RESULT:=a;                 // init.
  if b>a then RESULT:=b;
end;
//________________________________________________________________________________________

function min(a,b:extended):extended;              // return smallest of 2 floats.

begin
  RESULT:=a;                 // init.
  if b<a then RESULT:=b;
end;
//________________________________________________________________________________________

function insert_crlf_str(msg_str:string):string;

var
  i:integer;

begin
  repeat                                 //  replace any | chars in string with a CR/LF.
    i:=Pos('|',msg_str);
    if i>0 then msg_str[i]:=Chr(13);     //  replace with CR.
  until i=0;
  RESULT:=AdjustLineBreaks(msg_str);     //  and add LF.
end;
//____________________________________________________________________________________

function remove_esc_str(msg_str:string):string;

var
  i:integer;

begin
  repeat i:=Pos(Chr(27),msg_str);          // remove any ESC characters.
    if i>0 then Delete(msg_str,i,1);
  until i=0;

  RESULT:=msg_str;
end;
//_______________________________________________________________________________________

function space_lead(s:string):string;      //  replace any leading 0 chars in string with a space.
                                           //  unless followed by decimal point or 0 is only char in string.
var
  i:integer;

begin
  try
    if s='' then EXIT;
    if s='0' then EXIT;
    if Length(s)<2 then EXIT;

    for i:=1 to (Length(s)-1) do if (s[i]='0') and (s[i+1]<>'.') then s[i]:=' '
                                                                 else BREAK;
  finally
    RESULT:=s;
  end;//try
end;
//____________________________________________________________________________________

function captext(d:extended):string;

begin
  if ABS(d)>=max_rad_test then RESULT:=' straight '
                          else begin
                                 RESULT:=FormatFloat('###0.00;"- "###0.00',d);
                                 if (omit_neg_brackets=False) and (d<0) then RESULT:='[ '+RESULT+' ]';
                               end;
end;
//___________________________________________________________________________________

function remove_invalid_str(msg_str:string):string;    // remove characters invalid in filenames.

var
  a,i:integer;

begin  // mods 0.79.a
                                    
  msg_str:=Trim(msg_str);

  for a:=$00 to $1F do repeat i:=Pos(Chr(a),msg_str); if i>0 then Delete(msg_str,i,1); until i=0;

    // allow spaces $20 -- will be replaced with underscores later.

  for a:=$21 to $2C do repeat i:=Pos(Chr(a),msg_str); if i>0 then Delete(msg_str,i,1); until i=0;

    // allow $2D dash/hyphen/minus

  for a:=$2E to $2F do repeat i:=Pos(Chr(a),msg_str); if i>0 then Delete(msg_str,i,1); until i=0;

   // allow $30 - $39    0-9

  for a:=$3A to $40 do repeat i:=Pos(Chr(a),msg_str); if i>0 then Delete(msg_str,i,1); until i=0;

   // allow $41 - $5A    A-Z

  for a:=$5B to $5E do repeat i:=Pos(Chr(a),msg_str); if i>0 then Delete(msg_str,i,1); until i=0;

   // allow $5F  underscore

  repeat i:=Pos(Chr($60),msg_str); if i>0 then Delete(msg_str,i,1); until i=0;

   // allow $61 - $7A   a-z 

  for a:=$7B to $FF do repeat i:=Pos(Chr(a),msg_str); if i>0 then Delete(msg_str,i,1); until i=0;

  RESULT:=lower_case_filename(msg_str);
end;
//________________________________________________________________________________________

function lower_case_filename(name_str:string):string;   // 0.79.a lower case all filenames and change spaces to underscores.

var
  i:integer;

begin
  RESULT:=name_str;  // default init.

  repeat
    i:=Pos(' ',name_str);                        // find next space
    if i>0 then begin
                  Delete(name_str,i,1);
                  Insert('_',name_str,i);
                end;
  until i=0;

  RESULT:=LowerCase(name_str);
end;
//_________________________________________________________________________________________

function invalid_85a_file_name(str:string):boolean;

var
  s:string;

begin
  RESULT:=False;  // default init.

  if str='' then EXIT;

  s:=ExtractFileName(str);

  if Copy(s,1,5)='_85a_'
     then begin
            alert(6,'    invalid  file  name',
                    '|||Sorry,   '+s+'   is not a valid file name.'
                   +'|||The prefix   _85a_   is reserved for use internally by Templot.'
                   +'|||Please choose a different name for your file.',
                    '','','','','','O K      ',0);
            RESULT:=True;
          end;
end;
//_______________________________________________________________________________________

function remove_space_str(msg_str:string):string;    // remove all space characters from string.

var
  i:integer;

begin
  repeat i:=Pos(' ',msg_str);
    if i>0 then Delete(msg_str,i,1);
  until i=0;

  RESULT:=msg_str;
end;
//____________________________________________________________________________________________

function remove_multispace_str(msg_str:string):string;  // reduce multiple spaces in string to singles.

var
  i:integer;

begin
  repeat i:=Pos('  ',msg_str); if i>0 then Delete(msg_str,(i+1),1); until i=0;
  RESULT:=msg_str;
end;

//__________________________________________________________________________________________

// NEW TRANSITION CODING - MANY MORE TERMS IN EXPANSION TO PERMIT 360 DEGREES SWING

// 1-3-98
//____________________________________________________

function fact(a:integer):extended;        // return (a! = a factorial)

var
  n:integer;
  acc:extended;

begin
  if a<1
    then begin
           RESULT:=0;
           EXIT;
         end;
  try
    acc:=1;
    for n:=1 to a do acc:=acc*n;
    RESULT:=acc;
  except
    RESULT:=maxfp;       // overflowed.
  end;//try
end;
//__________________________________________________________________________________________

procedure normalize_angle(var k:extended);

begin
  while k>(Pi*2) do k:=k-(Pi*2);       // range +/- 360 degs.  !!! mod 16-8-00
  while k<(0-Pi*2) do k:=k+(Pi*2);
end;
//_________________________________________________________________________________________

function k_ram_str(k:extended):string;     // get k angle as 1:n RAM string

var
  kn:extended;

begin
  RESULT:='';   // init default

  while k>Pi/2 do k:=k-Pi;                 // force k into range +/- 90 degs..
  while k<(0-Pi/2) do k:=k+Pi;

  if ABS(k)<minfp then EXIT;     // div 0   1:INF.

  kn:=1/TAN(k);

  if kn>0 then RESULT:=' ( 1 in '+round_str(ABS(kn),2)+' RAM )'
          else RESULT:=' ( [ -1 in '+round_str(ABS(kn),2)+' ] RAM )';
end;
//__________________________________________________________________________________________

function calc_transition(rad1,rad2,zonel:extended; var cen1x,cen1y,cen2x,cen2y,apartl,kval:extended):boolean;

         // calc transition constant and rad centres for transition from rad1 to rad2 in length zonel.
         // return True if calc OK, False otherwise.
         // all dimensions from TRANSITION origin.
var
  rad_diff:extended;
  len1,len2:extended;  // lengths along curve to r1, r2 points.
  t1,t2:extended;      // angle turned trhrough at ditto.
  xat1,xat2:extended;  // x dims at ditto (from TRANSITION origin).
  yat1,yat2:extended;  // y ditto.

  apartx,aparty:extended;  // distance centres apart.

  dummy1,dummy2:extended;

begin
  RESULT:=False;            // init.
  rad_diff:=rad1-rad2;
  if ABS(rad_diff)<0.01 then EXIT;    // limit to minimum difference of +/- 0.01 mm.

  try
    len1:=zonel*rad2/rad_diff;         // length along curve from transition origin to r1 point.
    len2:=len1+zonel;

    kval:=len1*rad1;                   // transition constant.

    t1:=len1/2/rad1;                   // angle at r1 point.
    t2:=len2/2/rad2;                   // angle at r2 point.

    if transcalcs(False,True,kval,len1,xat1,yat1,dummy1,dummy2)=False then EXIT;  // get  x,y at r1 point).

    cen1x:=xat1-rad1*SIN(t1);      // x to centre of r1.
    cen1y:=yat1+rad1*COS(t1);      // y to centre of r1.

    if transcalcs(False,True,kval,len2,xat2,yat2,dummy1,dummy2)=False then EXIT;    // ditto for r2 point...

    cen2x:=xat2-rad2*SIN(t2);      // x to centre of r2.
    cen2y:=yat2+rad2*COS(t2);      // y to centre of r2.

    apartx:=cen2x-cen1x;
    aparty:=cen2y-cen1y;

    apartl:=SQRT(SQR(apartx)+SQR(aparty));

    RESULT:=True;
  except
    EXIT;
  end;//try
end;
//___________________________________________________________________________________________

function transcalcs(draw_in_progress,auto_terms:boolean; k, tsn:extended; var xn, yn, tn, rn:extended):boolean; //  Transition equations.

         //  return xn,yn,tn,rn at tsn for transition constant k.

         //  ( tsn is measured along the curve from the transition datum.
         //    xn and yn are co-ordinates to this point on the curve from the transition datum.
         //    tn is the swing to this point from the transition datum. )

         //  returns True if results valid, False otherwise.

         //  if auto_terms is true, overide custom terms if nec.

         //  if draw_in_progress=True, the caller is drawing a template, use 0.01mm accuracy limit for auto terms.
         //  if draw_in_progress=False, this is a set up calc, use 0.000001mm accuracy.

         //  All calculations are carried out in kilometres to reduce the possibility of overflow errors.
var
  nt:integer;                   // power for numerators.
  nb:integer;                   // power for denominators.

  sign_of_xn:extended;           // + or -1 sign of next term in expansion
  sign_of_yn:extended;

  acc_limit:extended;

  k_trans, s_trans, x_trans, y_trans, x_term, y_term :extended;

  i,n:integer;

begin
  n:=0;                        // keep compiler happy.
  try
    if ABS(tsn)<minfp          // transition origin (no div by zero later).
       then begin
              xn:=0;
              yn:=0;
              tn:=0;
              rn:=max_rad;
              RESULT:=True;    // infinite radius.
              EXIT;
            end;

    s_trans:=tsn/1.0E6;    // convert mm to kilometres.
    y_trans:=0;            // initialise for summing terms...
    x_trans:=s_trans;
    k_trans:=k/1.0E12;     // k units are length^2.

    sign_of_xn:=-1.0;
    sign_of_yn:=1.0;

    if (trans_auto=False) and (auto_terms=False)      // use custom number of terms
       then begin
              for n:=1 to trans_terms do    // number of terms in expansion.
                begin
                  nt:=n*4;
                  nb:=n*2;
                                            // calculate terms...

                  x_term:=POWER(s_trans,(nt+1))/(nt+1)/POWER(2,nb)/fact(nb)/POWER(k_trans,nb);

                  y_term:=POWER(s_trans,(nt-1))/(nt-1)/POWER(2,(nb-1))/fact(nb-1)/POWER(k_trans,(nb-1));

                  x_trans:=x_trans+(sign_of_xn*x_term);    // add next term, swapping signs.
                  y_trans:=y_trans+(sign_of_yn*y_term);

                  sign_of_xn:=0-sign_of_xn;                // and swap the signs for the next term.
                  sign_of_yn:=0-sign_of_yn;
                end;//for
              n:=trans_terms;     // keep compiler happy.
            end
       else begin
                     // use less accuracy if for drawing or mouse action in progress..

              if (draw_in_progress=True) or (mouse_modify<>-1)
                 then acc_limit:=1.0E-8                         // in km = 0.01mm/1E6
                 else acc_limit:=1.0E-12;                       // in km = 0.000001mm/1E6

              n:=0;
              repeat
                Inc(n);
                nt:=n*4;
                nb:=n*2;          // calculate terms...

                x_term:=POWER(s_trans,(nt+1))/(nt+1)/POWER(2,nb)/fact(nb)/POWER(k_trans,nb);

                y_term:=POWER(s_trans,(nt-1))/(nt-1)/POWER(2,(nb-1))/fact(nb-1)/POWER(k_trans,(nb-1));

                x_trans:=x_trans+(sign_of_xn*x_term);    // add next term, swapping signs.
                y_trans:=y_trans+(sign_of_yn*y_term);

                sign_of_xn:=0-sign_of_xn;                // and swap the signs for the next term.
                sign_of_yn:=0-sign_of_yn;

              until (ABS(x_term)<acc_limit) and (ABS(y_term)<acc_limit);   //  keep going until terms less than accuracy limit.
            end;

    xn:=x_trans*1.0E6;               // set outputs
    yn:=y_trans*1.0E6;

    tn:=SQR(tsn)/2/k;                // angle turned through.
    rn:=k/tsn;                       // and the radius.

    RESULT:=True;
  except
    RESULT:=False;
    reset_trans;

    cancel_adjusts(False); // 0.93.a

    repeat                             // reset transition defaults.
      i:= alert(0,'    transition  error',
               '||The calculations for this transition have exceeded the capabilities of your system,'
              +' and your transition curve has been cancelled.'
              +'||Re-try using a less exotic transition.'
              +'||( It may be necessary to change a radius or length dimension by only a few millimetres.)',
               '','','','?  help','','O K',4);
      if i=4 then if alert_help(0,'If you are using the normal AUTO TERMS option, this error has probably been caused'
                                +' by the need for too many terms in the expansion to reach the required level of accuracy.'
                                +' The number of terms being used when the error occurred was '+IntToStr(n)+'.'
                                +'||If this number is more than about 60, or less for older processors, this transition'
                                +' is not really a practical proposition because of the very long re-draw times (during which Templot may'
                                +' appear to have crashed but probably hasn''t - coffee while you wait?).'
                                +'||If you can accept less accuracy, you could try using the CUSTOM TERMS option instead,'
                                +' and setting fewer terms than this. '
                                +'||Do you want to do this now?| ','yes - use custom terms')=1
                     then control_room_form.custom_terms_menu_entry.Click;
    until i<>4;
  end;//try

end;
//_______________________________________________________________________________________

procedure Tmath_form.colour_panelClick(Sender: TObject);

begin
  Color:=get_colour('choose  a  new  colour  for  the  dialog',Color);
end;
//___________________________________________________________________________________________

procedure Tmath_form.size_updownClick(Sender: TObject; Button: TUDBtnType);

begin
  if size_updown.Position>size_updown.Tag                          // ! position goes up, size goes down.
     then ScaleBy(9,10);                                           // scale the form contents down.

  if size_updown.Position<size_updown.Tag
     then ScaleBy(10,9);                                           // scale the form contents up.

  ClientHeight:=VertScrollBar.Range;                               // allow 4 pixel right margin.
  ClientWidth:=HorzScrollBar.Range+4;                              // don't need bottom margin - datestamp label provides this.
  ClientHeight:=VertScrollBar.Range;                               // do this twice, as each affects the other.

  size_updown.Tag:=size_updown.Position;                           // and save for the next click.
end;
//________________________________________________________________________________________

function xy_to_dwg100(pin:Tpex):TPex;     // this function and next prepare x,y data for lists.

  //  apply distortions and re-originations,
  //  set hand, and ensure all inputs are within range for 31-bits.
  //  mirror to 1/100th mm

  //  n.b. h_minint, h_maxint are floats.
  //  = max, min integer values divided by 2 to give 31 bit range - this allows for any arithmetic on the stored values,
  //  e.g. for shift keeps, re-origination in keeps box, etc.
  //  (the only case likely to arise is the rad centre marks for very large radii, e.g when
  //  doing transitions to the straight.)

var
  xscaled,yscaled:extended;
  xconed,yconed:extended;
  xskewed,yskewed:extended;
  dummy:integer;

begin
  xscaled:=pin.x*list_factor_x;                                      // these are all in mm ...
  yscaled:=pin.y*list_factor_y*hand_i;

  xconed:=xscaled + (x_coning_distortion_factor*xscaled*yscaled);
  yconed:=yscaled + (y_coning_distortion_factor*yscaled*xscaled);

  xskewed:=xconed + (x_skewing_distortion_factor*yconed);
  yskewed:=yconed + (y_skewing_distortion_factor*xconed);

  RESULT.x:=limits(h_minint,h_maxint,(xskewed*mirror_x + re_org_x),dummy);  // but now in 1/100ths mm.
  RESULT.y:=limits(h_minint,h_maxint,(yskewed*mirror_y + re_org_y),dummy);
end;
//_________________________________________________________________________________________

function xy_to_list(pin:Tpex):TPoint;     // prepare x,y data for list.
                                          // round off and convert to integer.
var
  pout:Tpex;

begin
  pout:=xy_to_dwg100(pin);                  // call above function to do conversions.
  RESULT.X:=Round(pout.x);
  RESULT.Y:=Round(pout.y);
end;
//_________________________________________________________________________________________

function blank_start(x:extended):extended;   // 17-10-02 0,76.a  blanking mods.

begin
  if x<startx
     then RESULT:=startx
     else RESULT:=x;
end;
//_______________________________________________________________________________________

function extract_tbnumber_str(var tbnum_str:string):string;   // return next timber numbering string from the acummulated string.

var
  str_pos:integer;

begin
  str_pos:=Pos(Chr($1B),tbnum_str);    // find next separator.

  if str_pos<>0
     then begin
            RESULT:=Copy(tbnum_str,1,str_pos-1);  // extract next number string (without the separator).
            Delete(tbnum_str,1,str_pos);          // and remove it (including the separator).
          end
     else RESULT:='';      // no more numbers in the string, or the string got corrupted in some way.
end;
//_________________________________________________________________________________________

function set_font(fname:string; fsize:integer; fstyle:TFontStyles; fcolour:integer):TFont;

begin
  with temp_font do begin
    Name:=fname;
    Size:=fsize;
    Style:=fstyle;
    Color:=fcolour;
  end;//with
  RESULT:=temp_font;
end;
//___________________________________________________________________________________________

procedure Tmath_form.FormCreate(Sender: TObject);

begin
  if Screen.Height<500 then Top:=4;    // move form up the screen for lo-res.
  ClientWidth:=542;
  ClientHeight:=440;
  AutoScroll:=True;

  temp_font:=TFont.Create;      // RESULT font for set_font.
end;
//__________________________________________________________________________________________

function time_now_modified(def:integer):integer;    // modify Delphi float time format to a unique integer code;
                                                    // return default def if conversion problem, or if new code is same as last one generated.
                                                    // (def is intended to be supplied as a random integer).
var
  now_float:extended;
  code:integer;

begin
  try
    now_float:=ABS((Date+Time)*2.0E5); // convert date/time to integer in 1/200000 ths of days (0.432 seconds intervals). ABS in case of millenium bug?
                                       // at mid-day on 3-4-99 this gives now_float = 7,250,700,000
                                       // less maxint currently three times          -2,147,483,647  * 3
                                       //                                         (= -6,442,450,941)
                                       //  RESULT                                  =    808,249,059.
                                       // (result repeats every 2147483647/200000  = 10737 days = 29 years.)

    while now_float>maxint do now_float:=now_float-maxint;
    while now_float<minint do now_float:=now_float-minint;    // minint -ve

    code:=do_truncx(now_float);

                      // check if we have been called twice in quick succession (e.g. roll-back functions), (or wonky date/time function)...

    if code=last_code_generated then RESULT:=def        // date/time has failed to change, so return default.
                                else RESULT:=code;
  except
    RESULT:=def;
    code:=def;
  end;//try

  last_code_generated:=code;     // save this calc for check next time. !!! mod version 0.22 18-10-99 was :=RESULT; (so third quick call generates first code again).
end;
//_________________________________________________________________________________________

               // routines to handle home-made integer arrays...     25-5-99

function intarray_create(max_index:integer; zero:boolean):Pointer;

     // max_index is integers, not bytes.
     // if zero=True, clear the array on creation.
     // if max_index=0, creates one valid data slot.

var
  p:Pointer;
  index:integer;
  pint:^integer;     // pointer to integer.

begin
  if max_index<0 then run_error(196);

  try
    GetMem(p,(max_index+3)*int_size);   // would be +1 for data only.  +3 means extra 8 bytes (if 4-byte integers).
                                        // first 4 bytes in array used to contain the max_index,
                                        // then the data, then 4 spare safety bytes at the end.
    pint:=p;
    pint^:=max_index;      // save the max index value in the first slot.

    if zero=True then for index:=0 to max_index do intarray_set(p,index,0);    // clear the data area.

    RESULT:=p;             // and return the pointer.
  except
    memory_alert;          // tell him what's happened.
    RESULT:=nil;
  end;//try
end;
//_________________________________________________________________________________________

procedure intarray_free(p:Pointer);

begin
  if p=nil then run_error(199);
  try
    FreeMem(p);
  except
    run_error(195);
  end;//try
end;
//_______________________________________________________________________________________

function intarray_max(p:Pointer):integer;

var
  pint:^integer;     // pointer to integer.

begin
  if p=nil then run_error(190);
  pint:=p;
  RESULT:=pint^;     // return the max index for this array (in first slot).
end;
//________________________________________________________________________________________

function intarray_get(p:Pointer; index:integer):integer;   // return value at this index.

var
  address:integer;
  pint:^integer;     // pointer to integer.

begin
  if p=nil then run_error(191);
  pint:=p;
  if (index<0) or (index>pint^)
     then begin
            showmessage('program error.  '+IntToStr(index)+'  '+IntToStr(pint^));  // 244d debug

            run_error(192);     // max index is at first slot or index out of range.
          end;
  address:=Integer(p)+(index+1)*int_size;  // +1 because data starts at the second slot.
  pint:=Pointer(address);
  RESULT:=pint^;                   // return integer data from this address.
end;
//________________________________________________________________________________________

procedure intarray_set(p:Pointer; index:integer; d:integer);   // enter new value d at this index.

var
  address:integer;
  pint:^integer;     // pointer to integer.

begin
  if p=nil then run_error(193);
  pint:=p;
  if (index<0) or (index>pint^) then run_error(194);     // max index is at first slot.

  address:=Integer(p)+(index+1)*int_size;  // +1 because data starts at the second slot.
  pint:=Pointer(address);
  pint^:=d;                                // write integer at this address.
end;
//________________________________________________________________________________________

procedure memory_alert;      // do memory fail message.

begin
    alert(5,'    memory  problem',
            'There is insufficient memory available on your system to meet the current requirements.'
           +'||You could try :'
           +'||1. Closing other applications and/or waiting for any printing to finish.'
           +'|2. Deleting any background maps/picture shapes. This is the most likely cause of the problem.'
           +'|3. Saving and clearing your storage box contents and background drawing.'
           +'|4. Shortening the control template.'
           +'|5. Having fewer background items on the trackpad by wiping finalised templates to become unused templates.'
           +'|6. Simplifying the background items, for example rail gauge-faces only or no timbering (GENERATOR menu items).'
           +'|7. Increasing the calculation step-size (PROGRAM > EXPERT menu on the PROGRAM PANEL menus).'
           +'||Do a background REBUILD for items 6 and 7 to take effect.',
            '','','','','','OK',0);
end;
//________________________________________________________________________________________

procedure Tmath_form.font_buttonClick(Sender: TObject);

begin
  big_label.Font.Assign(get_font('choose  a  new  font  and  text  colour  for  this  window',big_label.Font,True));
end;
//___________________________________________________________________________________________

function rad_str(r:extended; dp:integer):string; // get radius as a string.

begin
  if ABS(r)<(max_rad_test-2) then RESULT:=round_str(r,dp)  // 212a -2 kludge for make transition
                             else RESULT:='straight';
end;
//__________________________________________________________________________________________

procedure dotransform(krot,xrot,yrot:extended; pin:Tpex; var pout:Tpex);

                   //  perform any rotations/transformations.
                   //  enter with rotation angle and centre point krot, xrot, yrot.
                   //  and input point pin.

                   //  result point pout returned.
var
  x,y:extended;

begin
  x:=pin.x-xrot;                             // shift to origin.
  y:=pin.y-yrot;

  pout.x:=x*COS(krot)-y*SIN(krot)+xrot;      // rotate and shift back.
  pout.y:=x*SIN(krot)+y*COS(krot)+yrot;
end;
//______________________________________________________________________________________

               // new geometry routines  15-9-99...

function rad_tanp1_p2(p1,p2:Tpex; tn:extended; var rad,swing:extended):boolean;    // 15-9-99.

   // return radius through 2 given points p1,p2 given the tangent swing angle tn at p1...
   // also return the swing angle for the length of curve.
   // return False if radius is infinity (straight) or zero or swing cannot be calculated.
   // otherwise return True.

var
  x,y,h,cosk:extended;

begin
  RESULT:=False;    // default init.
  x:=p2.x-p1.x;
  y:=p2.y-p1.y;
  h:=x*SIN(tn)-y*COS(tn);
  if ABS(h)<minfp
     then begin
            rad:=SGZ(h)*max_rad;   // "straight"
            swing:=0;
            EXIT;
          end
     else rad:=(SQR(x)+SQR(y))/2/h;

  if ABS(rad)<minfp
     then begin
            swing:=0;
            EXIT;
          end;

  cosk:=(rad-h)/rad;     // cosine of swing along curve.

  if ABS(cosk)>(1-minfp)
     then begin
            swing:=0;
            EXIT;
          end;

  swing:=ARCCOS(cosk);
  RESULT:=True;
end;
//________________________________________________

       //  8th May 2001 v:0.71.a

function calc_geo_radius(rout,xp,yp,kp:extended; var rin,kin,krin,gpx:extended):boolean;

      // for geometrical radius calcs.
      // enter with rout radius in main road centre-line,
      // xp, yp tangent point on turnout road (xp from template datum, yp from main road track centre-line at datum).
      // kp turnout road angle at xp,yp (from template datum).

      // return rin radius of arc tangential to main road and turnout road at xp,yp
      // and angle kin from template datum to tangent point on main road
      // and angle krin total swing on rin between tangent points
      // and length gpx along main road curve from template datum to tangent point (+ve towards the crossing)
      // and gorgx,gorgy geometrical radial centres (!!! from the main-road centre-line).
      // see diagram.

var
  a,b,c,d,e,k,q,z:extended;

begin
  RESULT:=False;     // default init
  try

    if ABS(rout)<max_rad_test{curved}=True        // curved template.
       then begin
              z:=TAN(kp);
              if ABS(z)<minfp then EXIT;

              a:=rout-yp-xp/z;

              z:=SIN(kp);
              if ABS(z)<minfp then EXIT;

              b:=a*z;
              c:=a*COS(kp);
              d:=xp/z;

              e:=c+d;

              z:=2*(e-rout);
              if ABS(z)<minfp then EXIT;

              rin:=(SQR(b)+SQR(e)-SQR(rout))/z;

              q:=e-rin;

              if ABS(b)<minfp then EXIT;

              k:=ARCTAN(q/b);

              kin:=Pi/2-kp-k;

              while kin>(Pi/2) do kin:=kin-Pi;       // needed by experiment.
              while kin<(0-Pi/2) do kin:=kin+Pi;     // now in range -90 to 90

              krin:=kin+kp;

              gpx:=0-kin*rout;    // along the curve.

            end
       else begin      // straight template

              z:=SIN(kp);
              if ABS(z)<minfp then EXIT;

              a:=yp/z;
              b:=a*COS(kp);

              gpx:=xp-b-a;

              z:=TAN(kp/2);
              if ABS(z)<minfp then EXIT;

              rin:=a/z;

              krin:=kp;

              kin:=0;

            end;

    RESULT:=True;
  except
    EXIT;   // f.p. errors.
  end;
end;
//______________________________________________________________________________

procedure check_pad_views;   // 0.91.c  process pad view rollbacks once per second.
                             // this routine runs every 1 second. called from rad_lamp_timer (pad_unit)
var
  i,n:integer;

begin
          // see if view has remained unchanged for 5 seconds...

  with pad_view_now do begin
    if (offset_x=zoom_offsetx) and (offset_y=zoom_offsety) and (width_x=screenx)
       then begin
              if view_delay_count<6 then INC(view_delay_count);  // no need to count beyond 6 seconds.
            end
       else begin                        // update current view..
              offset_x:=zoom_offsetx;
              offset_y:=zoom_offsety;
              width_x:=screenx;

              view_delay_count:=0;   // view changed, restart count.

              panning_form.rollback_panel.Color:=$0066AAFF;   // show view is changing.
            end;
  end;//with

  if view_delay_count<>5 then EXIT;   // do nothing while view is changing, or has previously been updated (count=6).

     // has remained static for 5 seconds.
     // see if current view is in list..

  panning_form.next_view_button.Enabled:=False;      // time's up on allowing reverse action.
  panning_form.rollback_panel.Color:=$00CCEEEE;      // show view is static.

  pad_view_index:=0;   // for rollback -- now at top of list.

  i:=-1;  // init

  for n:=0 to (pad_view_list.Count-1) do begin
    with Tpad_view(pad_view_list.Objects[n]).pad_view_data do begin
      if (offset_x=pad_view_now.offset_x) and (offset_y=pad_view_now.offset_y) and (width_x=pad_view_now.width_x)
         then begin
                i:=n;    // found match in list.
                BREAK;
              end;
    end;//with
  end;//next n

  if i=0 then EXIT;  // current view is at top of list anyway.

                     // put it at the top...

  pad_view_list.InsertObject(0,'null',Tpad_view.Create);            // string not used.
  Tpad_view(pad_view_list.Objects[0]).pad_view_data:=pad_view_now;


  if (i>0) and (i<pad_view_list.Count)  // if in list, remove old entry from list
     then begin
            Tpad_view(pad_view_list.Objects[i]).Free;
            pad_view_list.Delete(i);
          end;

  while pad_view_list.Count>8 do begin     // not more than 8 views wanted in list, so delete the bottom entry...

    Tpad_view(pad_view_list.Objects[(pad_view_list.Count-1)]).Free;
    pad_view_list.Delete(pad_view_list.Count-1);

  end;//while

end;
//______________________________________________________________________________

function store_and_background(click,zero_it:boolean):boolean;  // 0.93.a click=True means direct click by user.

  // return False if he cancels, or zero-length template not stored

var
  i,keeps_count:integer;

begin
  RESULT:=False;      //
  keep_added:=False;  // init.

  if click=True
     then begin
            if check_control_template_is_valid('store')=False then EXIT;  // 0.93.a  zero length
          end
     else begin
            if turnoutx=0 then EXIT;    // 0.93.a  zero length
          end;

  keeps_count:=keeps_list.Count;

  store_unused(False,False);
  copy_or_wipe_background;

         // 0.93.a   ...

  if (click=True) and (classic_templot=True) and (keep_form.briefly_hide_on_store_menu_entry.Checked=True)  // 205e now radio item.  205c
     then begin
            do_hide_current;
            hidden_on_store:=8;         // 205c count down in timer to show it again.
          end;

  if (click=True) and (classic_templot=True) and (keep_form.hide_on_store_menu_entry.Checked=True)  // 205e radio item
     then begin
            do_hide_current;
            hidden_on_store:=0;   // cancel any countdown in progress
          end;

  if (click=True) and (classic_templot=True) and (hide_current_flag=False) and (keep_form.reveal_on_store_menu_entry.Checked=True)  // 206a radio item
     then begin
            stored_xshift:=xshift;
            stored_yshift:=yshift;
            slide_inc:=g/25;
            slide_on_store:=51;    // counts down 51-1 (50)

            xshift:=stored_xshift+(slide_on_store-1)*slide_inc;           // 50 = displace by g*2 before sliding back
            yshift:=stored_yshift+(slide_on_store-1)*slide_inc*hand_i;

            math_form.reveal_timer.Interval:=750;   // 3/4 second wait
            math_form.reveal_timer.Enabled:=True;

          end;

  if (click=True) and (classic_templot=True) and (keep_form.alert_on_store_menu_entry.Checked=True)  // 206c radio item
     then begin

            alert_box.preferences_checkbox.Checked:=False;
            alert_box.preferences_checkbox.Show;

            repeat
              i:=alert(2,'php/123    store  &  background',
                       '`0store &amp; background  -  notes for beginners`9'
                      +'||A copy of the control template has been stored in your `0storage box`3, and displayed as a `0background template`3 on the trackpad as part of your track plan.'
                      +'||You can''t see much of it yet, because the control template is in front of it on the screen. When you move the control template away, the background template will become fully visible.'
                      +'||green_panel_begin tree.gif If you want to see the new background template without moving the control template, press the `0HOME`2 key on the keyboard to hide the control template.'
                      +' Press the `0HOME`2 key again to see the control template again.'
                      +'||You can see a little of the background template because the most recently stored template is always shown with bold timber outlines.green_panel_end'
                      +'|If you don''t want to see this message again, tick the|`0don''t show this message again`1 box at the bottom|of this dialog window.',
                       '','','more  information  and  options','show  storage  box','','continue',3);

              if i=3 then alert_help(0,get_store_beginner_help,'');

              if i=4 then pad_form.view_box_menu_entry.Click;

            until i<>3;

            keep_form.dont_hide_on_store_menu_entry.Checked:=alert_box.preferences_checkbox.Checked;         // 208a change option setting -- radio item
            alert_box.preferences_checkbox.Hide;

          end;

  if (zero_it=True) and (classic_templot=False)  // Quick mode
     then begin
            pad_form.snap_to_zero_menu_entry.Click;                 // 0.93.a invalidate after storing.
            xshift:=zoom_offsetx+(screenx-turnoutx)/2;              // put it on the centre of the pad,
            yshift:=(zoom_offsety+screeny/2.5-y_datum)*hand_i-g/2;  // 0.93.a was /2.0 // and on main centre-line (if straight turnout).
          end;

  if keeps_list.Count>keeps_count then keep_added:=True;   // check if it kept or he cancelled (for the make tools).

  RESULT:=True;
end;
//______________________________________________________________________________

function get_store_beginner_help:string;  // 208a

begin
  RESULT:='php/123    `0store  &amp;  background`9'
       +'||After storing a copy of it, the control template remains unchanged and is ready for you to use again in building up your track plan of background templates.'
       +' The control template is always shown in front of any background templates. It is not itself part of your track plan.'
       +'||If you want the control template to be temporarily hidden when you store a copy of it on the background, there are some option settings on the `0options`1 menu on the `0storage box`3:'
       +'||<IMG SRC="'+exe_str+'internal\hlp\store_bgnd_options.png">'
       +'||To see the storage box, click the `0main > storage box`1 menu item, or press `0CTRL+B`2.';
end;
//______________________________________________________________________________

procedure Tmath_form.reveal_timerTimer(Sender: TObject);   // 206a  slide control template back over stored copy

var
  now_now:double;

begin
  reveal_timer.Enabled:=False;  // delay over, slide it back...

  gocalc(2,8);   // 8 updates pad background bitmap

  reveal_limit_count:=10;  // for red-lamp timer  10 arbitrary

  now_now:=Now;

  repeat
    repeat                         // wait if back here too soon.
      Application.ProcessMessages;
    until Now>(now_now+1E-7);      // = 1E-7*1day = 8.64mS  prevent to rapid slide on fast systems
                                   // 50 steps * 8.64mS = 432mS minimum slide time.
    now_now:=Now;

    if slide_on_store<1 then BREAK;  // mouse click or key down sets zero.

    if reveal_limit_count<1 then BREAK;  // taking too long, so abandon slide and jump back.

    xshift:=stored_xshift+(slide_on_store-1)*slide_inc;
    yshift:=stored_yshift+(slide_on_store-1)*slide_inc*hand_i;
    DEC(slide_on_store);
    gocalc(2,7);           // control template over bgnd bitmap

  until slide_on_store<1;  // not =0 (may have been set to zero by clicks, keys)

  slide_on_store:=0;  // cancel any unwanted decrements after set to zero elsewhere

  xshift:=stored_xshift;   // ensure back in place.
  yshift:=stored_yshift;

  show_and_redraw(True,False);  // and showing there
end;
//______________________________________________________________________________

procedure convert_to_or_from_gaunt(to_gaunt:boolean); // 0.93.a ex 081

var
  dummy:extended;
  old_dp,new_dp:extended;

begin
  if (half_diamond=True) or (plain_track=True) then EXIT;

  startx:=0;     // cancel any blanking.
  gocalc(0,0);

  kform_now:=kform;
  docurving(True,True,pegx,pegy,now_peg_x,now_peg_y,now_peg_k,dummy);    // save current peg data for peg_curve calcs.

  old_dp:=dpx-xorg;   // deflection point from switch front.

  gaunt:=to_gaunt;
  gocalc(0,0);

  new_dp:=dpx-xorg;
  xorg:=xorg+old_dp-new_dp;   // adjust xorg approach length to maintain overall length constant.

  if xorg<0        // approach length can't go negative.
     then begin
            turnoutx:=turnoutx-xorg;          // increase overall length to keep V-crossing and exit track on alignment.
            if spiral=True then os:=os-xorg;
            if slewing=True then slew_s:=slew_s-xorg;
            xorg:=0;
          end;

  if turnoutx>turnoutx_max then turnoutx:=turnoutx_max;
  if xorg>turnoutx then xorg:=turnoutx;

  gocalc(0,0);

  turnout_i:=1;      // length locked at turnoutx.
  gocalc(0,0);

  peg_curve;
end;
//______________________________________________________________________________

procedure trail_check_len_mm(X:integer);    // 0.94.a  check rail diffs

begin
  mouse_diff.len_diff:=(mouse_check_len_mm_now+(X-mouse_diffs_now_X)*diff_dir/fx)/inscale;

  if mouse_diff.len_diff<min_diff then mouse_diff.len_diff:=min_diff;

  set_checkrail_diff(current_diff_code,mouse_diff);
end;
//______________________________________________________________________________

procedure trail_check_flare_mm(X:integer);    // 0.94.a  check rail diffs

begin
  mouse_diff.flr_diff:=(mouse_check_flare_mm_now+(X-mouse_diffs_now_X)*diff_dir/fx/1.5)/inscale; // 1.5 arbitrary finer control

  if mouse_diff.flr_diff<min_diff then mouse_diff.flr_diff:=min_diff;

  if mouse_diff.flr_diff>max_diff then mouse_diff.flr_diff:=max_diff;

  set_checkrail_diff(current_diff_code,mouse_diff);
end;
//______________________________________________________________________________

procedure trail_check_gap_mm(Y:integer);    // 0.94.a  check rail diffs

begin
  mouse_diff.gap_diff:=(mouse_check_gap_mm_now+(Y-mouse_diffs_now_Y)*diff_dir/fy/4);    // 4 arbitrary finer control

  if mouse_diff.gap_diff<min_diff then mouse_diff.gap_diff:=min_diff;    // bend-out can't go negative.

  if mouse_diff.gap_diff>max_diff then mouse_diff.gap_diff:=max_diff;    // or more than 1/2 track gauge

  set_checkrail_diff(current_diff_code,mouse_diff);
end;
//______________________________________________________________________________

function get_arc_centre(p1,p2,p3:Tpex; var arc_centre:Tpex):boolean;

   // return the centre of a circle defined by 3 points on its circumference

var
  x,y,len1,len2:extended;

            ////////////////////////////////////////////////////////////////////

            procedure swap_points(var p1,p2:Tpex);

              // swap p1 and p2

            var
              temp:Tpex;

            begin
              temp:=p1;
              p1:=p2;
              p2:=temp;
            end;
            ////////////////////////////////////////////////////////////////////

            function same_point(p1,p2:Tpex):boolean;

             // returns True if p1 and p2 are coincident

            begin
              RESULT:=( (ABS(p1.x-p2.x)<minfp) AND (ABS(p1.y-p2.y)<minfp) );
            end;
            ////////////////////////////////////////////////////////////////////

begin
  RESULT:=True;  // init

  if (same_point(p1,p2)=True)
  or (same_point(p1,p3)=True)
  or (same_point(p2,p3)=True)
  or ( (ABS(p1.x-p2.x)<minfp) and (ABS(p1.x-p3.x)<minfp) )
     then begin
            arc_centre.x:=0;
            arc_centre.y:=0;
            RESULT:=False;
            EXIT;
          end;

  if (ABS(p1.x-p2.x)<minfp) or (ABS(p1.y-p2.y)<minfp)
     then swap_points(p2,p3);

  if ABS(p2.x-p3.x)<minfp
     then swap_points(p1,p2);

  if ABS(p1.x-p2.x)>minfp
     then len1:=(p2.y-p1.y)/(p2.x-p1.x)
     else len1:=maxfp;

  if ABS(p2.x-p3.x)>minfp
     then len2:=(p3.y-p2.y)/(p3.x-p2.x)
     else len2:=maxfp;

  if (ABS(len1)<minfp) and (ABS(len2)<minfp)
     then RESULT:=False;

  if RESULT=True
     then begin
            x:=(len1*len2*(p1.y-p3.y)+len2*(p1.x+p2.x)-len1*(p2.x+p3.x))/(2*(len2-len1));

            if ABS(len1)>minfp
               then y:=0-(x-(p1.x+p2.x)/2)/len1+(p1.y+p2.y)/2
               else y:=0-(x-(p2.x+p3.x)/2)/len2+(p2.y+p3.y)/2;

               arc_centre.x:=x;
               arc_centre.y:=y;
          end;
end;
//______________________________________________________________________________

function get_arc_radius(p1,p2,p3:Tpex):extended;

    // return the radius of a circle defined by 3 points on its circumference

var
  s,t:extended;
  a,b,c:extended;

begin
  a:=SQRT(SQR(p1.x-p2.x)+SQR(p1.y-p2.y));
  b:=SQRT(SQR(p2.x-p3.x)+SQR(p2.y-p3.y));
  c:=SQRT(SQR(p3.x-p1.x)+SQR(p3.y-p1.y));

  s:=(a+b+c)/2;

  t:=s*(s-a)*(s-b)*(s-c);

  if t<minfp then RESULT:=maxfp
             else RESULT:=(a*b*c)/(4*SQRT(t));
end;
//______________________________________________________________________________

function calculate_turnout_radius(curving_rad:extended; var heel_notch,mid_notch,xing_notch:Tnotch):extended;  // 218a

  // using Tnotch for convenience, no effect on the pegging notch

  // calculate radius through 3 points on turnout curve -- at switch heel, crossing, and mid-way between them (track centre-line, tradius-g/2)

  // curving_rad is used only to find sign of result

  // if curving_rad=0, return positive rad anyway

var
  xx1,yy1:extended;
  xx2,yy2:extended;
  xxmid,yymid:extended;

  turn_rad:extended;

  s:extended;
  a,b,c:extended;

begin
  RESULT:=max_rad;   // init straight

  heel_notch:=get_snap_peg_xy_data(104);           // switch heel
  mid_notch:=get_snap_peg_xy_data(270);            // mid-way

  if xing_calc_i=0
     then xing_notch:=get_snap_peg_xy_data(108)    // regular   curve ends at CESP
     else xing_notch:=get_snap_peg_xy_data(5);     // cuviform or generic   calc ends at TCP

  xx1:=heel_notch.notch_x;
  yy1:=heel_notch.notch_y;

  xxmid:=mid_notch.notch_x;
  yymid:=mid_notch.notch_y;

  xx2:=xing_notch.notch_x;
  yy2:=xing_notch.notch_y;

  // calc the radius from 3 points on arc ...

  try
    a:=SQRT(SQR(xx1-xxmid)+SQR(yy1-yymid));
    b:=SQRT(SQR(xxmid-xx2)+SQR(yymid-yy2));
    c:=SQRT(SQR(xx2-xx1)+SQR(yy2-yy1));
    s:=(a+b+c)/2;

    turn_rad:=ABS( (a*b*c)/(4*SQRT(s*(s-a)*(s-b)*(s-c))) );
  except
    turn_rad:=max_rad;   // straight
  end;//try

  if turn_rad>max_rad then EXIT;

  if turn_rad<(2*g) then EXIT;     // ???   arbitrary

  if curving_rad=0
     then RESULT:=turn_rad
     else RESULT:=turn_rad*SGZ(curved_onto_calc(tradius-g/2,curving_rad));

end;
//______________________________________________________________________________

function calculate_turnout_radius_beyond(curving_rad:extended; var xing_notch,mid_notch,end_notch:Tnotch):extended;  // 218b

     // USE FOR CURVIFORM ONLY

  // using Tnotch for convenience, no effect on the pegging notch

  // calculate radius through 3 points on turnout curve beyond crossing -- at TCP, at TMINP, at TVJP  (track centre-line, tradius-g/2)

  // curving_rad is used only to find sign of result

  // if curving_rad=0, return positive rad anyway

var
  xx1,yy1:extended;
  xx2,yy2:extended;
  xxmid,yymid:extended;

  turn_rad:extended;

  s:extended;
  a,b,c:extended;

begin
  RESULT:=max_rad;   // init straight

  xing_notch:=get_snap_peg_xy_data(5);       // TCP
  mid_notch:=get_snap_peg_xy_data(240);      // TMINP
  end_notch:=get_snap_peg_xy_data(18);       // TVJP

  xx1:=xing_notch.notch_x;
  yy1:=xing_notch.notch_y;

  xxmid:=mid_notch.notch_x;
  yymid:=mid_notch.notch_y;

  xx2:=end_notch.notch_x;
  yy2:=end_notch.notch_y;

  // calc the radius from 3 points on arc ...

  try
    a:=SQRT(SQR(xx1-xxmid)+SQR(yy1-yymid));
    b:=SQRT(SQR(xxmid-xx2)+SQR(yymid-yy2));
    c:=SQRT(SQR(xx2-xx1)+SQR(yy2-yy1));
    s:=(a+b+c)/2;

    turn_rad:=ABS( (a*b*c)/(4*SQRT(s*(s-a)*(s-b)*(s-c))) );
  except
    turn_rad:=max_rad;   // straight
  end;//try

  if turn_rad>max_rad then EXIT;

  if turn_rad<(2*g) then EXIT;     // ???   arbitrary

  if curving_rad=0
     then RESULT:=turn_rad
     else RESULT:=turn_rad*SGZ(curved_onto_calc(tradius-g/2,curving_rad));

end;
//______________________________________________________________________________

function calculate_return_curve_radius(var exit_notch,mid_notch,trp_notch:Tnotch):extended;  // 218d

  // using Tnotch for convenience, no effect on the pegging notch

  // calculate radius through 3 points on return curve -- at TVJP exit, TRP curve end, and mid-way between them (track centre-line, tradius-g/2)

var
  xx1,yy1:extended;
  xx2,yy2:extended;
  xxmid,yymid:extended;

  turn_rad:extended;

  s:extended;
  a,b,c:extended;

begin
  RESULT:=max_rad;   // init straight default

  gocalc(0,0);  // 227e ensure updated trpx if change of spacing

  exit_notch:=get_snap_peg_xy_data(18);     // TVJP
  mid_notch:=get_snap_peg_xy_data(275);     // mid-way
  trp_notch:=get_snap_peg_xy_data(7);       // TRP curve end

  xx1:=exit_notch.notch_x;
  yy1:=exit_notch.notch_y;

  xxmid:=mid_notch.notch_x;
  yymid:=mid_notch.notch_y;

  xx2:=trp_notch.notch_x;
  yy2:=trp_notch.notch_y;

    // calc the radius from 3 points on arc ...

  try
    a:=SQRT(SQR(xx1-xxmid)+SQR(yy1-yymid));
    b:=SQRT(SQR(xxmid-xx2)+SQR(yymid-yy2));
    c:=SQRT(SQR(xx2-xx1)+SQR(yy2-yy1));
    s:=(a+b+c)/2;

    turn_rad:=ABS( (a*b*c)/(4*SQRT(s*(s-a)*(s-b)*(s-c))) );
  except
    turn_rad:=max_rad;   // straight
  end;//try

  if turn_rad>max_rad then EXIT;

  if turn_rad<(2*g) then EXIT;     // ???   arbitrary

  RESULT:=turn_rad;
end;
//______________________________________________________________________________

function tpex_from_tnotch(notch:Tnotch):Tpex;

begin
  RESULT.x:=notch.notch_x;
  RESULT.y:=notch.notch_y;
end;
//______________________________________________________________________________

function get_nearer_pex(p0,p1,p2:Tpex):Tpex;

   // return nearer of p1 or p2 to p0

var
  a,b:extended;

begin
  a:=SQRT(SQR(p1.x-p0.x)+SQR(p1.y-p0.y));
  b:=SQRT(SQR(p2.x-p0.x)+SQR(p2.y-p0.y));

  if a<b then RESULT:=p1
         else RESULT:=p2;
end;
//______________________________________________________________________________

function get_notch_distance(n1,n2:Tnotch):extended;

   // return distance between notches

begin
  RESULT:=SQRT(SQR(n1.notch_x-n2.notch_x)+SQR(n1.notch_y-n2.notch_y));
end;
//______________________________________________________________________________

function get_snap_peg_xy_data(code:integer):Tnotch;

  // angle ignored

var
  dummy_str:string;
  x,y,k:extended;
  temp_y,dummy2,temp_k:extended;

begin
  with RESULT do begin     // defalt inits.
    notch_x:=0;
    notch_y:=0;
    notch_k:=0;   // not used
  end;//with

  x:=pegx;    //  default inits (for peg_code 20, peg free) ...
  y:=pegy;
  k:=pegangle;

  dummy_str:=calc_peg_dims(code,x,y,k);     // code is the peg_code.

  with RESULT do begin
    docurving(True,True,x,y,notch_x,temp_y,temp_k,dummy2);   // get notch data from current peg position.
    notch_y:=temp_y*hand_i+y_datum;
  end;//with

end;
//______________________________________________________________________________

function draw_xing_label(k,xlabel,yms,yts,ymid,xnote,ynote:extended):Tpex;  // 211b mark the crossing labels

var
  p1,p2,p3,p4,pk1,pk2,pk3,pk4,ponpad,pp1,pp2,pp3,pp4:Tpex;

  dummy:extended;

  x_curmod,x_curlabel,y_curmod,y_curlabel,k_curlabel:extended;

begin

  RESULT.x:=0;  // init
  RESULT.y:=0;

  if xlabel<(startx-minfp) then EXIT;    // blanking
  if xlabel>(turnoutx+scale) then EXIT;  // overall length

  p1.x:=xlabel;
  p1.y:=yts;
  p2.x:=xlabel;
  p2.y:=yms;

  docurving(False,True,xlabel,ymid,x_curlabel,y_curlabel,k_curlabel,dummy);    // calc curving for the rotation point.

  x_curmod:=x_curlabel-xlabel;   // shift required to rotation point, to suit the curving..
  y_curmod:=y_curlabel-ymid;

    // long line across template ...

  dotransform(k+k_curlabel,xlabel,ymid,p1,pk1);       // equalising + curving line angle, transform p1 to pk1 (relative to equalising centre).
  dotransform(k+k_curlabel,xlabel,ymid,p2,pk2);       // ditto p2

  pk1.x:=pk1.x+x_curmod;      // shift p1 to final position for the curving.
  pk1.y:=pk1.y+y_curmod;

  dotransform(kform,xform,yform,pk1,ponpad);  // transform to template position on pad..
  pp1.x:=ponpad.x+xshift;
  pp1.y:=ponpad.y+yshift;

  pk2.x:=pk2.x+x_curmod;      // shift p2 to final position for the curving.
  pk2.y:=pk2.y+y_curmod;

  dotransform(kform,xform,yform,pk2,ponpad);     // transform to template position on pad..
  pp2.x:=ponpad.x+xshift;
  pp2.y:=ponpad.y+yshift;

  fill_mark(convert_point(pp1),convert_point(pp2),700,'');  // into marks list.

  RESULT:=pp1;     // in case label note needed (TS side) (half-diamonds)

  if (xnote=0) and (ynote=0) then EXIT;  // note line not wanted

    // short line to note ...

  p3.x:=xlabel;
  p3.y:=yts-ynote;
  p4.x:=xlabel+xnote;
  p4.y:=p3.y;

  dotransform(k+k_curlabel,xlabel,ymid,p3,pk3);       // equalising + curving line angle, transform p3 to pk3 (relative to equalising centre).
  dotransform(k+k_curlabel,xlabel,ymid,p4,pk4);       // ditto p4

  pk3.x:=pk3.x+x_curmod;      // shift p3 to final position for the curving.
  pk3.y:=pk3.y+y_curmod;

  dotransform(kform,xform,yform,pk3,ponpad);  // transform to template position on pad..
  pp3.x:=ponpad.x+xshift;
  pp3.y:=ponpad.y+yshift;

  pk4.x:=pk4.x+x_curmod;      // shift p4 to final position for the curving.
  pk4.y:=pk4.y+y_curmod;

  dotransform(kform,xform,yform,pk4,ponpad);     // transform to template position on pad..
  pp4.x:=ponpad.x+xshift;
  pp4.y:=ponpad.y+yshift;

  fill_mark(convert_point(pp3),convert_point(pp4),700,'');   // into marks list.

  RESULT:=pp4;  // return position for note

end;
//______________________________________________________________________________

function do_show_modal(modal_form:TForm):TModalResult;   // 212a wine bug

begin
  if running_under_wine=True
     then begin
            INC(modal_form_count);

            if modal_form_count>12 then modal_form_count:=12;  // 12 nested modal forms? !!!

            modal_form_array[modal_form_count]:=modal_form;

            RESULT:=modal_form.ShowModal;   // do it under wine

            DEC(modal_form_count);

            if modal_form_count<0 then modal_form_count:=0;    // error? !!!
          end

     else RESULT:=modal_form.ShowModal;  // do it under Windows

end;
//______________________________________________________________________________

procedure show_modal_message(msg:string);                // 212a Wine bug

begin
  showing_message:=True;
  ShowMessage(msg);
  showing_message:=False;
end;
//______________________________________________________________________________

procedure Tmath_form.FormDestroy(Sender: TObject);

begin
  temp_font.Free;
end;
//______________________________________________________________________________

procedure Tmath_form.overwrite_labelClick(Sender: TObject);  // 214a

begin
  form_overwrite_mode:= NOT form_overwrite_mode;
  if form_overwrite_mode=True
     then overwrite_label.Caption:='OVR'
     else overwrite_label.Caption:='INS';
end;
//______________________________________________________________________________

procedure Tmath_form.math_editboxKeyPress(Sender:TObject; var Key:Char);

   // 214a  overwrite mode ...

begin
  if (Sender is TCustomEdit) and (form_overwrite_mode=True)
     then begin
            with TCustomEdit(Sender) do begin
              if (SelLength=0) and (SelStart<Length(Text))
                 then begin
                        case Key of
                          ' '..#126, #128..#255: SelLength:=1;    // select next character
                        end;//case
                      end;
            end;//with
          end;
end;
//______________________________________________________________________________

procedure companion_help(topic_url_str:string);

begin
  go_to_url('https://85a.uk/templot/companion/'+topic_url_str);
end;
//______________________________________________________________________________

procedure default_template;      // a short straight plain track template     223c

begin
  pad_form.convert_to_plain_track_menu_entry.Click;
  gocalc(0,0);

  pad_form.disable_slewing_menu_entry.Click;
  gocalc(0,0);

  pad_form.straight_template_menu_entry.Click;
  gocalc(0,0);

  peg_code:=0;
  gocalc(0,0);

  turnoutx:=66*scale;               // 1 chain
  xorg:=turnoutx;                   // and ensure xorg matches for plain track.
  turnout_i:=1;                     // length locked at new turnoutx.
  gocalc(0,0);

  normalize_transforms;
  kform:=0;                         // horizontal on screen
  gocalc(0,0);

  redraw(True);
end;
//______________________________________________________________________________

function count_character_in_string(ch:char; str:string):integer;   // 226c

var
  n:integer;

begin
  RESULT:=0;

  for n:=1 to Length(str) do begin
    if str[n]=ch then INC(RESULT);
  end;//next
end;
//______________________________________________________________________________

function do_truncx(x:extended):integer;

begin
  try                  // 226d Delphi bug? mysterious F.P. exceptions with valid data
    RESULT:=TRUNC(x);
  except
    RESULT:=0;
  end;//try
end;
//______________________________________________________________________________

function do_fracx(x:extended):extended;

begin
  try                  // 226d Delphi bug? mysterious F.P. exceptions with valid data
    RESULT:=FRAC(x);
  except
    RESULT:=0;
  end;//try
end;
//______________________________________________________________________________

function hold_the_control:Ttemplate_info;   // 227a

var
  hold_ti:Ttemplate_info;

begin
  hold_ti.keep_name_str:=current_name_str;
  hold_ti.keep_memo_str:=current_memo_str;

  fill_kd(hold_ti);       // temp save the control template

  RESULT:=hold_ti;
end;
//______________________________________________________________________________

procedure unhold_the_control(var hold_ti:Ttemplate_info);   // 227a

begin
  copy_keep(hold_ti); // retrieve saved control

  current_name_str:=hold_ti.keep_name_str;
  current_memo_str:=hold_ti.keep_memo_str;

  info_form.ref_name_label.Caption:=current_name_str;

  gocalc(0,0);  // restore all calculated data  227b
end;
//______________________________________________________________________________

procedure copy_shove_array(clear_array:boolean; var from_array,to_array:Tshoved_timbers);

var
  f:integer;

begin
  SetLength(to_array,Length(from_array));

  if Length(from_array)>0 then for f:=0 to Length(from_array)-1 do to_array[f]:=from_array[f];

  if clear_array=True then SetLength(from_array,0);
end;
//______________________________________________________________________________

procedure copy_symbols_array(clear_array:boolean; var from_array,to_array:Tsymbols);    // 227a

var
  f:integer;

begin
  SetLength(to_array,Length(from_array));

  if Length(from_array)>0 then for f:=0 to Length(from_array)-1 do to_array[f]:=from_array[f];

  if clear_array=True then SetLength(from_array,0);
end;
//______________________________________________________________________________

procedure copy_timbers_array(clear_array:boolean; var from_array,to_array:Ttimber_counts);    // 227a

var
  f:integer;

begin
  SetLength(to_array,Length(from_array));

  if Length(from_array)>0 then for f:=0 to Length(from_array)-1 do to_array[f]:=from_array[f];

  if clear_array=True then SetLength(from_array,0);
end;
//______________________________________________________________________________

procedure copy_jaws_array(clear_array:boolean; var from_array,to_array:Tjaws);    // 237c

var
  f:integer;

begin
  SetLength(to_array,Length(from_array));

  if Length(from_array)>0 then for f:=0 to Length(from_array)-1 do to_array[f]:=from_array[f];

  if clear_array=True then SetLength(from_array,0);
end;
//______________________________________________________________________________

procedure copy_seats_array(clear_array:boolean; var from_array,to_array:Tseats);    // 237c

var
  f:integer;

begin
  SetLength(to_array,Length(from_array));

  if Length(from_array)>0 then for f:=0 to Length(from_array)-1 do to_array[f]:=from_array[f];

  if clear_array=True then SetLength(from_array,0);
end;
//______________________________________________________________________________

procedure copy_keys_array(clear_array:boolean; var from_array,to_array:Tkeys);    // 237c

var
  f:integer;

begin
  SetLength(to_array,Length(from_array));

  if Length(from_array)>0 then for f:=0 to Length(from_array)-1 do to_array[f]:=from_array[f];

  if clear_array=True then SetLength(from_array,0);
end;
//______________________________________________________________________________

procedure copy_jmods_array(clear_array:boolean; var from_array,to_array:Tjmods);    // 237c

var
  f:integer;

begin
  SetLength(to_array,Length(from_array));

  if Length(from_array)>0 then for f:=0 to Length(from_array)-1 do to_array[f]:=from_array[f];

  if clear_array=True then SetLength(from_array,0);
end;
//______________________________________________________________________________

procedure do_draw_template_direct(x1,y1,x2,y2:extended);   // 233b  create new control template

var
  new_notch_data:Tnotch;
  k,len,xend,yend,dummy1,dummy2:extended;
  snapped:boolean;

begin

     // get drawn line angle ...

  if ABS(x2-x1)<minfp
     then begin
            if ABS(y2-y1)<minfp
               then begin
                      show_modal_message('error: no drawn line'+#13+#13+'Drag a line with the mouse to create a template.');
                      EXIT;
                    end;

            if y2>y1 then k:=Pi/2        // vertical line
                     else k:=0-Pi/2;
          end
     else begin
            if ABS(y2-y1)<minfp
               then begin
                      if x2>x1 then k:=0     // horizontal line
                               else k:=Pi;
                    end
               else k:=ARCTAN((y2-y1)/(x2-x1));
          end;

          // drawn line length ...
  try
    len:=SQRT(SQR(x2-x1)+SQR(y2-y1));     // new template length
  except
    len:=66*scale;     // set 1 chain
  end;//try

  if len<(g/3)         // arbitrary for mouse fumbles
     then begin
            show_modal_message('error: drawn line too short'+#13+#13+'Drag a line with the mouse to create a template.');
            EXIT;
          end;

  hidden_on_store:=0;  // hide flashing changes to control template ...
  do_hide_current;

  pad_form.notch_unlinked_from_current_menu_entry.Click;  // radio item - cancel any moving the notch in mouse actions

  with new_notch_data do begin
    notch_x:=x1;
    notch_y:=y1;
    notch_k:=k;
  end;//with

  new_notch(new_notch_data,False);      // move notch, no link group

  store_and_background(False,False);     // first keep it and copy to background.
  if keep_added=False then EXIT;         // he cancelled.

  if slewing=True then pad_form.disable_slewing_menu_entry.Click;

  pt_convert(False);       // to plain track     don't show control template

  mint_new_current(0,False);        // don't show control template

  xorg:=len;               // set it
  turnoutx:=xorg;

  if turnoutx>turnoutx_max
     then begin
            turnoutx:=turnoutx_max;
            xorg:=turnoutx;
          end;

  nomrad:=max_rad;  // straight
  spiral:=False;
  slewing:=False;
            
  shift_onto_notch(False,False);

  docurving(True,True,turnoutx,g/2,xend,yend,dummy1,dummy2);  // get position of far end

  if (ABS(x2-xend)>0.01) and (ABS(y2-(yend*hand_i+y_datum))>0.01) then pad_form.rotate_current_180_menu_entry.Click;    // to match far end      0.01 proximity arbitrary

  gocalc(0,0);

  snapped:=snap_onto_bgnd_pegs(True); // (True) = always

  if (notchx<zoom_offsetx)
  or (notchx>(zoom_offsetx+screenx))
  or (notchy<zoom_offsety)
  or (notchy>(zoom_offsety+screeny))
     then pad_form.pad_on_notch_menu_entry.Click;       // ensure it is showing

  if snapped=False then pad_form.reset_notch_menu_entry.Click;   // show if there was no snap by resetting notch

  show_and_redraw(True,True);
end;
//______________________________________________________________________________

initialization

  int_size:=SizeOf(integer);     // only need to call this once.

  SetLength(current_jaw_options_set,50);   // 244a curently 31 jaw types 0..49
//______________________________________________________________________________

end.

