summaryrefslogtreecommitdiff
path: root/app/bin
diff options
context:
space:
mode:
Diffstat (limited to 'app/bin')
-rw-r--r--app/bin/CMakeLists.txt30
-rw-r--r--app/bin/ChangeLog495
-rw-r--r--app/bin/acclkeys.h3
-rw-r--r--app/bin/appdefaults.c435
-rw-r--r--app/bin/bdf2xtp.c10
-rw-r--r--app/bin/bitmaps/bezier.xpm23
-rw-r--r--app/bin/bitmaps/dbezier.xpm22
-rw-r--r--app/bin/bitmaps/ecornu.xpm23
-rw-r--r--app/bin/bitmaps/eltsharp.xpm21
-rw-r--r--app/bin/bitmaps/helix.xpm21
-rw-r--r--app/bin/bitmaps/map.xpm22
-rw-r--r--app/bin/bitmaps/pan.xpm22
-rw-r--r--app/bin/cbezier.c1153
-rw-r--r--app/bin/cbezier.h53
-rw-r--r--app/bin/cblock.c31
-rw-r--r--app/bin/ccontrol.c25
-rw-r--r--app/bin/ccornu.c1240
-rw-r--r--app/bin/ccornu.h23
-rw-r--r--app/bin/ccurve.c241
-rw-r--r--app/bin/ccurve.h18
-rw-r--r--app/bin/cdraw.c204
-rw-r--r--app/bin/celev.c18
-rw-r--r--app/bin/cgroup.c52
-rw-r--r--app/bin/chndldto.c18
-rw-r--r--app/bin/chotbar.c47
-rw-r--r--app/bin/cjoin.c105
-rw-r--r--app/bin/cjoin.h15
-rw-r--r--app/bin/cmisc.c774
-rw-r--r--app/bin/cmodify.c153
-rw-r--r--app/bin/cnote.c638
-rw-r--r--app/bin/common.h23
-rw-r--r--app/bin/compound.c253
-rw-r--r--app/bin/compound.h10
-rw-r--r--app/bin/cparalle.c15
-rw-r--r--app/bin/cprint.c101
-rw-r--r--app/bin/cprofile.c18
-rw-r--r--app/bin/cpull.c102
-rw-r--r--app/bin/cruler.c7
-rw-r--r--app/bin/cselect.c261
-rw-r--r--app/bin/cselect.h12
-rw-r--r--app/bin/csensor.c27
-rw-r--r--app/bin/csignal.c38
-rw-r--r--app/bin/csnap.c16
-rw-r--r--app/bin/csplit.c22
-rw-r--r--app/bin/cstraigh.c80
-rw-r--r--app/bin/cstraigh.h12
-rw-r--r--app/bin/cstruct.c28
-rw-r--r--app/bin/cswitchmotor.c23
-rw-r--r--app/bin/ctext.c94
-rw-r--r--app/bin/ctodesgn.c20
-rw-r--r--app/bin/ctrain.c4680
-rw-r--r--app/bin/ctrain.h10
-rw-r--r--app/bin/cturnout.c168
-rw-r--r--app/bin/cturntbl.c64
-rw-r--r--app/bin/cundo.c17
-rw-r--r--app/bin/cundo.h12
-rw-r--r--app/bin/custom.c16
-rw-r--r--app/bin/custom.h18
-rw-r--r--app/bin/dbench.c9
-rw-r--r--app/bin/dbitmap.c29
-rw-r--r--app/bin/dcar.c45
-rw-r--r--app/bin/dcmpnd.c14
-rw-r--r--app/bin/dcontmgm.c13
-rw-r--r--app/bin/dcustmgm.c25
-rw-r--r--app/bin/dease.c110
-rw-r--r--app/bin/denum.c31
-rw-r--r--app/bin/dlayer.c1538
-rw-r--r--app/bin/doption.c221
-rw-r--r--app/bin/dpricels.c21
-rw-r--r--app/bin/dprmfile.c65
-rw-r--r--app/bin/draw.c684
-rw-r--r--app/bin/draw.h14
-rw-r--r--app/bin/drawgeom.c306
-rw-r--r--app/bin/drawgeom.h20
-rw-r--r--app/bin/dxfformat.c2
-rw-r--r--app/bin/dxfformat.h24
-rw-r--r--app/bin/dxfoutput.c16
-rw-r--r--app/bin/elev.c15
-rw-r--r--app/bin/fileio.c229
-rw-r--r--app/bin/fileio.h19
-rw-r--r--app/bin/i18n.c7
-rw-r--r--app/bin/layout.c375
-rw-r--r--app/bin/layout.h56
-rw-r--r--app/bin/lprintf.c16
-rw-r--r--app/bin/macro.c123
-rw-r--r--app/bin/misc.c375
-rw-r--r--app/bin/misc.h30
-rw-r--r--app/bin/misc2.c98
-rw-r--r--app/bin/misc2.h30
-rw-r--r--app/bin/param.c391
-rw-r--r--app/bin/param.h13
-rw-r--r--app/bin/paths.c220
-rw-r--r--app/bin/paths.h32
-rw-r--r--app/bin/shrtpath.c4
-rw-r--r--app/bin/shrtpath.h10
-rw-r--r--app/bin/smalldlg.c32
-rw-r--r--app/bin/smalldlg.h4
-rw-r--r--app/bin/tbezier.c1629
-rw-r--r--app/bin/tbezier.h57
-rw-r--r--app/bin/tcornu.c1321
-rw-r--r--app/bin/tcornu.h66
-rw-r--r--app/bin/tcurve.c170
-rw-r--r--app/bin/tease.c36
-rw-r--r--app/bin/track.c270
-rw-r--r--app/bin/track.h161
-rw-r--r--app/bin/trackx.h9
-rw-r--r--app/bin/trkseg.c599
-rw-r--r--app/bin/tstraigh.c55
-rw-r--r--app/bin/unittest/CMakeLists.txt21
-rw-r--r--app/bin/unittest/defaultstest.c110
-rw-r--r--app/bin/unittest/pathstest.c121
-rw-r--r--app/bin/uthash.h960
-rw-r--r--app/bin/utility.c16
-rw-r--r--app/bin/utility.h8
-rw-r--r--app/bin/version.h7
115 files changed, 16642 insertions, 6322 deletions
diff --git a/app/bin/CMakeLists.txt b/app/bin/CMakeLists.txt
index ee0886d..74b1bc8 100644
--- a/app/bin/CMakeLists.txt
+++ b/app/bin/CMakeLists.txt
@@ -42,18 +42,6 @@ SET(LIN_SOURCES
GET_TARGET_PROPERTY(genhelp_EXE genhelp LOCATION)
-IF(XTRKCAD_USE_GETTEXT)
- SET(GENHELP_OPTS "-bhi")
- #
- # Find the GnuWin32 installation directory, the gettext include should be located in subdir include
- #
- IF(WIN32)
- ADD_DEFINITIONS(-DUSE_SIMPLE_GETTEXT )
- ENDIF(WIN32)
-ELSE(XTRKCAD_USE_GETTEXT)
- SET(GENHELP_OPTS "-bh")
-ENDIF(XTRKCAD_USE_GETTEXT)
-
ADD_CUSTOM_COMMAND(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/bllnhlp.c
DEPENDS genhelp ${help_SOURCE_DIR}/genhelp.in
@@ -62,8 +50,11 @@ ADD_CUSTOM_COMMAND(
SET(SOURCES
${LIN_SOURCES}
+ appdefaults.c
bllnhlp.c
+ cbezier.c
cblock.c
+ ccornu.c
ccurve.c
ccontrol.c
cdraw.c
@@ -115,12 +106,16 @@ SET(SOURCES
elev.c
fileio.c
i18n.c
+ layout.c
lprintf.c
macro.c
misc2.c
param.c
+ paths.c
shrtpath.c
smalldlg.c
+ tbezier.c
+ tcornu.c
tcurve.c
tease.c
track.c
@@ -134,12 +129,6 @@ INCLUDE_DIRECTORIES(${XTrkCAD_BINARY_DIR})
INCLUDE_DIRECTORIES(${help_BINARY_DIR})
INCLUDE_DIRECTORIES(${wlib_SOURCE_DIR}/include)
-# find libintl.h and use it
-find_path ( INTL_PATH libintl.h )
-if(INTL_PATH)
- INCLUDE_DIRECTORIES(${INTL_PATH})
-endif(INTL_PATH)
-
LINK_DIRECTORIES(${GTK_LIBRARY_DIRS})
LINK_DIRECTORIES(${GTK_WEBKIT_LIBRARY_DIRS})
@@ -149,12 +138,13 @@ ADD_LIBRARY(xtrkcad-lib ${SOURCES})
ADD_DEPENDENCIES(xtrkcad-lib Help)
ADD_EXECUTABLE(xtrkcad WIN32
- misc.c
+ misc.c
xtrkcad.rc
)
TARGET_LINK_LIBRARIES(xtrkcad xtrkcad-lib)
TARGET_LINK_LIBRARIES(xtrkcad xtrkcad-wlib)
-target_link_libraries(xtrkcad dynstring)
+TARGET_LINK_LIBRARIES(xtrkcad xtrkcad-cornu)
+TARGET_LINK_LIBRARIES(xtrkcad dynstring)
ADD_EXECUTABLE(mkturnout
${LIN_SOURCES}
diff --git a/app/bin/ChangeLog b/app/bin/ChangeLog
deleted file mode 100644
index 034812e..0000000
--- a/app/bin/ChangeLog
+++ /dev/null
@@ -1,495 +0,0 @@
-Apr 28, 2010
- FIX: Daniel Spagnol
- i18n.c, misc.c: replaced hard-coded XTRKCAD_LOCALE_DIR path with
- 'locale' based on application library directory (XTRKCAD_LOCALE_DIR is
- defined at makefiles generation time and does not reflect the place
- where the application is actually installed)
-
-Jan 01, 2010
- FIX: Martin Fischer
- custom.c, custom.h: fix compile warnings
-
-Dec 30, 2009
- FIX: Martin Fischer
- misc.c: make load last layout option work
-
-Dec 29, 2009
- FIX: Martin Fischer
- denum.c: remove signed / unsigned mismatch
-
-Dec 19, 2009
- FIX: Robert Heller
- cswitchmotor.c: Patch to fix Visual C++ compile error
-
-Dec 12, 2009
- FIX: Martin Fischer
- draw.c, custom.c: refactoring, move some functionality from the lowlevel
- library to the more appropriate core application modules
-
-Oct 14, 2009
- ENH: Daniel Spagnol
- chotbar.c: undone Oct 03, 2009 changes due to gtk+-2.18 fixed it for us.
- Actually gtk+-2.18 fixed all surface drawing performance issues for
- quartz.
-
-Oct 09, 2009
- FIX: Daniel Spagnol
- denum.c: application crash due to a double value used as a "%*" sprintf
- argument. scenario is "Manage" -> "Parts List..." -> "Price" (checkbox).
- denum.c: added a character counter function for utf-8 strings
-
-Oct 04, 2009
- FIX: Martin Fischer
- misc2.c: minimum radius is correctly changed
-
-Oct 03, 2009
- FIX: Daniel Spagnol
- chotbar.c: hotbar redraw too slow under gtk-quartz
-
-Sep 21, 2009
- ENH: Martin Fischer <m_fischer@users.sourceforge.net>
- custom.c, misc.c, param.c, param.h, smalldlg.c smalldlg.h:
- New 'About' dialog
-
-Sep 16, 2009
- FIX: Martin Fischer <m_fischer@users.sourceforge.net>
- cblock.c, cswitchmotor.c: remove some unused locals
-
-Aug 16, 2009
- FIX: Martin Fischer <m_fischer@users.sourceforge.net>
- CMakeLists.txt cprint.c denum.c i18n.c i18n.h misc.c
- Improve internationalization support, use simple gettext on Win32
-
-Aug 16, 2009
- FIX: Martin Fischer <m_fischer@users.sourceforge.net>
- custom.c cturnout.c: Code cleanup
-
-Jul 30, 2009
- FIX: Martin Fischer <m_fischer@users.sourceforge.net>
- dcustmgm.c: set locale when exporting to parameter
-
-Jul 24, 2009
- ENH: Martin Fischer <m_fischer@users.sourceforge.net>
- misc.c: add command line option for configuration
- file selection
-
-Jul 10, 2009
- ENH: Martin Fischer <m_fischer@users.sourceforge.net>
- misc.c: use getopt() for access to command line arguments
-
-Jul 09, 2009
- FIX: Martin Fischer <m_fischer@users.sourceforge.net>
- custom.c, misc.c, denum.c, doption.c: remove some
- obsolete flags
-
-Jul 08, 2009
- FIX: Martin Fischer <m_fischer@users.sourceforge.net>
- cblock.c, cswitchmotor.c: make compile under MSVC
-
-Jul 08, 2009
- ENH: Robert Heller
- cblock.c, cswitchmotor.c: new functionality for layout
- control: blocks and switchmotors
-
-Version 4.0.3a
-==============
-
-Jul 05, 2009
- FIX: Martin Fischer <m_fischer@users.sourceforge.net>
- track.c: Bug fix #2816663: Block gaps are not printed
-
-Jul 01, 2009
- FIX: Martin Fischer <m_fischer@users.sourceforge.net>
- CMakeList.txt: remove dependency from unneeded cmisc2.c
-
-Jun 26, 2009
- FIX: Martin Fischer <m_fischer@users.sourceforge.net>
- custom.c: correct handling of export file extensions
-
-Jun 20, 2009
- FIX: Martin Fischer <m_fischer@users.sourceforge.net>
- ctodesgn.c: convert roadbed width as necessary
- (Robert Myers)
-
-Jun 15, 2009
- FIX: Martin Fischer <m_fischer@users.sourceforge.net>
- tcurve.c, drawgeom.c: fix variable initialization
- problems.
-
-Jun 14, 2009
- FIX: Martin Fischer <m_fischer@users.sourceforge.net>
- macro.c: make demos work again with new dialogs
-
-Jun 13, 2009
- FIX: Martin Fischer <m_fischer@users.sourceforge.net>
- dlayer.c: "changed" state of layout is updated with
- layer changes. (DonDASA)
-
-Version 4.0.3
-=============
-
-Jun 10, 2009
- FIX: Martin Fischer <m_fischer@users.sourceforge.net>
- ctodesgn.c: remove unneeded local variables
-
-Jun 08, 2009
- FIX: Martin Fischer <m_fischer@users.sourceforge.net>
- draw.c: no tooltip for the main drawing area
-
-Jun 06, 2009
- FIX: Martin Fischer <m_fischer@users.sourceforge.net>
- draw.c: fix compiler warning
-
-May 26, 2009
- ENH: Martin Fischer <m_fischer@users.sourceforge.net>
- ctrain.c: update icons
-
-May 25, 2009
- ENH: Martin Fischer <m_fischer@users.sourceforge.net>
- ctrain.c: change default for train running to 'Go'
- beautify throttle slider
-
-May 25, 2009
- ENH: Martin Fischer <m_fischer@users.sourceforge.net>
- cturnout.c, track.c, track.h,utility.c, cparalle.c
- parallel command also works for straight pieces of
- sectional track
-
-May 15, 2009
- ENH: Martin Fischer <m_fischer@users.sourceforge.net>
- macro.c, misc.c: more message boxes with icon
-
-May 08, 2009
- ENH: Martin Fischer <m_fischer@users.sourceforge.net>
- fileio.c, misc.c: use new message box with icon
-
-
-Oct 11, 2008
- FIX: Martin Fischer <m_fischer@users.sourceforge.net>
- draw.h: fixed prototype for DoZoom as suggested by
- Stefan Gruenendahl
-
-Sep 05, 2008
- ENH: Martin Fischer <m_fischer@users.sourceforge.net>
- misc.c, cselect.c, track.c: create full partlist
- when no track is selected
-
-Sep 01, 2008
- ENH: Martin Fischer <m_fischer@users.sourceforge.net>
- misc.c, common.h: add new toolbar icons for file ops
-
-Aug 29, 2008
- FIX: Martin Fischer <m_fischer@users.sourceforge.net>
- draw.c: fixed bug #1821257: no zoom larger than 1:1
-
-Jul 11, 2008
- FIX: Martin Fischer <m_fischer@users.sourceforge.net>
- misc.c: update map on loading initial layout
-
-Jul 10, 2008
- ENH: Martin Fischer <m_fischer@users.sourceforge.net>
- misc.c, misc.h, draw.c: allow user to cancel close request
-
-Jun 04, 2008
- FIX: Martin Fischer <m_fischer@users.sourceforge.net>
- cselect.c: Rescale dialog wasn't updated correctly
- misc2.c: fixed bug when rescale same piece several times
-
-Jun 03. 2008
- FIX: Martin Fischer <m_fischer@users.sourceforge.net>
- CMakeLists.txt: find getext on Win32
-
-Jun 03, 2008
- FIX: Martin Fischer <m_fischer@users.sourceforge.net>
- cselect.c: fixed bug when rescale same piece several times
- csnap.c: initialize grid spacing value
-
-Apr 13, 2008
- ENH: Bob Blackwell
- ctrain.c: updated label text
-
-Mar 27, 2008
- FIX: Martin Fischer <m_fischer@users.sourceforge.net>
- csnap.c: working default value for grid spacing
-
-Mar 21, 2008
- FIX: Bob Blackwell
- doption.c: uppdated labels in option dialogs
-
-Mar 18, 2008
- FIX: Bob Blackwell
- doption.c: rearrange option settings in display / command / preferences
- dialog
-
-Feb 04, 2008
- FIX: Martin Fischer <m_fischer@users.sourceforge.net>
- CMakeLists.txt: Fix missing icon problem for Windows exe
-
-Feb 04, 2008
- FIX: Mikko Nissinen <mni77@users.sourceforge.net>
- misc.c: Fixed an internationalization bug in MenuPlayback.
-
-Feb 04, 2008
- FIX: Mikko Nissinen <mni77@users.sourceforge.net>
- cnote.c: Minor fix to internationalization.
-
-Feb 03, 2008
- ENH: Martin Fischer <m_fischer@users.sourceforge.net>
- cprint.c: printout of date is correctly localized now.
-
-Feb 03, 2008
- ENH: Martin Fischer <m_fischer@users.sourceforge.net>
- misc.c, misc.h doption.c: on startup last file can now be loaded automatically.
- This behavior is controled by an option in the preferences dialog.
-
-Jan 28, 2008
- FIX: Mikko Nissinen <mni77@users.sourceforge.net>
- misc.c: Product name changed in font selection dialog.
-
-Jan 28, 2008
- FIX: Mikko Nissinen <mni77@users.sourceforge.net>
- common.c: Dynamically allocate and form some global translatable
- strings.
-
-Jan 27, 2008
- FIX: Mikko Nissinen <mni77@users.sourceforge.net>
- macro.c: String XTrkCad changed to XTrackCAD.
-
-Jan 27, 2008
- FIX: Martin Fischer <m_fischer@users.sourceforge.net>
- misc.c, fileio.c: fixed product name
-
-Jan 27, 2008
- FIX: Martin Fischer <m_fischer@users.sourceforge.net>
- dcar.c: corrected problem in CarPartWrite()
-
-Jan 25, 2008
- FIX: Martin Fischer <m_fischer@users.sourceforge.net>
- custom.c, version.h: Changed product name to XTrackCAD and version
- to 4.1.0b1
-
-Jan 23, 2008
- FIX: Mikko Nissinen <mni77@users.sourceforge.net>
- ctodesgn.c: Removed '_()' around turnout label from InitNewTurn()
- and ShowTurnoutDesigner().
- dcustmgm.c: Saving custom stuff in demo mode changed the locale
- to "C" without restoring it back to original.
-
-Jan 23, 2008
- FIX: Martin Fischer <m_fischer@users.sourceforge.net>
- fileio.c: increase precision for roomsize to 6 digits .
-
-Jan 23, 2008
- FIX: Mikko Nissinen <mni77@users.sourceforge.net>
- param.c: ParamPlayback(): If parameter type is PD_FLOAT, then use the
- locale "C" during atof().
-
-Jan 22, 2008
- ENH: Mikko Nissinen <mni77@users.sourceforge.net>
- misc.c: Save user locale when program initializes.
- macro.c: Gettext support added.
-
-Jan 21, 2008
- ENH: Mikko Nissinen <mni77@users.sourceforge.net>
- Gettext support added. The following 48 files were modified:
- ccurve.c, cdraw.c, celev.c, cgroup.c, chndldto.c, cjoin.c, cmisc.c,
- cmisc2.c, cmodify.c, cnote.c, compound.c, cparalle.c, cpull.c,
- cruler.c, cselect.c, csnap.c, csplit.c, cstraigh.c, cstruct.c,
- ctext.c, ctodesgn.c, ctrain.c, cturnout.c, cturntbl.c, cundo.c,
- custom.c, dbench.c, dbitmap.c, dcar.c, dcmpnd.c, dcustmgm.c, dease.c,
- denum.c, dlayer.c, doption.c, dpricels.c, dprmfile.c, draw.c,
- drawgeom.c, misc2.c, param.c, smalldlg.c, tcurve.c, tease.c, track.c,
- tstraigh.c
-
-Jan 18, 2008
- FIX: Mikko Nissinen <mni77@users.sourceforge.net>
- dcar.c: CarInvSaveText() Car list text file is now created to
- selected path instead of current working directory.
-
-Jan 15, 2008
- IMPROVEMENT: Mikko Nissinen <mni77@users.sourceforge.net>
- Basic gettext support added. Gettext is initialized in misc.c:wMain().
- The initialization routine is defined in i18n.[ch] along with all
- other gettext definitions.
- CMakeLists.txt
- fileio.[ch]
- i18n.[ch]
- misc.c
- Also the following CMakeLists were modified for gettext:
- xtrkcad/CMakeLists.txt
- xtrkcad/app/CMakeLists.txt
- xtrkcad/app/help/CMakeLists.txt
- xtrkcad/app/i18n/CMakeLists.txt (Initial import)
- xtrkcad/app/wlib/gtklib/CMakeLists.txt
-
-Dec 13, 2007
- FIX: Martin Fischer <m_fischer@users.sourceforge.net>
- fileio.c: fixed segfault when locale is saved
-
-Dec. 12. 2007
- FIX: Martin Fischer <m_fischer@users.sourceforge.net>
- dlayer.c: layers lists are updated properly after file is loaded
- fileio.c: fixed segfault when locale is saved
- Makefile: updated dependencies for dlayer.c
-
-Dec 08, 2007
- FIX: Martin Fischer <m_fischer@users.sourceforge.net>
- xtrkcad.ico: create a new color icon
-
-Dec. 01, 2007
- BUGFIX: Martin Fischer <m_fischer@users.sourceforge.net>
- acclkeys.h: removed non-working accelerator key for deselect all
-
-Nov. 30, 2007
- FIX: Timothy M. Shead
- misc.c: make sure that font initialization is run first
-
-Oct 29, 2007
- BUGFIX: Martin Fischer <m_fischer@users.sourceforge.net>
- dlayer.c: Shortened button text to 'Defaults'
-
-Oct 10, 2007
- BUGFIX: Martin Fischer <m_fischer@users.sourceforge.net>
- csnap.c cprint.c, misc.c: Accelerator keys for Print and
- Snap Grid Dialog work again.
-
-Oct 10, 2007
- BUGFIX: Martin Fischer <m_fischer@users.sourceforge.net>
- acclkeys.h: Revert and Redo used the same accelerator key.
- Fixed, Revert doesn't have an acclerator now.
-
-Sep 28, 2007
- IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net>
- misc.c, smalldlg.c: Use large message for tip of the day
- teaser line. Changed to a more generous spacing in dialogs.
-
-Sep 23, 2007
- IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net>
- misc.c, smalldlg.c: separated tip window code into new
- source file. Slightly improved the "tip of the day" dialog
- (jump to next and prev tip).
-
-Sep 15, 2007
- IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net>
- misc.c: XTrkCad now has a real splash window during startup
-
-Jul 22, 2007
- IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net>
- draw.c: the mouse wheel can be used for zooming in and out
-
-Jun 27, 2007
- IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net>
- dlayer.c: some cleanup and modified layer buttons. Also all
- layer buttons where moved to the bitmaps directory.
-
-Jun 16, 2007
- IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net>
- fileio.c: default directory for storing files is the user's
- home directory now.
-
-Jun 15, 2007
- BUGFIX: Martin Fischer <m_fischer@users.sourceforge.net>
- dlayer.c: fixed function prototype for Windows compile
-
-Jun 15, 2007
- IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net>
- dlayer.c: layer buttons now are push buttons that are in
- 'pressed' state when layer is visible.
-
-Jun 15, 2007
- IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net>
- dlayer.c, fileio.c, misc.c: settings for the layers can now
- be saved in the preferences. On opening a new layout or upon
- startup of XTrkCad these settings are automatically loaded.
-
-May 18, 2007
-
- IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net>
- draw.c misc.c: disable zoom up and zoom down buttons when
- end of list is reached
-
-Apr 30, 2007
-
- IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net>
- draw.c, misc.c, draw.h: use radio buttons for selecting zoom factor
- zoom in and out goes through all available zoom factors step by step
- setting zoom is available from the pulldown menu as well
-
-Apr 11, 2007
-
- IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net>
- draw.c: changed layout of status bar to include labels.
- Part count is no longer shown.
-
-Feb 23, 2007
- BUGFIX: Martin Fischer <m_fischer@users.sourceforge.net>
- cmisc.c, cselect.c rescale / resize works again. UI change to
- allow changing scale and gauge independently
-
-Feb 16, 2007
-
- IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net>
- Recently used files list is only updated after successful load
-
-
-Version 4.0.1
-=============
-
-May 26th, 2006
-
- IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net>
- Visual Studio C++ 2005 Express is now supported under Windows
-
-Mar 26th, 2006
-
- IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net>
- misc.c, fileio,c, draw.c If the application crashed the user can decide
- to resume work at the last state saved in a checkpoint file
- checkpoint files (ckp and ck1) are removed on normal exit
-
-Mar 25th, 2006
-
- BUGFIX: Martin Fischer <m_fischer@users.sourceforge.net>
- misc2.c prevent warning in DoSetScaleDesc
-
-Mar 02nd, 2006
-
- IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net>
- cturnout.c Improvements to the select turnout dialog, new turnout is drawn
- blue
-
-Feb. 26th, 2006
-
- NEW FEATURE: Martin Fischer <m_fischer@users.sourceforge.net>
- misc.c, cselect.c, 'Select orphaned track' command added to set all
- unconnected track pieces.
-
-Feb, 22nd, 2006
-
- NEW FEATURE: Martin Fischer <m_fischer@users.sourceforge.net>
- misc.c, misc2.c, doption.c Scale and gauge are two independant seetings
- now.
-
- NEW FEATURE: Martin Fischer <m_fischer@users.sourceforge.net>
- misc.c, cselect.c Add new function 'Invert Selection' which inverts
- the selection state of all visible objects on the layout
-
- NEW FEATURE: Martin Fischer <m_fischer@users.sourceforge.net>
- misc.c Add new function 'Revert' to main menu, implemented in ChkRevert
- acclkeys.h Added Ctrl-r as accelerator for 'Revert'
-
- IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net>
- cselect.c Optimized performance for 'Select Connected' operation
-
- IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net>
- bllnhelp.c: removed inconsistencies in usage of 'track' and 'object'
-
- IMPROVEMENT: Martin Fischer <m_fischer@users.sourceforge.net>
- misc.c: moved 'Join' command to 'Change' menu
-
- BUGFIX: Martin Fischer <m_fischer@users.sourceforge.net>
- fileio.c Setting locale to portable 'C' before reading/writing parameters
- and trackplans to prevent problems with locales that use comma as decimal
- separator ( eg. Germany )
-
- BUGFIX:
diff --git a/app/bin/acclkeys.h b/app/bin/acclkeys.h
index 09bd9bb..1cbdf00 100644
--- a/app/bin/acclkeys.h
+++ b/app/bin/acclkeys.h
@@ -30,6 +30,7 @@
/* commands */
#define ACCL_DESCRIBE (WCTL+'?')
#define ACCL_SELECT (WCTL+'e')
+#define ACCL_PAN (WCTL+'/')
#define ACCL_STRAIGHT (WCTL+'g')
#define ACCL_CURVE1 (WCTL+'4')
#define ACCL_CURVE2 (WCTL+'5')
@@ -38,6 +39,7 @@
#define ACCL_CIRCLE1 (WCTL+'8')
#define ACCL_CIRCLE2 (WCTL+'9')
#define ACCL_CIRCLE3 (WCTL+'0')
+#define ACCL_BEZIER (0)
#define ACCL_TURNOUT (WCTL+'t')
#define ACCL_TURNTABLE (WCTL+WSHIFT+'n')
#define ACCL_PARALLEL (WCTL+WSHIFT+'p')
@@ -70,6 +72,7 @@
#define ACCL_DRAWFILLCIRCLE1 (WALT+WCTL+'8')
#define ACCL_DRAWFILLCIRCLE2 (WALT+WCTL+'9')
#define ACCL_DRAWFILLCIRCLE3 (WALT+WCTL+'0')
+#define ACCL_DRAWBEZLINE (0)
#define ACCL_DRAWBOX (WCTL+WSHIFT+'[')
#define ACCL_DRAWFILLBOX (WALT+WCTL+'[')
#define ACCL_DRAWPOLYLINE (WCTL+WSHIFT+'2')
diff --git a/app/bin/appdefaults.c b/app/bin/appdefaults.c
new file mode 100644
index 0000000..a2dd885
--- /dev/null
+++ b/app/bin/appdefaults.c
@@ -0,0 +1,435 @@
+/** \file appdefaults.c
+* Provide defaults, mostly for first run of the program.
+*/
+
+/* XTrkCad - Model Railroad CAD
+* Copyright (C) 2017 Martin Fischer
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, 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 License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include <locale.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <wchar.h>
+
+#ifdef WINDOWS
+#include <Windows.h>
+#include <malloc.h>
+#endif
+
+#include "common.h"
+#include "custom.h"
+#include "fileio.h"
+#include "paths.h"
+#include "wlib.h"
+
+enum defaultTypes {
+ INTEGERCONSTANT,
+ FLOATCONSTANT,
+ STRINGCONSTANT,
+ INTEGERFUNCTION,
+ FLOATFUNCTION,
+ STRINGFUNCTION
+};
+
+struct appDefault {
+ char *defaultKey; /**< the key used to access the value */
+ bool wasUsed; /**< value has already been used on this run */
+ enum defaultTypes
+ valueType; /**< type of default, constant or pointer to a function */
+ union {
+ int intValue;
+ double floatValue;
+ char *stringValue;
+ int (*intFunction)(struct appDefault *, void *);
+ double (*floatFunction)(struct appDefault *, void *);
+ char *(*stringFunction)(struct appDefault *, void *);
+ } defaultValue;
+ void *additionalData;
+};
+
+static int GetLocalMeasureSystem(struct appDefault *ptrDefault,
+ void *additionalData);
+static int GetLocalDistanceFormat(struct appDefault *ptrDefault,
+ void *additionalData);
+static char *GetLocalPopularScale(struct appDefault *ptrDefault,
+ void *additionalData);
+static double GetLocalRoomSize(struct appDefault *ptrDefault,
+ void *additionalData);
+static char *GetParamFullPath(struct appDefault *ptrDefault,
+ void *additionalData);
+static char *GetParamPrototype(struct appDefault *ptrDefault,
+ void *additionalData);
+
+/**
+ * List of application default settings. As this is searched by binary search, the list has to be kept sorted
+ * alphabetically for the key, the first element
+ * Also the search is case sensitive on this field.
+ */
+
+struct appDefault xtcDefaults[] = {
+ { "DialogItem.cmdopt-preselect", 0, INTEGERCONSTANT,{ .intValue = 1 } }, /**< default command is select */
+ { "DialogItem.grid-horzenable", 0, INTEGERCONSTANT, { .intValue = 0 }},
+ { "DialogItem.grid-vertenable", 0, INTEGERCONSTANT,{ .intValue = 0 } },
+ { "DialogItem.pref-dstfmt", 0, INTEGERFUNCTION,{ .intFunction = GetLocalDistanceFormat } }, /**< number format for distances */
+ { "DialogItem.pref-units", 0, INTEGERFUNCTION,{ .intFunction = GetLocalMeasureSystem } }, /**< default unit depends on region */
+ { "DialogItem.rgbcolor-exception", 0, INTEGERCONSTANT, { .intValue = 15923462 }}, /**< rich yellow as exception color */
+ { "Parameter File Map.British stock", 0, STRINGFUNCTION,{ .stringFunction = GetParamFullPath }, "br.xtp" },
+ { "Parameter File Map.European stock", 0, STRINGFUNCTION,{ .stringFunction = GetParamFullPath }, "eu.xtp" },
+ { "Parameter File Map.NMRA RP12-25 Feb 2015 O scale Turnouts", 0, STRINGFUNCTION,{ .stringFunction = GetParamFullPath }, "nmra-o.xtp" },
+ { "Parameter File Map.NMRA RP12-27 Feb 2015 S Scale Turnouts", 0, STRINGFUNCTION,{ .stringFunction = GetParamFullPath }, "nmra-s.xtp" },
+ { "Parameter File Map.NMRA RP12-31 Feb 2015 HO Scale Turnouts", 0, STRINGFUNCTION,{ .stringFunction = GetParamFullPath }, "nmra-ho.xtp" },
+ { "Parameter File Map.NMRA RP12-33 Feb 2015 TT Scale Turnouts", 0, STRINGFUNCTION,{ .stringFunction = GetParamFullPath }, "nmra-tt.xtp" },
+ { "Parameter File Map.NMRA RP12-35 Feb 2015 N Scale Turnouts", 0, STRINGFUNCTION,{ .stringFunction = GetParamFullPath }, "nmra-n.xtp" },
+ { "Parameter File Map.NMRA RP12-37 Feb 2015 Z scale Turnouts", 0, STRINGFUNCTION,{ .stringFunction = GetParamFullPath }, "nmra-z.xtp" },
+ { "Parameter File Map.North American Prototypes", 0, STRINGFUNCTION,{ .stringFunction = GetParamFullPath }, "protoam.xtp" },
+ { "Parameter File Map.Trees", 0, STRINGFUNCTION,{ .stringFunction = GetParamFullPath } , "trees.xtp" },
+ { "Parameter File Names.File1", 0, STRINGFUNCTION,{ .stringFunction = GetParamPrototype }},
+ { "Parameter File Names.File2", 0, STRINGCONSTANT,{ .stringValue = "Trees" } },
+ { "Parameter File Names.File3", 0, STRINGCONSTANT,{ .stringValue = "NMRA RP12-37 Feb 2015 Z scale Turnouts" } },
+ { "Parameter File Names.File4", 0, STRINGCONSTANT,{ .stringValue = "NMRA RP12-35 Feb 2015 N Scale Turnouts" } },
+ { "Parameter File Names.File5", 0, STRINGCONSTANT,{ .stringValue = "NMRA RP12-33 Feb 2015 TT Scale Turnouts" } },
+ { "Parameter File Names.File6", 0, STRINGCONSTANT,{ .stringValue = "NMRA RP12-31 Feb 2015 HO Scale Turnouts" } },
+ { "Parameter File Names.File7", 0, STRINGCONSTANT,{ .stringValue = "NMRA RP12-27 Feb 2015 S Scale Turnouts" } },
+ { "Parameter File Names.File8", 0, STRINGCONSTANT,{ .stringValue = "NMRA RP12-25 Feb 2015 O scale Turnouts" } },
+ { "draw.roomsizeX", 0, FLOATFUNCTION, {.floatFunction = GetLocalRoomSize }}, /**< layout width */
+ { "draw.roomsizeY", 0, FLOATFUNCTION,{ .floatFunction = GetLocalRoomSize } }, /**< layout depth */
+ { "misc.scale", 0, STRINGFUNCTION, { .stringFunction = GetLocalPopularScale}}, /**< the (probably) most popular scale for a region */
+};
+
+#define DEFAULTCOUNT (sizeof(xtcDefaults)/sizeof(xtcDefaults[0]))
+
+
+static long bFirstRun; /**< TRUE if appl is run the first time */
+static char regionCode[3]; /**< will be initialized to the locale's region code */
+
+static wBool_t(*GetIntegerPref)(const char *, const char *, long *, long) = wPrefGetIntegerExt; /**< pointer to active integer pref getter */
+static wBool_t(*GetFloatPref)(const char *, const char *, double *, double) = wPrefGetFloatExt; /**< pointer to active float pref getter */
+static char *(*GetStringPref)(const char *, const char *) = wPrefGetStringExt; /**< pointer to active string pref getter */
+
+/**
+ * A recursive binary search function. It returns location of x in
+ * given array arr[l..r] is present, otherwise -1
+ * Taken from http://www.geeksforgeeks.org/binary-search/ and modified
+ *
+ * \param arr IN array to search
+ * \param l IN starting index
+ * \param r IN highest index in array
+ * \param key IN key to search
+ * \return index if found, -1 otherwise
+ */
+
+static int binarySearch(struct appDefault arr[], int l, int r, char *key)
+{
+ if (r >= l) {
+ int mid = l + (r - l) / 2;
+ int res = strcmp(key, arr[mid].defaultKey);
+
+ // If the element is present at the middle itself
+ if (!res) {
+ return mid;
+ }
+
+ // If the array size is 1
+ if (r == 0) {
+ return -1;
+ }
+
+ // If element is smaller than mid, then it can only be present
+ // in left subarray
+ if (res < 0) {
+ return binarySearch(arr, l, mid - 1, key);
+ }
+
+ // Else the element can only be present in right subarray
+ return binarySearch(arr, mid + 1, r, key);
+ }
+
+ // We reach here when element is not present in array
+ return -1;
+}
+
+/**
+ * Lookup default for a value
+ *
+ * \param defaultValues IN array of all default values
+ * \param section IN default's section
+ * \param name IN default's name
+ * \return pointer to default if found, NULL otherwise
+ */
+struct appDefault *
+FindDefault(struct appDefault *defaultValues, const char *section,
+ const char *name)
+{
+ char *searchString = malloc(strlen(section) + strlen(name) +
+ 2); //includes separator and terminating \0
+ int res;
+ sprintf(searchString, "%s.%s", section, name);
+
+ res = binarySearch(defaultValues, 0, DEFAULTCOUNT-1, searchString);
+ free(searchString);
+
+ if (res != -1 && defaultValues[res].wasUsed == FALSE) {
+ defaultValues[res].wasUsed = TRUE;
+ return (defaultValues + res);
+ } else {
+ return (NULL);
+ }
+}
+/**
+ * Get the application's default region code. On Windows, the system's API is used.
+ * On *ix the environment variable LANG is supposed to contain a value in the
+ * format ll_RR* where rr is the two character region code.
+ */
+static void
+InitializeRegionCode(void)
+{
+ strcpy(regionCode, "US");
+
+#ifdef WINDOWS
+ {
+ LCID lcid;
+ char iso3166[10];
+
+ lcid = GetThreadLocale();
+ GetLocaleInfo(lcid, LOCALE_SISO3166CTRYNAME, iso3166, sizeof(iso3166));
+ strncpy(regionCode, iso3166, 2);
+ }
+#else
+ {
+ char *pLang;
+ pLang = getenv("LANG");
+
+ if (pLang) {
+ char *ptr;
+ ptr = strpbrk(pLang, "_-");
+
+ if (ptr) {
+ strncpy(regionCode, ptr + 1, 2);
+ }
+ }
+ }
+#endif
+}
+
+/**
+ * For the US the classical 4x8 sheet is used as default size. in the metric world 1,25x2,0m is used.
+ */
+
+static double
+GetLocalRoomSize(struct appDefault *ptrDefault, void *data)
+{
+ if (!strcmp(ptrDefault->defaultKey, "draw.roomsizeY")) {
+ return (strcmp(regionCode, "US") ? 125.0/2.54 : 48);
+ }
+
+ if (!strcmp(ptrDefault->defaultKey, "draw.roomsizeX")) {
+ return (strcmp(regionCode, "US") ? 200.0 / 2.54 : 96);
+ }
+
+ return (0.0); // should never get here
+}
+
+/**
+ * The most popular scale is supposed to be HO except for UK where OO is assumed.
+ */
+
+static char *
+GetLocalPopularScale(struct appDefault *ptrDefault, void *data)
+{
+ return (strcmp(regionCode, "GB") ? "HO" : "OO");
+}
+
+/**
+ * The measurement system is english for the US and metric elsewhere
+ */
+static int
+GetLocalMeasureSystem(struct appDefault *ptrDefault, void *data)
+{
+ return (strcmp(regionCode, "US") ? 1 : 0);
+}
+
+/**
+* The distance format is 999.9 cm for metric and ?? for english
+*/
+static int
+GetLocalDistanceFormat(struct appDefault *ptrDefault, void *data)
+{
+ return (strcmp(regionCode, "US") ? 8 : 5);
+}
+
+/**
+* Prototype definitions currently only exist for US and British. So US
+* is assumed to be the default.
+*/
+
+static char*
+GetParamPrototype(struct appDefault *ptrDefault, void *additionalData)
+{
+ return (strcmp(regionCode, "GB") ? "North American Prototypes" : "British stock");
+}
+
+/**
+ * The full path to the applications parameter directory
+ */
+static char *
+GetParamFullPath(struct appDefault *ptrDefault, void *additionalData)
+{
+ char *str;
+ MakeFullpath(&str, libDir, PARAM_SUBDIR, (char*)additionalData, (void *)0);
+ return str;
+}
+
+
+/**
+ * The following are three jump points for the correct implementation. Changing the funtion pointer
+ * allows to switch from the extended default version to the basic implementation.
+ */
+
+wBool_t
+wPrefGetInteger(const char *section, const char *name, long *result, long defaultValue)
+{
+ return GetIntegerPref(section, name, result, defaultValue);
+}
+
+wBool_t
+wPrefGetFloat(const char *section, const char *name, double *result, double defaultValue)
+{
+ return GetFloatPref(section, name, result, defaultValue);
+}
+
+char *
+wPrefGetString(const char *section, const char *name)
+{
+ return GetStringPref(section, name);
+}
+
+/**
+ * Get an integer value from the configuration file. The is a wrapper for the real
+ * file access and adds a region specific default value.
+ *
+ * \param section IN section in config file
+ * \param name IN name in config file
+ * \param result OUT pointer to result
+ * \param defaultValue IN the default value to use if config is not found
+ * \return returncode of wPrefGetIntegerBasic()
+ */
+wBool_t
+wPrefGetIntegerExt(const char *section, const char *name, long *result,
+ long defaultValue)
+{
+ struct appDefault *thisDefault;
+
+ thisDefault = FindDefault(xtcDefaults, section, name);
+
+ if (thisDefault) {
+ if (thisDefault->valueType == INTEGERCONSTANT) {
+ defaultValue = thisDefault->defaultValue.intValue;
+ } else {
+ defaultValue = (thisDefault->defaultValue.intFunction)(thisDefault,
+ thisDefault->additionalData);
+ }
+ }
+
+ return (wPrefGetIntegerBasic(section, name, result, defaultValue));
+}
+
+/**
+ * Get a float value from the configuration file. The is a wrapper for the real
+ * file access and adds a region specific default value.
+ *
+ * \param section IN section in config file
+ * \param name IN name in config file
+ * \param result OUT pointer to result
+ * \param defaultValue IN the default value to use if config is not found
+ * \return returncode of wPrefGetFloatBasic()
+ */
+
+wBool_t
+wPrefGetFloatExt(const char *section, const char *name, double *result,
+ double defaultValue)
+{
+ struct appDefault *thisDefault;
+
+ thisDefault = FindDefault(xtcDefaults, section, name);
+
+ if (thisDefault) {
+ if (thisDefault->valueType == FLOATCONSTANT) {
+ defaultValue = thisDefault->defaultValue.floatValue;
+ } else {
+ defaultValue = (thisDefault->defaultValue.floatFunction)(thisDefault,
+ thisDefault->additionalData);
+ }
+ }
+
+ return (wPrefGetFloatBasic(section, name, result, defaultValue));
+}
+
+/**
+ * Get a string from the configuration file. The is a wrapper for the real
+ * file access and adds a region specific default value.
+ *
+ * \param section IN section in config file
+ * \param name IN name in config file
+ * \return returncode of wPrefGetStringBasic()
+ */
+char *
+wPrefGetStringExt(const char *section, const char *name)
+{
+ struct appDefault *thisDefault;
+
+ thisDefault = FindDefault(xtcDefaults, section, name);
+
+ if (thisDefault) {
+ char *prefString;
+ char *defaultValue;
+
+ if (thisDefault->valueType == STRINGCONSTANT) {
+ defaultValue = thisDefault->defaultValue.stringValue;
+ } else {
+ defaultValue = (thisDefault->defaultValue.stringFunction)(thisDefault,
+ thisDefault->additionalData);
+ }
+
+ prefString = (char *)wPrefGetStringBasic(section, name);
+ return (prefString ? prefString : defaultValue);
+ } else {
+ return ((char *)wPrefGetStringBasic(section, name));
+ }
+}
+
+/**
+ * Initialize the application default system. The flag firstrun is used to find
+ * out whether the application was run before. This is accomplished by trying
+ * to read it from the configuration file. As it is only written after this
+ * test, it can never be found on the first run of the application ie. when the
+ * configuration file does not exist yet.
+ */
+
+void
+InitAppDefaults(void)
+{
+ wPrefGetIntegerBasic( "misc", "firstrun", &bFirstRun, TRUE);
+ if (bFirstRun) {
+ wPrefSetInteger("misc", "firstrun", FALSE);
+ InitializeRegionCode();
+ } else {
+ GetIntegerPref = wPrefGetIntegerBasic;
+ GetFloatPref = wPrefGetFloatBasic;
+ GetStringPref = wPrefGetStringBasic;
+ }
+}
diff --git a/app/bin/bdf2xtp.c b/app/bin/bdf2xtp.c
index adc2b04..76fb31a 100644
--- a/app/bin/bdf2xtp.c
+++ b/app/bin/bdf2xtp.c
@@ -134,21 +134,17 @@ double findDistance( coOrd p0, coOrd p1 )
int small(double v )
/* is <v> close to 0.0 */
{
- return (fabs(v) < 0.0001);
+ return (fabs(v) < 0.000000000001);
}
double findAngle( coOrd p0, coOrd p1 )
/* find angle between two points */
{
double dx = p1.x-p0.x, dy = p1.y-p0.y;
- if (small(dx)) {
- if (dy >=0) return 0.0;
+ if (small(dx) && small(dy)) {
+ if (dy >=0.0) return 0.0;
else return 180.0;
}
- if (small(dy)) {
- if (dx >=0) return 90.0;
- else return 270.0;
- }
return R2D(atan2( dx,dy ));
}
diff --git a/app/bin/bitmaps/bezier.xpm b/app/bin/bitmaps/bezier.xpm
new file mode 100644
index 0000000..6c592ed
--- /dev/null
+++ b/app/bin/bitmaps/bezier.xpm
@@ -0,0 +1,23 @@
+/* XPM */
+static char * bezier_xpm[] = {
+"16 16 4 1",
+" c None",
+"! c #000000000000",
+"# c #FFFF00000000",
+"$ c #808080000000",
+" ### !!!!",
+" # #########",
+" ### !! !",
+" $!! $!!!",
+" !!$$ !! !",
+" !! $!$ !",
+" $$ !! !! $ ",
+" !!!!!!! ",
+" !! ! $$ ",
+" $!! !! ",
+"! !$$!! ",
+"! ! $! ",
+"!!!$ !!$ ",
+"! !! ### ",
+"######### # ",
+"!!!! ### "}; \ No newline at end of file
diff --git a/app/bin/bitmaps/dbezier.xpm b/app/bin/bitmaps/dbezier.xpm
new file mode 100644
index 0000000..1bf366f
--- /dev/null
+++ b/app/bin/bitmaps/dbezier.xpm
@@ -0,0 +1,22 @@
+/* XPM */
+static char * dbezier_xpm[] = {
+"16 16 3 1",
+" c None",
+"! c #000000000000",
+"# c #FFFF00000000",
+" ### ",
+" # ########",
+" ### !!!!",
+" !!!! ",
+" !!!! ",
+" !!! ",
+" !!! ",
+" !! ",
+" !!! ",
+" !! ",
+" !!! ",
+" !!! ",
+" !!! ",
+"!!!! ### ",
+"######## # ",
+" ### "}; \ No newline at end of file
diff --git a/app/bin/bitmaps/ecornu.xpm b/app/bin/bitmaps/ecornu.xpm
new file mode 100644
index 0000000..e32e56a
--- /dev/null
+++ b/app/bin/bitmaps/ecornu.xpm
@@ -0,0 +1,23 @@
+/* XPM */
+static char * ecornu_xpm[] = {
+"41 16 4 1",
+". c None",
+" c #000000000000",
+"# c #FFFF00000000",
+"$ c #808080000000",
+" .................................. .",
+" ..................................... ",
+" ...... ... .. .. . ... .. .. .",
+" .. .. . .... .. . .. .. . .. . .. . .",
+" ..... .. .. .. . .. .. . . .. . .",
+" ..... .. .... . .... .. .. . .... .. . .",
+" .. . ... . .. .. .. . .. .. ",
+".........................................",
+"...$$$...###.....................$$$$$...",
+"..$...$.#...#................$$$$.....$..",
+".$..$..$#......##..###..###$$#..#.$$$..$.",
+"$..$.$..#.....#..#.#$$#$#..#.#..#$...$..$",
+"$..$...$#.....#$$#$#....#..#.#..#..$.$..$",
+".$..$$$.#..$#$#..#.#....#..#.#..#$..$..$.",
+"..$.....$###...##..#....#..#..##..$...$..",
+"...$$$$$...........................$$$..."};
diff --git a/app/bin/bitmaps/eltsharp.xpm b/app/bin/bitmaps/eltsharp.xpm
new file mode 100644
index 0000000..70985c1
--- /dev/null
+++ b/app/bin/bitmaps/eltsharp.xpm
@@ -0,0 +1,21 @@
+/* XPM */
+static char * eltsharp_xpm[] = {
+"41 16 2 1",
+". c None",
+" c #000000000000",
+" .................................. .",
+" ..................................... ",
+" ...... ... .. .. . ... .. .. .",
+" .. .. . .... .. . .. .. . .. . .. . .",
+" ..... .. .. .. . .. .. . . .. . .",
+" ..... . .... . .... .. .. . .... .. . .",
+" .. . . ... . .. .. .. . .. .. ",
+".........................................",
+"..... ...... . ......................",
+".... ...... ...... ......................",
+"... ....... ...... ... .. .. .....",
+".. ......... . .. . .. . .. . .. ....",
+"... ............ . .. . .. . .... .. ....",
+".... ........... . .. . . . .... .....",
+"..... ..... .. .. .. . . .... .......",
+"................................. ......."};
diff --git a/app/bin/bitmaps/helix.xpm b/app/bin/bitmaps/helix.xpm
deleted file mode 100644
index ba0551e..0000000
--- a/app/bin/bitmaps/helix.xpm
+++ /dev/null
@@ -1,21 +0,0 @@
-/* XPM */
-static char * helix_xpm[] = {
-"16 16 2 1",
-" c None",
-". c #000000000000",
-" ",
-" ........... ",
-" . ",
-" . . ",
-" . . ",
-" .......... ",
-" . . ",
-" . . ",
-" .......... ",
-" . . ",
-" . . ",
-" ......... ",
-" . ",
-" . ",
-" ............ ",
-" "};
diff --git a/app/bin/bitmaps/map.xpm b/app/bin/bitmaps/map.xpm
new file mode 100644
index 0000000..a1f427a
--- /dev/null
+++ b/app/bin/bitmaps/map.xpm
@@ -0,0 +1,22 @@
+/* XPM */
+static char * map_xpm[] = {
+"16 16 3 1",
+"o c None",
+" c #000000000000",
+". c #FFFFFFFFFFFF",
+"ooo ooo",
+"ooo ooo",
+"oo ........ oo",
+"oo ........ oo",
+"o .......... o",
+"o .......... o",
+" ...... .... ",
+" .... .. ... ",
+" .. .. .... .. ",
+" ... ...... . ",
+" .............. ",
+" ",
+" ",
+"oooooooooooooooo",
+"oooooooooooooooo",
+"oooooooooooooooo"};
diff --git a/app/bin/bitmaps/pan.xpm b/app/bin/bitmaps/pan.xpm
new file mode 100644
index 0000000..8782714
--- /dev/null
+++ b/app/bin/bitmaps/pan.xpm
@@ -0,0 +1,22 @@
+/* XPM */
+static char * pan_xpm[] = {
+"16 16 3 1",
+" c None",
+". c #000000000000",
+"X c #FFFF00000000",
+" ",
+" XX ",
+" XXXX ",
+" XXXXXX. ",
+" XX XX XX ",
+" XX ",
+" XX XX XX ",
+" XXXXXXXXXXXXXX ",
+" XXXXXXXXXXXXXX ",
+" XX XX XX ",
+" XX ",
+" XX XX XX ",
+" XXXXXX ",
+" XXXX ",
+" XX ",
+" "};
diff --git a/app/bin/cbezier.c b/app/bin/cbezier.c
new file mode 100644
index 0000000..92855c1
--- /dev/null
+++ b/app/bin/cbezier.c
@@ -0,0 +1,1153 @@
+/** \file cbezier.c
+ * Bezier Command. Draw or modify a Bezier (Track or Line).
+ */
+ /* XTrkCad - Model Railroad CAD
+ *
+ * Cubic Bezier curves have a definitional representation as an a set of four points.
+ * The first and fourth are the end points, while the middle two are control points.
+ * The control points positions define the angle at the ends and by their relative positions the overall
+ * curvature. This representation is a familiar approach for those who know drawing programs such as Adobe
+ * Illustrator or CorelDraw.
+ *
+ * In XTrackCAD, the Bezier form is also represented and drawn as a set of
+ * joined circular arcs that approximate the Bezier form within a small tolerance. This is because
+ * many of the operations we need to do are either computationally difficult or
+ * impossible using the Bezier equations. For example, creating a parallel Bezier
+ * which is necessary to draw a track with two lines or sleepers has no easy, stable solution.
+ * But the program is already able to do these tasks for straight lines and curves.
+ *
+ * Note that every time we change the Bezier points we have to recalculate the arc approximation,
+ * but that means that the majority of the time we are using the simpler approximation.
+ *
+ * We do not allow Bezier curves that have loops or cusps as they make no sense for tracks and
+ * can easily be approximated for lines with multiple unaligned Bezier curves.
+ *
+ * This program borrows from particular ideas about converting Bezier curves that Pomax placed into
+ * open source. The originals in Javascript can be found at github.com/Pomax.
+ * The web pages that explain many other techniques are located at https://pomax.github.io/bezierinfo
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, 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 License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#include "track.h"
+#include "draw.h"
+#include "ccurve.h"
+#include "cbezier.h"
+#include "tbezier.h"
+#include "cstraigh.h"
+#include "drawgeom.h"
+#include "cjoin.h"
+#include "i18n.h"
+#include "common.h"
+#include "wcolors.h"
+#include "math.h"
+#include "utility.h"
+#include "param.h"
+#include "fileio.h"
+#include "layout.h"
+#include "cundo.h"
+
+extern drawCmd_t tempD;
+
+
+/*
+ * STATE INFO
+ */
+enum Bezier_States { NONE,
+ POS_1,
+ CONTROL_ARM_1,
+ POS_2,
+ CONTROL_ARM_2,
+ PICK_POINT,
+ POINT_PICKED,
+ TRACK_SELECTED };
+
+typedef struct {
+ curveData_t curveData;
+ double start;
+ double end;
+ coOrd pos0;
+ coOrd pos1;
+ } bCurveData_t;
+
+static struct {
+ enum Bezier_States state;
+ coOrd pos[4];
+ int selectPoint;
+ wDrawColor color;
+ DIST_T width;
+ track_p trk[2];
+ EPINX_T ep[2];
+ dynArr_t crvSegs_da;
+ int crvSegs_da_cnt;
+ trkSeg_t cp1Segs_da[4];
+ int cp1Segs_da_cnt;
+ trkSeg_t cp2Segs_da[4];
+ int cp2Segs_da_cnt;
+ BOOL_T unlocked;
+ track_p selectTrack;
+ BOOL_T track;
+ DIST_T minRadius;
+ } Da;
+
+
+
+/**
+ * Draw a ControlArm.
+ * A control arm has two filled or unfilled circles for endpoints and a straight line between them.
+ * If the end or control point is not selectable we don't mark it with a circle.
+ * If a selectable end or control point is unlocked place a filled circle on it, otherwise an empty circle.
+ * A red color indicates that this arm, end or control point is "active" as it was selected.
+ */
+int createControlArm(
+ trkSeg_t sp[], //seg pointer for up to 3 trkSegs (ends and line)
+ coOrd pos0, //end on curve
+ coOrd pos1, // control point at other end of line
+ BOOL_T track, // isTrack()? (otherwise Line)
+ BOOL_T selectable, // can this arm be selected?
+ BOOL_T cp_direction_locked, //isFixed to track
+ int point_selected, //number of point 0, 1 or -1
+ wDrawColor color //drawColorBlack or drawColorWhite
+ )
+{
+ DIST_T d, w;
+ d = tempD.scale*0.25;
+ w = tempD.scale/tempD.dpi; /*double width*/
+ sp[0].u.l.pos[0] = pos0;
+ sp[0].u.l.pos[1] = pos1;
+ sp[0].type = SEG_STRLIN;
+ sp[0].width = w;
+ sp[0].color = (point_selected>=0)?drawColorRed:drawColorBlack;
+ int n = 0;
+ if (selectable) {
+ for (int j=0;j<2;j++) {
+ if (j==0 && cp_direction_locked) continue; //Don't show select circle if end locked
+ n++;
+ sp[n].u.c.center = j==0?pos0:pos1;
+ sp[n].u.c.radius = d/4;
+ sp[n].width = w;
+ sp[n].color = (j==point_selected)?drawColorRed:drawColorBlack;
+ if (j==point_selected && cp_direction_locked) {
+ sp[n].type = SEG_FILCRCL;
+ } else {
+ sp[n].type = SEG_CRVLIN;
+ sp[n].u.c.a0 = 0.0;
+ sp[n].u.c.a1 = 360.0;
+ }
+ }
+ }
+ return n+1;
+}
+
+coOrd getPoint(coOrd pos[4], double s) {
+ double mt = 1-s;
+ double a = mt*mt*mt;
+ double b = mt*mt*s*3;
+ double c = mt*s*s*3;
+ double d = s*s*s;
+ coOrd ret;
+ ret.x = a*pos[0].x + b*pos[1].x + c*pos[2].x + d*pos[3].x;
+ ret.y = a*pos[0].y + b*pos[1].y + c*pos[2].y + d*pos[3].y;
+ return ret;
+}
+/*
+ * Get Error between a Bezier and an arc centered at pc that goes from start to end
+ *
+ * Because the curve is defined to pass through the start and the end and the middle, the test is
+ * to see how much of an error there is between those points. If the sum of the errors is off by more \
+ * than 0.5 pixels - that will mean it is not a good fit.
+ *
+ */
+double BezError(coOrd pos[4], coOrd center, coOrd start_point, double start, double end) {
+ double quarter = (end - start) / 4; // take point at 1/4 and 3/4 and check
+ coOrd c1 = getPoint(pos, start + quarter);
+ coOrd c2 = getPoint(pos, end - quarter);
+ double ref = FindDistance(center, start_point); //radius
+ double d1 = FindDistance(center, c1); // distance to quarter
+ double d2 = FindDistance(center, c2); // distance to three quarters
+ return fabs(d1-ref) + fabs(d2-ref); //total error at quarter points
+};
+
+/*
+ * Get distance between a point and a line segment
+ */
+
+double DistanceToLineSegment(coOrd p, coOrd l1, coOrd l2) {
+ double A = p.x - l1.x;
+ double B = p.y - l1.y;
+ double C = l2.x - l1.x;
+ double D = l2.y - l1.y;
+
+ double dot = A * C + B * D;
+ double len_sq = C * C + D * D;
+ double param = -1;
+ if (len_sq != 0) //non 0 length line
+ param = dot / len_sq;
+
+ double xx, yy;
+
+ if (param < 0) { // zero length line or beyond end use point 1
+ xx = l1.x;
+ yy = l1.y;
+ } else if (param > 1) { // beyond point 2 end of line segment
+ xx = l2.x;
+ yy = l2.y;
+ } else { // In the middle
+ xx = l1.x + param * C;
+ yy = l1.y + param * D;
+ }
+
+ double dx = p.x - xx; //distance to perpendicular (or end point)
+ double dy = p.y - yy;
+ return sqrt(dx * dx + dy * dy);
+}
+
+/*
+ * Get Error between a straight line segment and the Bezier curve.
+ * Sum distance to straight line of quarter points.
+ */
+
+double BezErrorLine(coOrd pos[4], coOrd start_point, coOrd end_point, double start, double end) {
+ double quarter = (end - start) / 4; // take point at 1/4 and 3/4 and check
+ coOrd c1 = getPoint(pos, start + quarter);
+ coOrd c2 = getPoint(pos, end - quarter);
+ double d1 = DistanceToLineSegment(c1, start_point, end_point);
+ double d2 = DistanceToLineSegment(c2, start_point, end_point);
+ return fabs(d1)+fabs(d2);
+}
+
+/*
+ * Add element to DYNARR pointed to by caller from segment handed in
+ */
+void addSegBezier(dynArr_t * const array_p, trkSeg_p seg) {
+ trkSeg_p s;
+
+
+ DYNARR_APPEND(trkSeg_t, * array_p, 1); //Adds 1 to cnt
+ s = &DYNARR_N(trkSeg_t,* array_p,array_p->cnt-1);
+ s->type = seg->type;
+ s->color = seg->color;
+ s->width = seg->width;
+ s->bezSegs.cnt = 0;
+ if (s->bezSegs.ptr) MyFree(s->bezSegs.ptr);
+ s->bezSegs.ptr=NULL;
+ s->bezSegs.max = 0;
+ if ((s->type == SEG_BEZLIN || s->type == SEG_BEZTRK) && seg->bezSegs.cnt) {
+ s->u.b.angle0 = seg->u.b.angle0; //Copy all the rest
+ s->u.b.angle3 = seg->u.b.angle3;
+ s->u.b.length = seg->u.b.length;
+ s->u.b.minRadius = seg->u.b.minRadius;
+ for (int i=0;i<4;i++) s->u.b.pos[i] = seg->u.b.pos[i];
+ s->u.b.radius0 = seg->u.b.radius3;
+ s->bezSegs.cnt = 0;
+ if (s->bezSegs.ptr) MyFree(s->bezSegs.ptr);
+ s->bezSegs.max = 0;
+ s->bezSegs.ptr = NULL; //Make sure new space as addr copied in earlier from seg
+ for (int i = 0; i<seg->bezSegs.cnt; i++) {
+ addSegBezier(&s->bezSegs,(((trkSeg_p)seg->bezSegs.ptr)+i)); //recurse for copying embedded Beziers as in Cornu joint
+ }
+ } else {
+ s->u = seg->u;
+ }
+}
+
+enum BezierType {PLAIN, LOOP, CUSP, INFLECTION, DOUBLEINFLECTION, LINE, ENDS, COINCIDENT } bType;
+
+/*
+ * Analyse Bezier.
+ *
+ * Using results from Maureen C. Stone of XeroxParc and Tony deRose of U of Washington
+ * characterise the curve type and find out what features it has.
+ * We will eliminate cusps and loops as not useful forms. Line, Plain, Inflection and DoubleInflection are ok.
+ *
+ */
+EXPORT enum BezierType AnalyseCurve(coOrd inpos[4], double *Rfx, double *Rfy, double *cusp) {
+
+ *Rfx = *Rfy = 0;
+ if (Da.track && inpos[0].x == inpos[3].x && inpos[0].y == inpos[3].y ) {
+ return ENDS;
+ }
+
+ DIST_T d01 = FindDistance(inpos[0],inpos[1]);
+ DIST_T d12 = FindDistance(inpos[1],inpos[2]);
+ DIST_T d02 = FindDistance(inpos[0],inpos[2]);
+ if (d01+d12 == d02) { //straight
+ DIST_T d23 = FindDistance(inpos[2],inpos[3]);
+ DIST_T d03 = FindDistance(inpos[0],inpos[3]);
+ if (d02+d23 == d03) return LINE;
+ }
+ int common_points = 0;
+ for (int i=0;i<3;i++) {
+ if (inpos[i].x == inpos[i+1].x && inpos[i].y == inpos[i+1].y) common_points++;
+ }
+ for (int i=0;i<2;i++) {
+ if (inpos[i].x == inpos[i+2].x && inpos[i].y == inpos[i+2].y) common_points++;
+ }
+
+ if (common_points>2) {
+ return COINCIDENT;
+ }
+
+ coOrd pos[4];
+ coOrd offset2, offset = inpos[0];
+
+ for (int i=0;i<4;i++) { //move to zero origin
+ pos[i].x = inpos[i].x-offset.x;
+ pos[i].y = inpos[i].y-offset.y;
+ }
+
+ offset2.x = -offset.x + pos[3].x;
+ offset2.y = -offset.y + pos[3].y;
+ if (pos[1].y == 0.0) { //flip order of points
+ for (int i=0;i<4;i++) {
+ coOrd temp_pos = pos[i];
+ pos[i].x = pos[3-i].x - offset2.x;
+ pos[i].y = pos[3-i].y - offset2.y;
+ pos[3-i] = temp_pos;
+ }
+ if (pos[1].y == 0.0) { //Both ways round the second point has no y left after translation
+ return PLAIN;
+ }
+ }
+ double f21 = (pos[2].y)/(pos[1].y);
+ double f31 = (pos[3].y)/(pos[1].y);
+ if (fabs(pos[2].x-(pos[1].x*f21)) <0.0001) return PLAIN; //defend against divide by zero
+ double fx = (pos[3].x-(pos[1].x*f31))/(pos[2].x-(pos[1].x*f21));
+ double fy = f31+(1-f21)*fx;
+ *Rfx = fx;
+ *Rfy = fy;
+ *cusp = fabs(fy - (-(fx*fx)+2*fx+3)/4);
+
+ if (fy > 1.0) return INFLECTION;
+ if (fx >= 1.0) return PLAIN;
+ if (fabs(fy - (-(fx*fx)+2*fx+3)/4) <0.100) return CUSP;
+ if (fy < (-(fx*fx)+2*fx+3)/4) {
+ if (fx <= 0.0 && fy >= (3*fx-(fx*fx))/3) return LOOP;
+ if (fx > 0.0 && fy >= (sqrt(3*(4*fx-fx*fx))-fx)/2) return LOOP;
+ return PLAIN;
+ }
+
+ return DOUBLEINFLECTION;
+}
+
+/*
+ * ConvertToArcs
+ * Take a Bezier curve and turn it into a set of circular arcs, such that the error between the arc and the
+ * Bezier is under 0.5 pixels at maxiumum zoom.
+ *
+ * This enables us to use normal methods (operating over the array of arcs)
+ * to perform actions on the Bezier and also to export it to DXF.
+ *
+ */
+EXPORT BOOL_T ConvertToArcs (coOrd pos[4], dynArr_t * segs, BOOL_T track, wDrawColor color, DIST_T width) {
+ double t_s = 0.0, t_e = 1.0;
+ double errorThreshold = 0.05;
+ bCurveData_t prev_arc;
+ prev_arc.end = 0.0;
+ bCurveData_t arc;
+ segs->cnt = 0; //wipe out
+ BOOL_T safety;
+ int col = 0;
+
+ double prev_e = 0.0;
+ // we do a binary search to find the "good `t` closest to no-longer-good"
+ do {
+ safety=FALSE;
+ // step 1: start with the maximum possible arc length
+ t_e = 1.0;
+ // points:
+ coOrd start_point, mid_point, end_point;
+ // booleans:
+ BOOL_T curr_good = FALSE, prev_good = FALSE, done = FALSE;
+ // numbers:
+ double t_m, step = 0;
+ // step 2: find the best possible arc
+ do { // !done
+ prev_good = curr_good; //remember last time
+ t_m = (t_s + t_e)/2;
+ step++;
+ start_point = getPoint(pos, t_s); //Start of arc
+ mid_point = getPoint(pos, t_m); //Middle of trial arc
+ end_point = getPoint(pos, t_e); //End of trial Arc
+
+ PlotCurve( crvCmdFromChord, start_point, end_point, mid_point,
+ &(arc.curveData), TRUE ); //Find Arc through three points
+
+ arc.start = t_s; //remember start
+ arc.end = t_e; //remember end
+ arc.pos0 = start_point; //remember start point (used for Straight)
+ arc.pos1 = end_point; // Remember end point (used for Straight)
+
+ if (arc.curveData.type == curveTypeStraight) {
+ double error = BezErrorLine(pos,start_point,end_point, t_s, t_e);
+ curr_good = (error <= errorThreshold/2);
+ arc.curveData.a0 = FindAngle(start_point,end_point);
+ arc.curveData.a1 = FindAngle(end_point,start_point);
+
+ } else if (arc.curveData.type == curveTypeNone) {
+ return FALSE; //Something wrong
+ } else {
+ double error = BezError(pos, arc.curveData.curvePos, start_point, t_s, t_e);
+ curr_good = (error <= errorThreshold/2);
+ };
+
+ done = prev_good && !curr_good; //Was better than this last time?
+ if(!done) {
+ // this arc is fine: we can move 'e' up to see if we can find a wider arc
+ if(curr_good) {
+ prev_e = t_e; //remember good end only
+ prev_arc = arc;
+ // if e is already at max, then we're done for this arc.
+ if (t_e >= 1.0) {
+ // make sure we cap at t=1
+ arc.end = prev_e = 1.0;
+ // if we capped the arc segment to t=1 we also need to make sure that
+ // the arc's end angle is correct with respect to the bezier end point.
+ if (t_e > 1.0) {
+ if (arc.curveData.type != curveTypeStraight) {
+ coOrd d;
+ d.x = arc.curveData.curvePos.x + fabs(arc.curveData.curveRadius) * cos(D2R(arc.curveData.a1));
+ d.y = arc.curveData.curvePos.y + fabs(arc.curveData.curveRadius) * sin(D2R(arc.curveData.a1));
+
+ arc.curveData.a1 += FindAngle(d, getPoint(pos,1.0));
+ t_e = 1.0;
+ }
+ }
+ prev_arc = arc;
+ done = TRUE;
+ break;
+ }
+ // if not, move it up by half the iteration distance or to end
+ t_e = t_e + (t_e-t_s)/2;
+ if (t_e > 1.0) t_e = 1.0;
+ }
+ // this is a bad arc: we need to move 'e' down to find a good arc
+ else {
+ t_e = t_m;
+ }
+ } // If !Done end
+ } while(!done && safety++<100);
+ if(safety>=100) {
+ return FALSE; //Failed to make into arcs
+ }
+ prev_arc = prev_arc.end==0.0?arc:prev_arc;
+ trkSeg_t curveSeg; //Now set up tempSeg to copy into array
+ curveSeg.width = track?0:width;
+ if ( prev_arc.curveData.type == curveTypeCurve ) {
+ if (track)
+ curveSeg.color = (fabs(prev_arc.curveData.curveRadius)<(GetLayoutMinTrackRadius()-EPSILON))?wDrawColorRed:wDrawColorBlack;
+ else
+ curveSeg.color = color;
+ curveSeg.type = track?SEG_CRVTRK:SEG_CRVLIN;
+ curveSeg.u.c.a0 = prev_arc.curveData.a0;
+ curveSeg.u.c.a1 = prev_arc.curveData.a1;
+ curveSeg.u.c.center = prev_arc.curveData.curvePos;
+ if (prev_arc.curveData.negative)
+ curveSeg.u.c.radius = -prev_arc.curveData.curveRadius;
+ else
+ curveSeg.u.c.radius = prev_arc.curveData.curveRadius;
+ } else { //Straight Line because all points co-linear
+ curveSeg.type = track?SEG_STRTRK:SEG_STRLIN;
+ if (track)
+ curveSeg.color = wDrawColorBlack;
+ else
+ curveSeg.color = color;
+ curveSeg.u.l.angle = prev_arc.curveData.a1;
+ curveSeg.u.l.pos[0] = prev_arc.pos0;
+ curveSeg.u.l.pos[1] = prev_arc.pos1;
+ curveSeg.u.l.option = 0;
+ }
+ addSegBezier(segs, &curveSeg); //Add to array of segs used
+ t_s = prev_e;
+ col++;
+ } while(prev_e < 1.0);
+
+ return TRUE;
+};
+/*
+ * Draw Bezier while editing it. It consists of three elements - the curve and one or two control arms.
+ *
+ */
+
+EXPORT void DrawBezCurve(trkSeg_p control_arm1,
+ int cp1Segs_cnt,
+ trkSeg_p control_arm2,
+ int cp2Segs_cnt,
+ trkSeg_p curveSegs,
+ int crvSegs_cnt,
+ wDrawColor color
+ ) {
+ long oldDrawOptions = tempD.funcs->options;
+ tempD.funcs->options = wDrawOptTemp;
+ long oldOptions = tempD.options;
+ tempD.options = DC_TICKS;
+ tempD.orig = mainD.orig;
+ tempD.angle = mainD.angle;
+ if (crvSegs_cnt && curveSegs)
+ DrawSegs( &tempD, zero, 0.0, curveSegs, crvSegs_cnt, trackGauge, color );
+ if (cp1Segs_cnt && control_arm1)
+ DrawSegs( &tempD, zero, 0.0, control_arm1, cp1Segs_cnt, trackGauge, drawColorBlack );
+ if (cp2Segs_cnt && control_arm2)
+ DrawSegs( &tempD, zero, 0.0, control_arm2, cp2Segs_cnt, trackGauge, drawColorBlack );
+ tempD.funcs->options = oldDrawOptions;
+ tempD.options = oldOptions;
+
+}
+
+/*
+ * If Track, make it red if the radius is below minimum
+ */
+void DrawTempBezier(BOOL_T track) {
+ if (track) DrawBezCurve(Da.cp1Segs_da,Da.cp1Segs_da_cnt,Da.cp2Segs_da,Da.cp2Segs_da_cnt, (trkSeg_t *)Da.crvSegs_da.ptr,Da.crvSegs_da_cnt,Da.minRadius<(GetLayoutMinTrackRadius()-EPSILON)?drawColorRed:drawColorBlack);
+ else
+ DrawBezCurve(Da.cp1Segs_da,Da.cp1Segs_da_cnt,Da.cp2Segs_da,Da.cp2Segs_da_cnt, (trkSeg_t *)Da.crvSegs_da.ptr,Da.crvSegs_da_cnt,drawColorBlack); //Add Second Arm
+}
+
+void CreateBothControlArms(int selectPoint, BOOL_T track) {
+ if (selectPoint == -1) {
+ Da.cp1Segs_da_cnt = createControlArm(Da.cp1Segs_da, Da.pos[0],
+ Da.pos[1], track, TRUE, Da.trk[0]!=NULL, -1,
+ drawColorBlack);
+ Da.cp2Segs_da_cnt = createControlArm(Da.cp2Segs_da, Da.pos[3],
+ Da.pos[2], track, TRUE, Da.trk[1]!=NULL, -1,
+ drawColorBlack);
+ } else if (selectPoint == 0 || selectPoint == 1) {
+ Da.cp1Segs_da_cnt = createControlArm(Da.cp1Segs_da, Da.pos[0],
+ Da.pos[1], track, TRUE, Da.trk[0]!=NULL, selectPoint,
+ drawColorBlack);
+ Da.cp2Segs_da_cnt = createControlArm(Da.cp2Segs_da, Da.pos[3],
+ Da.pos[2], track, FALSE, Da.trk[1]!=NULL, -1,
+ drawColorBlack);
+ } else {
+ Da.cp1Segs_da_cnt = createControlArm(Da.cp1Segs_da, Da.pos[0],
+ Da.pos[1], track, FALSE, Da.trk[0]!=NULL, -1,
+ drawColorBlack);
+ Da.cp2Segs_da_cnt = createControlArm(Da.cp2Segs_da, Da.pos[3],
+ Da.pos[2], track, TRUE, Da.trk[1]!=NULL,
+ 3-selectPoint, drawColorBlack);
+ }
+}
+
+/*
+ * AdjustBezCurve
+ *
+ * Called to adjust the curve either when creating it or modifying it
+ * States are "PICK_POINT" and "POINT_PICKED" and "TRACK_SELECTED".
+ *
+ * In PICK_POINT, the user can select an end-point to drag and release in POINT_PICKED. They can also
+ * hit Enter (which saves the changes) or ESC (which cancels them).
+ *
+ * Only those points which can be picked are shown with circles - locked end-points are not shown.
+ *
+ * SHIFT at release will lock , re-locking any end-points that are aligned with like items at the same position
+ * (Track to unconnected Track, Line to any Line end).
+ *
+ */
+EXPORT STATUS_T AdjustBezCurve(
+ wAction_t action,
+ coOrd pos,
+ BOOL_T track,
+ wDrawColor color,
+ DIST_T width,
+ bezMessageProc message )
+{
+ track_p t;
+ DIST_T d;
+ ANGLE_T angle1, angle2;
+ static coOrd pos0, pos3, p;
+ enum BezierType b;
+ DIST_T dd;
+ EPINX_T ep;
+ double fx, fy, cusp;
+ int controlArm = -1;
+
+
+ if (Da.state != PICK_POINT && Da.state != POINT_PICKED && Da.state != TRACK_SELECTED) return C_CONTINUE;
+
+ switch ( action & 0xFF) {
+
+ case C_START:
+ Da.selectPoint = -1;
+ CreateBothControlArms(Da.selectPoint, track);
+ if (ConvertToArcs(Da.pos,&Da.crvSegs_da,track,color,Da.width)) Da.crvSegs_da_cnt = Da.crvSegs_da.cnt;
+ Da.minRadius = BezierMinRadius(Da.pos,Da.crvSegs_da);
+ Da.unlocked = FALSE;
+ if (track)
+ InfoMessage( _("Select End-Point - Ctrl unlocks end-point") );
+ else
+ InfoMessage( _("Select End-Point") );
+ DrawTempBezier(Da.track);
+ return C_CONTINUE;
+
+ case C_DOWN:
+ if (Da.state != PICK_POINT) return C_CONTINUE;
+ dd = 10000.0;
+ Da.selectPoint = -1;
+ DrawTempBezier(Da.track); //wipe out
+ for (int i=0;i<4;i++) {
+ d = FindDistance(Da.pos[i],pos);
+ if (d < dd) {
+ if (i==0 && Da.trk[0]) continue;
+ if (i==3 && Da.trk[1]) continue; //ignore locked points
+ dd = d;
+ Da.selectPoint = i;
+ }
+
+ }
+ if (!IsClose(dd) ) Da.selectPoint = -1;
+ if (Da.selectPoint == -1) {
+ InfoMessage( _("Not close enough to any valid, selectable point, reselect") );
+ DrawTempBezier(Da.track);
+ return C_CONTINUE;
+ } else {
+ pos = Da.pos[Da.selectPoint];
+ Da.state = POINT_PICKED;
+ InfoMessage( _("Drag point %d to new location and release it"),Da.selectPoint+1 );
+ }
+ CreateBothControlArms(Da.selectPoint, track);
+ if (ConvertToArcs(Da.pos, &Da.crvSegs_da, track, color,Da.width)) Da.crvSegs_da_cnt = Da.crvSegs_da.cnt;
+ Da.minRadius = BezierMinRadius(Da.pos, Da.crvSegs_da);
+ DrawTempBezier(Da.track);
+ return C_CONTINUE;
+
+ case C_MOVE:
+ if (Da.state != POINT_PICKED) {
+ InfoMessage(_("Pick any circle to adjust it - Enter to confirm, ESC to abort"));
+ return C_CONTINUE;
+ }
+ //If locked, reset pos to be on line from other track
+ DrawTempBezier(Da.track); //wipe out
+ if (Da.selectPoint == 1 || Da.selectPoint == 2) { //CPs
+ int controlArm = Da.selectPoint-1; //Snap to direction of track
+ if (Da.trk[controlArm]) {
+ angle1 = NormalizeAngle(GetTrkEndAngle(Da.trk[controlArm], Da.ep[controlArm]));
+ angle2 = NormalizeAngle(FindAngle(pos, Da.pos[Da.selectPoint==1?0:3])-angle1);
+ if (angle2 > 90.0 && angle2 < 270.0)
+ Translate( &pos, Da.pos[Da.selectPoint==1?0:3], angle1, -FindDistance( Da.pos[Da.selectPoint==1?0:3], pos )*cos(D2R(angle2)) );
+ else pos = Da.pos[Da.selectPoint==1?0:3];
+ } // Dont Snap control points
+ } else SnapPos(&pos);
+ Da.pos[Da.selectPoint] = pos;
+ CreateBothControlArms(Da.selectPoint, track);
+ if (ConvertToArcs(Da.pos,&Da.crvSegs_da,track, color, Da.width)) Da.crvSegs_da_cnt = Da.crvSegs_da.cnt;
+ Da.minRadius = BezierMinRadius(Da.pos,Da.crvSegs_da);
+ if (Da.track) {
+ b = AnalyseCurve(Da.pos,&fx,&fy,&cusp);
+ if (b==ENDS) {
+ wBeep();
+ InfoMessage(_("Bezier Curve Invalid has identical end points Change End Point"),b==CUSP?"Cusp":"Loop");
+ } else if ( b == CUSP || b == LOOP) {
+ wBeep();
+ InfoMessage(_("Bezier Curve Invalid has %s Change End Point"),b==CUSP?"Cusp":"Loop");
+ } else if ( b == COINCIDENT ) {
+ wBeep();
+ InfoMessage(_("Bezier Curve Invalid has three co-incident points"),b==CUSP?"Cusp":"Loop");
+ } else if ( b == LINE ) {
+ InfoMessage(_("Bezier is Straight Line"));
+ } else
+ InfoMessage( _("Bezier %s : Min Radius=%s Length=%s fx=%0.3f fy=%0.3f cusp=%0.3f"),track?"Track":"Line",
+ FormatDistance(Da.minRadius>=100000?0:Da.minRadius),
+ FormatDistance(BezierLength(Da.pos,Da.crvSegs_da)),fx,fy,cusp);
+ } else
+ InfoMessage( _("Bezier %s : Min Radius=%s Length=%s"),track?"Track":"Line",
+ FormatDistance(Da.minRadius>=100000?0:Da.minRadius),
+ FormatDistance(BezierLength(Da.pos,Da.crvSegs_da)));
+ DrawTempBezier(Da.track);
+ return C_CONTINUE;
+
+ case C_UP:
+ if (Da.state != POINT_PICKED) return C_CONTINUE;
+ //Take last pos and decide if it should be snapped to a track because SHIFT is held (pos0 and pos3)
+ ep = 0;
+ BOOL_T found = FALSE;
+
+ DrawTempBezier(Da.track); //wipe out
+
+ p = pos;
+
+ if (track && (Da.selectPoint == 0 || Da.selectPoint == 3)) { //EPs
+ if ((MyGetKeyState() & WKEY_SHIFT) != 0) { //Snap Track
+ if ((t = OnTrackIgnore(&p, FALSE, TRUE, Da.selectTrack)) != NULL) { //Snap to endPoint
+ ep = PickUnconnectedEndPointSilent(p, t);
+ if (ep != -1) {
+ Da.trk[Da.selectPoint/3] = t;
+ Da.ep[Da.selectPoint/3] = ep;
+ pos0 = Da.pos[(Da.selectPoint == 0)?1:2];
+ pos = GetTrkEndPos(t, ep);
+ found = TRUE;
+ }
+ } else {
+ wBeep();
+ InfoMessage(_("No unconnected End Point to lock to"));
+ }
+ }
+ }
+ if (found) {
+ angle1 = NormalizeAngle(GetTrkEndAngle(Da.trk[Da.selectPoint/3], Da.ep[Da.selectPoint/3]));
+ angle2 = NormalizeAngle(FindAngle(pos, pos0)-angle1);
+ Translate(&Da.pos[Da.selectPoint==0?1:2], Da.pos[Da.selectPoint==0?0:3], angle1, FindDistance(Da.pos[Da.selectPoint==0?1:2],pos)*cos(D2R(angle2)));
+ }
+ Da.selectPoint = -1;
+ CreateBothControlArms(Da.selectPoint,track);
+ if (ConvertToArcs(Da.pos,&Da.crvSegs_da,track,color,Da.width)) Da.crvSegs_da_cnt = Da.crvSegs_da.cnt;
+ Da.minRadius = BezierMinRadius(Da.pos,Da.crvSegs_da);
+ if (Da.track) {
+ b = AnalyseCurve(Da.pos,&fx,&fy,&cusp);
+ if (b==ENDS) {
+ wBeep();
+ InfoMessage(_("Bezier curve invalid has identical end points Change End Point"),b==CUSP?"Cusp":"Loop");
+ } else if ( b == CUSP || b == LOOP) {
+ wBeep();
+ InfoMessage(_("Bezier curve invalid has %s Change End Point"),b==CUSP?"Cusp":"Loop");
+ } else if ( b == COINCIDENT ) {
+ wBeep();
+ InfoMessage(_("Bezier curve invalid has three co-incident points"),b==CUSP?"Cusp":"Loop");
+ } else if ( b == LINE) {
+ InfoMessage(_("Bezier curve is straight line"));
+ }
+ InfoMessage(_("Pick any circle to adjust it - Enter to confirm, ESC to abort"));
+ } else
+ InfoMessage(_("Pick any circle to adjust it - Enter to confirm, ESC to abort"));
+ DrawTempBezier(Da.track);
+ Da.state = PICK_POINT;
+
+ return C_CONTINUE;
+
+ case C_OK: //C_OK is not called by Modify.
+ if ( Da.state == PICK_POINT ) {
+ char c = (unsigned char)(action >> 8);
+ if (Da.track && Da.pos[0].x == Da.pos[3].x && Da.pos[0].y == Da.pos[3].y ) {
+ wBeep();
+ ErrorMessage(_("Invalid Bezier Track - end points are identical"));
+ return C_CONTINUE;
+ }
+ if (Da.track) {
+ b = AnalyseCurve(Da.pos,&fx,&fy,&cusp);
+ if ( b == CUSP || b == LOOP ) {
+ wBeep();
+ ErrorMessage(_("Invalid Bezier Curve has a %s - Adjust"),b==CUSP?"Cusp":"Loop");
+ return C_CONTINUE;
+ } else if (b==COINCIDENT) {
+ wBeep();
+ ErrorMessage(_("Invalid Bezier Curve has three coincident points - Adjust"));
+ return C_CONTINUE;
+ } else if(b==ENDS) {
+ ErrorMessage(_("Invalid Bezier Track - end points are identical"));
+ return C_CONTINUE;
+ }
+ }
+ Da.minRadius = BezierMinRadius(Da.pos,Da.crvSegs_da);
+ DrawTempBezier(Da.track);
+ UndoStart( _("Create Bezier"), "newBezier - CR" );
+ if (Da.track) {
+ t = NewBezierTrack( Da.pos, (trkSeg_p)Da.crvSegs_da.ptr, Da.crvSegs_da.cnt);
+ for (int i=0;i<2;i++)
+ if (Da.trk[i] != NULL) ConnectAbuttingTracks(t,i,Da.trk[i],Da.ep[i]);
+ }
+ else t = NewBezierLine(Da.pos, (trkSeg_p)Da.crvSegs_da.ptr, Da.crvSegs_da.cnt,color,width);
+ UndoEnd();
+ if (Da.crvSegs_da.ptr) MyFree(Da.crvSegs_da.ptr);
+ Da.crvSegs_da.ptr = NULL;
+ Da.crvSegs_da.cnt = 0;
+ Da.crvSegs_da.max = 0;
+ DrawNewTrack(t);
+ Da.state = NONE;
+ MainRedraw();
+ MapRedraw();
+ return C_TERMINATE;
+
+ }
+ return C_CONTINUE;
+
+ case C_REDRAW:
+ DrawTempBezier(Da.track);
+ return C_CONTINUE;
+
+ default:
+ return C_CONTINUE;
+ }
+
+
+}
+
+struct extraData {
+ BezierData_t bezierData;
+ };
+
+/*
+ * CmdBezModify
+ *
+ * Called from Modify Command - this function deals with the real (old) track and calls AdjustBezCurve to tune up the new one
+ * Sequence is this -
+ * - The C_START is called from CmdModify C_DOWN action if a track has being selected. The old track is hidden, the editable one is shown.
+ * - C_MOVES will be ignored until a C_UP ends the track selection and moves the state to PICK_POINT,
+ * - C_DOWN then hides the track and shows the Bezier handles version. Selects a point (if close enough and available) and the state moves to POINT_PICKED
+ * - C_MOVE drags the point around modifying the curve
+ * - C_UP puts the state back to PICK_POINT (pick another)
+ * - C_OK (Enter/Space) creates the new track, deletes the old and shows the changed track.
+ * - C_CANCEL (Esc) sets the state to NONE and reshows the original track unchanged.
+ *
+ * Note: Available points are shown - if a Bezier track is attached to its neighbor, only the control point on that side is selectable.
+ * Any free end-point can be locked to a unconnected end point using SHIFT during drag.
+ */
+STATUS_T CmdBezModify (track_p trk, wAction_t action, coOrd pos) {
+ BOOL_T track = TRUE;
+ track_p t;
+ double width = 1.0;
+ long mode = 0;
+ long cmd;
+
+ struct extraData *xx = GetTrkExtraData(trk);
+ cmd = (long)commandContext;
+
+
+ switch (action&0xFF) {
+ case C_START:
+ Da.state = NONE;
+ DYNARR_RESET(trkSeg_t,Da.crvSegs_da);
+ Da.cp1Segs_da_cnt = 0;
+ Da.cp2Segs_da_cnt = 0;
+ Da.selectPoint = -1;
+ Da.selectTrack = NULL;
+
+ if (IsTrack(trk)) Da.track = TRUE;
+ else Da.track = FALSE;
+
+ Da.selectTrack = trk;
+ Da.trk[0] = GetTrkEndTrk( trk, 0 );
+ if (Da.trk[0]) Da.ep[0] = GetEndPtConnectedToMe(Da.trk[0],trk);
+ Da.trk[1] = GetTrkEndTrk( trk, 1 );
+ if (Da.trk[1]) Da.ep[1] = GetEndPtConnectedToMe(Da.trk[1],trk);
+
+ for (int i=0;i<4;i++) Da.pos[i] = xx->bezierData.pos[i]; //Copy parms from old trk
+ InfoMessage(_("%s picked - now select a Point"),track?"Track":"Line");
+ Da.state = TRACK_SELECTED;
+ DrawTrack(Da.selectTrack,&mainD,wDrawColorWhite); //Wipe out real track, draw replacement
+ return AdjustBezCurve(C_START, pos, Da.track, xx->bezierData.segsColor, xx->bezierData.segsWidth, InfoMessage);
+
+ case C_DOWN:
+ if (Da.state == TRACK_SELECTED) return C_CONTINUE; //Ignore until first up
+ return AdjustBezCurve(C_DOWN, pos, Da.track, xx->bezierData.segsColor, xx->bezierData.segsWidth, InfoMessage);
+
+
+ case C_MOVE:
+ if (Da.state == TRACK_SELECTED) return C_CONTINUE; //Ignore until first up and down
+ return AdjustBezCurve(C_MOVE, pos, Da.track, xx->bezierData.segsColor, xx->bezierData.segsWidth, InfoMessage);
+
+ case C_UP:
+ if (Da.state == TRACK_SELECTED) {
+ Da.state = PICK_POINT; //First time up, next time pick a point
+ }
+ return AdjustBezCurve(C_UP, pos, Da.track, xx->bezierData.segsColor, xx->bezierData.segsWidth, InfoMessage); //Run Adjust
+
+ case C_TEXT:
+ if ((action>>8) != 32)
+ return C_CONTINUE;
+ /* no break */
+ case C_OK:
+ if (Da.state != PICK_POINT) { //Too early - abandon
+ InfoMessage(_("No changes made"));
+ Da.state = NONE;
+ return C_CANCEL;
+ }
+ UndoStart( _("Modify Bezier"), "newBezier - CR" );
+ if (Da.track) t = NewBezierTrack( Da.pos, (trkSeg_p)Da.crvSegs_da.ptr, Da.crvSegs_da.cnt);
+ else t = NewBezierLine(Da.pos, (trkSeg_p)Da.crvSegs_da.ptr, Da.crvSegs_da.cnt,xx->bezierData.segsColor,xx->bezierData.segsWidth);
+
+ DeleteTrack(trk, TRUE);
+
+ if (Da.track) {
+ for (int i=0;i<2;i++) { //Attach new track
+ if (Da.trk[i] != NULL && Da.ep[i] != -1) { //Like the old track
+ ConnectAbuttingTracks(t,i,Da.trk[i],Da.ep[i]);
+ }
+ }
+ }
+ UndoEnd();
+ InfoMessage(_("Modify Bezier Complete - select another"));
+ Da.state = NONE;
+ return C_TERMINATE;
+
+ case C_CANCEL:
+ InfoMessage(_("Modify Bezier Cancelled"));
+ Da.state = NONE;
+ MainRedraw();
+ MapRedraw();
+ return C_TERMINATE;
+
+ case C_REDRAW:
+ return AdjustBezCurve(C_REDRAW, pos, Da.track, xx->bezierData.segsColor, xx->bezierData.segsWidth, InfoMessage);
+ }
+
+ return C_CONTINUE;
+
+}
+
+/*
+ * Find length by adding up the underlying segments. The segments can be straights, curves or bezier.
+ */
+DIST_T BezierLength(coOrd pos[4],dynArr_t segs) {
+
+ DIST_T dd = 0.0;
+ if (segs.cnt == 0 ) return dd;
+ for (int i = 0;i<segs.cnt;i++) {
+ trkSeg_t t = DYNARR_N(trkSeg_t, segs, i);
+ if (t.type == SEG_CRVTRK || t.type == SEG_CRVLIN) {
+ dd += fabs(t.u.c.radius*D2R(t.u.c.a1));
+ } else if (t.type == SEG_BEZLIN || t.type == SEG_BEZTRK) {
+ dd +=BezierLength(t.u.b.pos,t.bezSegs);
+ } else if (t.type == SEG_STRLIN || t.type == SEG_STRTRK ) {
+ dd += FindDistance(t.u.l.pos[0],t.u.l.pos[1]);
+ }
+ }
+ return dd;
+}
+
+DIST_T BezierMinRadius(coOrd pos[4],dynArr_t segs) {
+ DIST_T r = 100000.0, rr;
+ if (segs.cnt == 0 ) return r;
+ for (int i = 0;i<segs.cnt;i++) {
+ trkSeg_t t = DYNARR_N(trkSeg_t, segs, i);
+ if (t.type == SEG_CRVTRK || t.type == SEG_CRVLIN) {
+ rr = fabs(t.u.c.radius);
+ } else if (t.type == SEG_BEZLIN || t.type == SEG_BEZTRK) {
+ rr = BezierMinRadius(t.u.b.pos, t.bezSegs);
+ } else rr = 100000.00;
+ if (rr<r) r = rr;
+ }
+ return r;
+}
+
+/*
+ * Create a Bezier Curve (Track or Line)
+ * Sequence is
+ * 1. Place 1st End (snapping if needed)
+ * 2.Drag out 1st Control Arm (in one direction if snapped)
+ * 3.Place 2nd End (again snapping)
+ * 4.Drag out 2nd Control Arm (constrained if snapped)
+ * 5 to n. Select and drag around points until done
+ * n+1. Confirm with enter or Cancel with Esc
+ */
+STATUS_T CmdBezCurve( wAction_t action, coOrd pos )
+{
+ track_p t;
+ static int segCnt;
+ STATUS_T rc = C_CONTINUE;
+ long curveMode = 0;
+ long cmd;
+ if (action>>8) {
+ cmd = action>>8;
+ } else cmd = (long)commandContext;
+
+ Da.color = lineColor;
+ Da.width = (double)lineWidth/mainD.dpi;
+
+ switch (action&0xFF) {
+
+ case C_START:
+
+ Da.track = (cmd == bezCmdModifyTrack || cmd == bezCmdCreateTrack)?TRUE:FALSE;
+
+ Da.state = POS_1;
+ Da. selectPoint = -1;
+ for (int i=0;i<4;i++) {
+ Da.pos[i] = zero;
+ }
+ Da.trk[0] = Da.trk[1] = NULL;
+ //tempD.orig = mainD.orig;
+
+ DYNARR_RESET(trkSeg_t,Da.crvSegs_da);
+ Da.cp1Segs_da_cnt = 0;
+ Da.cp2Segs_da_cnt = 0;
+ InfoMessage( _("Place 1st end point of Bezier + Shift -> snap to %s end"), Da.track?"Unconnected Track":"Line" );
+ return C_CONTINUE;
+
+
+ case C_DOWN:
+ if ( Da.state == POS_1 || Da.state == POS_2) { //Set the first or third point
+ coOrd p = pos;
+ BOOL_T found = FALSE;
+ int end = Da.state==POS_1?0:1;
+ EPINX_T ep;
+ if (Da.track) {
+ if ((MyGetKeyState() & WKEY_SHIFT) != 0) { //Snap Track
+ if ((t = OnTrack(&p, FALSE, TRUE)) != NULL) {
+ ep = PickUnconnectedEndPointSilent(p, t);
+ if (ep != -1) {
+ Da.trk[end] = t;
+ Da.ep[end] = ep;
+ pos = GetTrkEndPos(t, ep);
+ found = TRUE;
+ }
+ }
+ if (!found) {
+ wBeep();
+ InfoMessage(_("Shift used, but no Unconnected Track End there"));
+ return C_CONTINUE;
+ }
+ }
+ } else { //Snap Bez Line to Lines
+ if ((MyGetKeyState() & WKEY_SHIFT) != 0) {
+ if ((t = OnTrack(&p,FALSE, FALSE)) != NULL) {
+ if (GetClosestEndPt(t,&p)) {
+ pos = p;
+ found = TRUE;
+ }
+ } else {
+ wBeep();
+ InfoMessage(_("Shift used, but no Line End there"));
+ return C_CONTINUE;
+ }
+ }
+ }
+ if (!found) SnapPos( &pos );
+ if (Da.state == POS_1) {
+ Da.pos[0] = pos;
+ Da.pos[1] = pos;
+ Da.state = CONTROL_ARM_1; //Draw the first control arm
+ Da.selectPoint = 1;
+ InfoMessage( _("Drag end of first Control Arm") );
+ Da.cp1Segs_da_cnt = createControlArm(Da.cp1Segs_da, Da.pos[0], Da.pos[1], Da.track,TRUE,Da.trk[1]!=NULL,1,wDrawColorBlack);
+ DrawBezCurve(Da.cp1Segs_da,Da.cp1Segs_da_cnt,NULL,0,NULL,0,drawColorBlack);
+ } else {
+ Da.pos[3] = pos; //2nd End Point
+ Da.pos[2] = pos; //2nd Ctl Point
+ Da.state = POINT_PICKED; // Drag out the second control arm
+ Da.selectPoint = 2;
+ InfoMessage( _("Drag end of second Control Arm") );
+ DrawBezCurve(Da.cp1Segs_da,Da.cp1Segs_da_cnt,NULL,0,NULL,0,drawColorBlack); //Wipe out initial Arm
+ Da.cp1Segs_da_cnt = createControlArm(Da.cp1Segs_da, Da.pos[0], Da.pos[1], Da.track,FALSE,Da.trk[0]!=NULL,-1,wDrawColorBlack);
+ Da.cp2Segs_da_cnt = createControlArm(Da.cp2Segs_da, Da.pos[3], Da.pos[2], Da.track,TRUE,Da.trk[1]!=NULL,1,wDrawColorBlack);
+ if (ConvertToArcs(Da.pos,&Da.crvSegs_da,Da.track,Da.color,Da.width)) Da.crvSegs_da_cnt = Da.crvSegs_da.cnt;
+ DrawTempBezier(Da.track);
+ }
+ return C_CONTINUE;
+ } else {
+ return AdjustBezCurve( action&0xFF, pos, Da.track, Da.color, Da.width, InfoMessage );
+ }
+ return C_CONTINUE;
+
+ case C_MOVE:
+ if (Da.state == POS_1) {
+ InfoMessage( _("Place 1st end point of Bezier + Shift -> snap to %s end"), Da.track?"Unconnected Track":"Line" );
+ return C_CONTINUE;
+ }
+ if (Da.state == POS_2) {
+ InfoMessage( _("Select other end of Bezier, +Shift -> snap to %s end"), Da.track?"Unconnected Track":"Line" );
+ }
+ if (Da.state == CONTROL_ARM_1 ) {
+ DrawBezCurve(Da.cp1Segs_da,Da.cp1Segs_da_cnt,NULL,0,NULL,0,drawColorBlack);
+ if (Da.trk[0]) {
+ EPINX_T ep = 0;
+ ANGLE_T angle1,angle2;
+ angle1 = NormalizeAngle(GetTrkEndAngle(Da.trk[0],Da.ep[0]));
+ angle2 = NormalizeAngle(FindAngle(pos, Da.pos[0])-angle1);
+ if (angle2 > 90.0 && angle2 < 270.0)
+ Translate( &pos, Da.pos[0], angle1, -FindDistance( Da.pos[0], pos )*cos(D2R(angle2)));
+ else pos = Da.pos[0];
+ } // Don't Snap control points
+ Da.pos[1] = pos;
+ Da.cp1Segs_da_cnt = createControlArm(Da.cp1Segs_da, Da.pos[0], Da.pos[1], Da.track, TRUE, Da.trk[0]!=NULL, 1, wDrawColorBlack);
+ DrawBezCurve(Da.cp1Segs_da,Da.cp1Segs_da_cnt,NULL,0,NULL,0,drawColorBlack);
+ } else {
+ return AdjustBezCurve( action&0xFF, pos, Da.track, Da.color, Da.width, InfoMessage );
+ }
+ return C_CONTINUE;
+
+ case C_UP:
+ if (Da.state == CONTROL_ARM_1) {
+ DrawBezCurve(Da.cp1Segs_da,Da.cp1Segs_da_cnt,NULL,0,NULL,0,drawColorBlack);
+ if (Da.trk[0]) {
+ EPINX_T ep = Da.ep[0];
+ ANGLE_T angle1,angle2;
+ angle1 = NormalizeAngle(GetTrkEndAngle(Da.trk[0],Da.ep[0]));
+ angle2 = NormalizeAngle(FindAngle(pos, Da.pos[0])-angle1);
+ if (angle2 > 90.0 && angle2 < 270.0)
+ Translate( &pos, Da.pos[0], angle1, -FindDistance( Da.pos[0], pos )*cos(D2R(angle2)));
+ else pos = Da.pos[0];
+ } // Don't Snap control points
+ Da.pos[1] = pos;
+ if (FindDistance(Da.pos[0],Da.pos[1]) <=minLength) {
+ InfoMessage( _("Control Arm 1 is too short, try again") );
+ Da.state = POS_1;
+ return C_CONTINUE;
+ }
+ Da.state = POS_2;
+ InfoMessage( _("Select other end of Bezier, +Shift -> snap to %s end"), Da.track?"Unconnected Track":"Line" );
+ Da.cp1Segs_da_cnt = createControlArm(Da.cp1Segs_da, Da.pos[0], Da.pos[1], Da.track, FALSE, Da.trk[0]!=NULL, -1, wDrawColorBlack);
+ DrawBezCurve(Da.cp1Segs_da,Da.cp1Segs_da_cnt,NULL,0,NULL,0,drawColorBlack);
+ return C_CONTINUE;
+ } else {
+ return AdjustBezCurve( action&0xFF, pos, Da.track, Da.color, Da.width, InfoMessage );
+ }
+ case C_TEXT:
+ if (Da.state != PICK_POINT || (action>>8) != ' ') //Space is same as Enter.
+ return C_CONTINUE;
+ /* no break */
+ case C_OK:
+ if (Da.state != PICK_POINT) return C_CONTINUE;
+ return AdjustBezCurve( C_OK, pos, Da.track, Da.color, Da.width, InfoMessage);
+
+ case C_REDRAW:
+ if ( Da.state != NONE ) {
+
+ DrawBezCurve(Da.cp1Segs_da,Da.cp1Segs_da_cnt,Da.cp2Segs_da,Da.cp2Segs_da_cnt,(trkSeg_t *)Da.crvSegs_da.ptr,Da.crvSegs_da.cnt, Da.color);
+ }
+ return C_CONTINUE;
+
+ case C_CANCEL:
+ if (Da.state != NONE) {
+ DrawBezCurve(Da.cp1Segs_da,Da.cp1Segs_da_cnt,Da.cp2Segs_da,Da.cp2Segs_da_cnt,(trkSeg_t *)Da.crvSegs_da.ptr,Da.crvSegs_da.cnt, Da.color);
+ Da.cp1Segs_da_cnt = 0;
+ Da.cp2Segs_da_cnt = 0;
+ Da.crvSegs_da_cnt = 0;
+ for (int i=0;i<2;i++) {
+ Da.trk[i] = NULL;
+ Da.ep[i] = -1;
+ }
+ if (Da.crvSegs_da.ptr) MyFree(Da.crvSegs_da.ptr);
+ Da.crvSegs_da.ptr = NULL;
+ Da.crvSegs_da.cnt = 0;
+ Da.crvSegs_da.max = 0;
+ }
+ Da.state = NONE;
+ return C_CONTINUE;
+
+ default:
+
+ return C_CONTINUE;
+ }
+
+}
+
+void UpdateParms(wDrawColor color,long width) {
+ DrawTempBezier(Da.track);
+ Da.color = lineColor;
+ Da.width = (double)lineWidth/mainD.dpi;
+ if (Da.crvSegs_da.cnt) {
+ ConvertToArcs(Da.pos,&Da.crvSegs_da,Da.track,Da.color,Da.width);
+ }
+ DrawTempBezier(Da.track);
+}
+
+
+#include "bitmaps/bezier.xpm"
+#include "bitmaps/dbezier.xpm"
+
+EXPORT void InitCmdBezier( wMenu_p menu )
+{
+
+}
diff --git a/app/bin/cbezier.h b/app/bin/cbezier.h
new file mode 100644
index 0000000..8a8a8b0
--- /dev/null
+++ b/app/bin/cbezier.h
@@ -0,0 +1,53 @@
+/*
+ * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/cbezier.h,v 1.1 2005-12-07 15:47:36 rc-flyer Exp $
+ */
+
+/* XTrkCad - Model Railroad CAD
+ * Copyright (C) 2005 Dave Bullis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, 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 License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "common.h"
+#include "wlib.h"
+#include "utility.h"
+
+
+dynArr_t tempEndPts_da;
+#define BezSegs(N) DYNARR_N( trkEndPt_t, tempEndPts_da, N )
+
+#define bezCmdNone (0)
+#define bezCmdModifyTrack (1)
+#define bezCmdModifyLine (2)
+#define bezCmdCreateTrack (3)
+#define bezCmdCreateLine (4)
+
+extern wDrawColor lineColor;
+extern long lineWidth;
+
+typedef void (*bezMessageProc)( char *, ... );
+STATUS_T CmdBezCurve( wAction_t, coOrd);
+STATUS_T CmdBezModify(track_p, wAction_t, coOrd);
+
+STATUS_T CreateBezier( wAction_t, coOrd, BOOL_T, wDrawColor, DIST_T, long, bezMessageProc );
+DIST_T BezierDescriptionDistance( coOrd, track_p );
+STATUS_T BezierDescriptionMove( track_p, wAction_t, coOrd );
+BOOL_T GetBezierMiddle( track_p, coOrd * );
+BOOL_T ConvertToArcs (coOrd[4], dynArr_t *, BOOL_T, wDrawColor, DIST_T);
+track_p NewBezierTrack(coOrd[4], trkSeg_t *, int);
+double BezierLength(coOrd[4], dynArr_t);
+double BezierMinRadius(coOrd[4],dynArr_t);
+void UpdateParms(wDrawColor color,long width);
+
diff --git a/app/bin/cblock.c b/app/bin/cblock.c
index 3c627e9..b1b14a8 100644
--- a/app/bin/cblock.c
+++ b/app/bin/cblock.c
@@ -47,10 +47,19 @@
*/
#include <ctype.h>
-#include "track.h"
-#include "trackx.h"
+#include <stdlib.h>
+#include <string.h>
+
#include "compound.h"
+#include "cundo.h"
+#include "custom.h"
+#include "fileio.h"
#include "i18n.h"
+#include "messages.h"
+#include "param.h"
+#include "track.h"
+#include "trackx.h"
+#include "utility.h"
EXPORT TRKTYP_T T_BLOCK = -1;
@@ -151,8 +160,8 @@ static descData_t blockDesc[] = {
/*NM*/ { DESC_STRING, N_("Name"), &blockData.name },
/*SC*/ { DESC_STRING, N_("Script"), &blockData.script },
/*LN*/ { DESC_DIM, N_("Length"), &blockData.length },
-/*E0*/ { DESC_POS, N_("End Pt 1: X"), &blockData.endPt[0] },
-/*E1*/ { DESC_POS, N_("End Pt 2: X"), &blockData.endPt[1] },
+/*E0*/ { DESC_POS, N_("End Pt 1: X,Y"), &blockData.endPt[0] },
+/*E1*/ { DESC_POS, N_("End Pt 2: X,Y"), &blockData.endPt[1] },
{ DESC_NULL } };
static void UpdateBlock (track_p trk, int inx, descData_p descUpd, BOOL_T needUndoStart )
@@ -196,12 +205,18 @@ static DIST_T DistanceBlock (track_p t, coOrd * p )
blockData_p xx = GetblockData(t);
DIST_T closest, current;
int iTrk = 1;
-
- closest = GetTrkDistance ((&(xx->trackList))[0].t, *p);
+ coOrd pos = *p;
+ closest = GetTrkDistance ((&(xx->trackList))[0].t, &pos);
+ coOrd best_pos = pos;
for (; iTrk < xx->numTracks; iTrk++) {
- current = GetTrkDistance ((&(xx->trackList))[iTrk].t, *p);
- if (current < closest) closest = current;
+ pos = *p;
+ current = GetTrkDistance ((&(xx->trackList))[iTrk].t, &pos);
+ if (current < closest) {
+ closest = current;
+ best_pos = pos;
+ }
}
+ *p = best_pos;
return closest;
}
diff --git a/app/bin/ccontrol.c b/app/bin/ccontrol.c
index 9428e1a..8ff0396 100644
--- a/app/bin/ccontrol.c
+++ b/app/bin/ccontrol.c
@@ -47,10 +47,18 @@
static const char rcsid[] = "@(#) : $Id$";
#include <ctype.h>
-#include "track.h"
-#include "trackx.h"
+#include <string.h>
+
#include "compound.h"
+#include "cundo.h"
+#include "custom.h"
+#include "fileio.h"
#include "i18n.h"
+#include "layout.h"
+#include "param.h"
+#include "track.h"
+#include "trackx.h"
+#include "utility.h"
EXPORT TRKTYP_T T_CONTROL = -1;
@@ -476,21 +484,18 @@ static STATUS_T CmdControl ( wAction_t action, coOrd pos )
InfoMessage(_("Place control"));
return C_CONTINUE;
case C_DOWN:
- SnapPos(&pos);
- DDrawControl( &tempD, pos, GetScaleRatio(curScaleInx), wDrawColorBlack );
- return C_CONTINUE;
- case C_MOVE:
- SnapPos(&pos);
- DDrawControl( &tempD, pos, GetScaleRatio(curScaleInx), wDrawColorBlack );
+ case C_MOVE:
+ SnapPos(&pos);
+ DDrawControl( &tempD, pos, GetScaleRatio(GetLayoutCurScale()), wDrawColorBlack );
return C_CONTINUE;
case C_UP:
SnapPos(&pos);
- DDrawControl( &tempD, pos, GetScaleRatio(curScaleInx), wDrawColorBlack );
+ DDrawControl( &tempD, pos, GetScaleRatio(GetLayoutCurScale()), wDrawColorBlack );
CreateNewControl(pos);
return C_TERMINATE;
case C_REDRAW:
case C_CANCEL:
- DDrawControl( &tempD, pos, GetScaleRatio(curScaleInx), wDrawColorBlack );
+ DDrawControl( &tempD, pos, GetScaleRatio(GetLayoutCurScale()), wDrawColorBlack );
return C_CONTINUE;
default:
return C_CONTINUE;
diff --git a/app/bin/ccornu.c b/app/bin/ccornu.c
new file mode 100644
index 0000000..b67d245
--- /dev/null
+++ b/app/bin/ccornu.c
@@ -0,0 +1,1240 @@
+/** \file ccornu.c
+ * Cornu Command. Draw or modify a Cornu Easement Track.
+ */
+/* XTrkCad - Model Railroad CAD
+ *
+ * Cornu curves are a family of mathematically defined curves that define spirals that Euler spirals and elastica come from.
+ *
+ * They have the useful property for us that curvature increases linearly along the curve which
+ * means the acceleration towards the center of the curve also increases evenly. Railways have long understood that
+ * smoothly changing the radius is key to passenger comfort and reduced derailments. The railway versions of these
+ * curves were called variously called easements, Talbot or Euler spirals.
+ *
+ * In XTrackCAD often want to change radius smoothly between two tracks whose end position, angle and curvature are known.
+ *
+ * Finding the right part(s) of the Cornu to fit the gap (if one is available) is mathematically complex,
+ * but fortunately Raph Levien published a PhD thesis on this together with his mathematical libraries as
+ * open source. He was doing work on font design where the Cornu shapes make beautiful smooth fonts. He
+ * was faced with the reality, though, that graphics packages do not include these shapes as native objects
+ * and so his solution was to produce a set of Bezier curves that approximate the solution.
+ *
+ * We already have a tool that can produce a set of arcs and straight line to approximate a Bezier - so that in the end
+ * for an easement track we will have an array of Bezier, each of which is an array of Arcs and Lines. The tool will
+ * use the approximations for most of its work.
+ *
+ * The inputs for the Cornu are the end points, angles and radii. To match the Cornu algorithm's expectations we input
+ * these as a set of knots (points on lines). One point is always the desired end point and the other two are picked
+ * direct the code to derive the other two end conditions. By specifying that the desired end point is a "one-way"
+ * knot we ensure that the result has smooth ends of either zero or fixed radius.
+ *
+ * When reading back the output, we simply ignore the results before the first end point knot and after the last.
+ *
+ * Because we are mathematically deriving the output, we can alter the end conditions and recalculate. This allows
+ * support of modify for Cornu Easements and also movement of tracks that are connected via a Cornu easement to another track.
+ *
+ * Note that unlike the existing Easements in XTrkCAD, the degree of sharpness (the rate of change of curvature)
+ * is derived not defined. By adjusting the ends, one can have an infinite set of sharpness solutions.
+ *
+ * Cornu will not find a solution for every set of input conditions, or may propose one that is impractical such as
+ * huge loops or tiny curves. These are mathematically correct, but not useful. In these cases the answer is to change the
+ * end conditions (more space between the ends, different angles or different radii).
+ *
+ * Note that every time we change the Cornu end points we have to recalculate the Bezier approximation,
+ * which recalculates the arc approximations, but that still means that the majority of the time we are using the approximation.
+ *
+ * Cornus do not have cusps, but can result in smooth loops. If there is too much looping, the code will reject the easement.
+ *
+ * This program is built and founded upon Raph Levien's seminal work and relies on an adaptation of his Cornu library.
+ * As with the Bezier work, it also relies on the pages about Bezier curves that PoMax put up at https://pomax.github.io/bezierinfo
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, 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 License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#include "track.h"
+#include "spiro.h"
+#include "spiroentrypoints.h"
+#include "bezctx_xtrkcad.h"
+#include "draw.h"
+#include "ccurve.h"
+#include "ccornu.h"
+#include "tcornu.h"
+#include "cstraigh.h"
+#include "drawgeom.h"
+#include "cjoin.h"
+#include "i18n.h"
+#include "common.h"
+#include "utility.h"
+#include "math.h"
+#include "param.h"
+#include "layout.h"
+#include "cundo.h"
+#include "messages.h"
+#include "cselect.h"
+
+extern drawCmd_t tempD;
+extern TRKTYP_T T_BEZIER;
+extern TRKTYP_T T_CORNU;
+
+
+/*
+ * STATE INFO
+ */
+enum Cornu_States { NONE,
+ POS_1,
+ LOC_2,
+ POS_2,
+ PICK_POINT,
+ POINT_PICKED,
+ TRACK_SELECTED };
+
+static struct {
+ enum Cornu_States state;
+ coOrd pos[2];
+ int selectPoint;
+ wDrawColor color;
+ DIST_T width;
+ track_p trk[2];
+ EPINX_T ep[2];
+ DIST_T radius[2];
+ ANGLE_T angle[2];
+ ANGLE_T arcA0[2];
+ ANGLE_T arcA1[2];
+ coOrd center[2];
+ curveType_e trackType[2];
+
+ BOOL_T extend[2];
+ trkSeg_t extendSeg[2];
+
+ trkSeg_t ep1Segs[2];
+ int ep1Segs_da_cnt;
+ trkSeg_t ep2Segs[2];
+ int ep2Segs_da_cnt;
+ dynArr_t crvSegs_da;
+ int crvSegs_da_cnt;
+ trkSeg_t trk1Seg;
+ trkSeg_t trk2Seg;
+ track_p selectTrack;
+ DIST_T minRadius;
+ BOOL_T circleorHelix[2];
+
+ bezctx * bezc;
+ } Da;
+
+
+
+/**
+ * Draw a EndPoint.
+ * A Cornu end Point has a filled circle surrounded by another circle for endpoint
+ */
+int createEndPoint(
+ trkSeg_t sp[], //seg pointer for up to 2 trkSegs (ends and line)
+ coOrd pos0, //end on curve
+ BOOL_T point_selected,
+ BOOL_T point_selectable,
+ BOOL_T track_modifyable
+ )
+{
+ DIST_T d, w;
+ d = tempD.scale*0.25;
+ w = tempD.scale/tempD.dpi; /*double width*/
+ sp[0].u.c.center = pos0;
+ sp[0].u.c.a0 = 0.0;
+ sp[0].u.c.a1 = 360.0;
+ sp[0].width = w;
+ sp[0].u.c.radius = d/4;
+ sp[0].color = (point_selected>=0)?drawColorRed:drawColorBlack;
+ if (track_modifyable)
+ sp[0].type = SEG_CRVLIN;
+ else
+ sp[0].type = SEG_FILCRCL;
+ if (point_selectable) {
+ sp[1].u.c.center = pos0;
+ sp[1].u.c.a0 = 0.0;
+ sp[1].u.c.a1 = 360.0;
+ sp[1].u.c.radius = d/2;
+ sp[1].type = SEG_CRVLIN;
+ sp[1].width = w;
+ sp[1].color = drawColorRed;
+ return 2;
+ }
+ return 1;
+}
+
+
+/*
+ * Add element to DYNARR pointed to by caller from segment handed in
+ */
+void addSegCornu(dynArr_t * const array_p, trkSeg_p seg) {
+ trkSeg_p s;
+
+
+ DYNARR_APPEND(trkSeg_t, * array_p, 10); //Adds 1 to cnt
+ s = &DYNARR_N(trkSeg_t,* array_p,array_p->cnt-1);
+ s->type = seg->type;
+ s->bezSegs.max = 0;
+ s->bezSegs.cnt = 0;
+ if (s->bezSegs.ptr) MyFree(s->bezSegs.ptr);
+ s->bezSegs.ptr = NULL;
+ s->color = seg->color;
+ s->width = seg->width;
+ if ((s->type == SEG_BEZLIN || s->type == SEG_BEZTRK) && seg->bezSegs.cnt) {
+ s->u.b.angle0 = seg->u.b.angle0; //Copy all the rest
+ s->u.b.angle3 = seg->u.b.angle3;
+ s->u.b.length = seg->u.b.length;
+ s->u.b.minRadius = seg->u.b.minRadius;
+ for (int i=0;i<4;i++) s->u.b.pos[i] = seg->u.b.pos[i];
+ s->u.b.radius0 = seg->u.b.radius3;
+ for (int i = 0; i<seg->bezSegs.cnt; i++) {
+ addSegCornu(&s->bezSegs, (((trkSeg_p)seg->bezSegs.ptr) + i)); //recurse for copying embedded Beziers as in Cornu joint
+ }
+ } else {
+ s->u = seg->u;
+ }
+}
+EXPORT void SetKnots(spiro_cp knots[6], coOrd posk[6]) {
+ for (int i = 0; i < 6; i++) {
+ knots[i].x = posk[i].x;
+ knots[i].y = posk[i].y;
+ }
+ knots[0].ty = SPIRO_OPEN_CONTOUR;
+ knots[1].ty = SPIRO_G2;
+ knots[2].ty = SPIRO_RIGHT;
+ knots[3].ty = SPIRO_LEFT;
+ knots[4].ty = SPIRO_G2;
+ knots[5].ty = SPIRO_END_OPEN_CONTOUR;
+}
+
+BOOL_T CallCornu0(coOrd pos[2], coOrd center[2], ANGLE_T angle[2], DIST_T radius[2], dynArr_t * array_p, BOOL_T spots) {
+ array_p->cnt = 0;
+ //Create LH knots
+ //Find remote end point of track, create start knot
+ int ends[2];
+ ends[0] = 2; ends[1] = 3;
+ spiro_cp knots[6];
+ coOrd posk[6];
+ BOOL_T back;
+ ANGLE_T angle1;
+
+ if (Da.bezc) free(Da.bezc);
+
+ Da.bezc = new_bezctx_xtrkcad(array_p,ends,spots);
+
+ coOrd pos0 = pos[0];
+
+ if (radius[0] == 0.0) {
+ Translate(&posk[0],pos0,angle[0],10);
+ Translate(&posk[1],pos0,angle[0],5);
+ } else {
+ angle1 = FindAngle(center[0],pos[0]);
+ if (NormalizeAngle(angle1 - angle[0])<180) back = TRUE;
+ else back = FALSE;
+ posk[0] = pos[0];
+ Rotate(&posk[0],center[0],(back)?-10:10);
+ posk[1] = pos[0];
+ Rotate(&posk[1],center[0],(back)?-5:5);
+ }
+ posk[2] = pos[0];
+
+ posk[3] = pos[1];
+
+ coOrd pos1 = pos[1];
+
+ if (radius[1] == 0.0 ) {
+ Translate(&posk[4],pos1,angle[1],5);
+ Translate(&posk[5],pos1,angle[1],10);
+ } else {
+ angle1 = FindAngle(center[1],pos[1]);
+ if (NormalizeAngle(angle1 - angle[1])>180) back = TRUE;
+ else back = FALSE;
+ posk[4] = pos[1];
+ Rotate(&posk[4],center[1],(back)?5:-5);
+ posk[5] = pos[1];
+ Rotate(&posk[5],center[1],(back)?10:-10);
+ }
+ SetKnots(knots,posk);
+ TaggedSpiroCPsToBezier(knots,Da.bezc);
+ if (!bezctx_xtrkcad_close(Da.bezc)) {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*
+ * Set up the call to Cornu0. Take the conditions of the two ends from the connected tracks.
+ */
+BOOL_T CallCornu(coOrd pos[2], track_p trk[2], EPINX_T ep[2], dynArr_t * array_p, cornuParm_t * cp) {
+
+ trackParams_t params;
+ ANGLE_T angle;
+ for (int i=0;i<2;i++) {
+ if (trk[i]) {
+ if (!GetTrackParams(PARAMS_CORNU,trk[i],pos[i],&params)) return FALSE;
+ cp->pos[i] = pos[i];
+ if (Da.ep[i]>=0) angle = GetTrkEndAngle(trk[i],ep[i]);
+ else angle = params.angle; //Turntable only
+ if (Da.circleorHelix[i]) { //Helix/Circle only
+ cp->radius[i] = params.arcR;
+ cp->center[i] = params.arcP;
+ cp->angle[i] = NormalizeAngle(params.track_angle+(ep[i]?180:0));
+ } else if (params.type == curveTypeStraight) {
+ cp->angle[i] = NormalizeAngle(angle+180); //Because end always backwards
+ cp->radius[i] = 0.0;
+ } else if ((params.type == curveTypeCornu || params.type == curveTypeBezier) && params.arcR == 0.0 ) {
+ cp->radius[i] = 0.0;
+ cp->angle[i] = NormalizeAngle(params.track_angle+(ep[i]?180:0)); //Use point not end
+ } else if (params.type == curveTypeCurve) {
+ cp->angle[i] = NormalizeAngle(params.track_angle+(ep[i]?180:0));
+ cp->radius[i] = params.arcR;
+ cp->center[i] = params.arcP;
+ } else if ((params.type == curveTypeCornu || params.type == curveTypeBezier) && params.arcR != 0.0 ){
+ cp->angle[i] = NormalizeAngle(params.track_angle+(ep[i]?180:0));
+ cp->radius[i] = params.arcR;
+ cp->center[i] = params.arcP;
+ } else {
+ cp->angle[i] = NormalizeAngle(angle+180); //Unknown - treat like straight
+ cp->radius[i] = params.arcR;
+ cp->center[i] = params.arcP;
+ }
+ }
+ }
+
+ return CallCornu0(pos,cp->center,cp->angle,cp->radius,array_p,TRUE);
+}
+
+
+/*
+ * Draw Cornu while editing it. It consists of up to five elements - the ends, the curve and one or two End Points.
+ *
+ */
+
+EXPORT void DrawCornuCurve(
+ trkSeg_p first_trk,
+ trkSeg_p point1,
+ int ep1Segs_cnt,
+ trkSeg_p curveSegs,
+ int crvSegs_cnt,
+ trkSeg_p point2,
+ int ep2Segs_cnt,
+ trkSeg_p second_trk,
+ trkSeg_p extend1_trk,
+ trkSeg_p extend2_trk,
+ wDrawColor color
+ ) {
+ long oldDrawOptions = tempD.funcs->options;
+ tempD.funcs->options = wDrawOptTemp;
+ long oldOptions = tempD.options;
+ tempD.options = DC_TICKS;
+ tempD.orig = mainD.orig;
+ tempD.angle = mainD.angle;
+ if (first_trk)
+ DrawSegs( &tempD, zero, 0.0, first_trk, 1, trackGauge, drawColorBlack );
+ if (crvSegs_cnt && curveSegs)
+ DrawSegs( &tempD, zero, 0.0, curveSegs, crvSegs_cnt, trackGauge, color );
+ if (second_trk)
+ DrawSegs( &tempD, zero, 0.0, second_trk, 1, trackGauge, drawColorBlack );
+ if (ep1Segs_cnt && point1)
+ DrawSegs( &tempD, zero, 0.0, point1, ep1Segs_cnt, trackGauge, drawColorBlack );
+ if (ep2Segs_cnt && point2)
+ DrawSegs( &tempD, zero, 0.0, point2, ep2Segs_cnt, trackGauge, drawColorBlack );
+ if (extend1_trk)
+ DrawSegs( &tempD, zero, 0.0, extend1_trk, 1, trackGauge, drawColorBlack);
+ if (extend2_trk)
+ DrawSegs( &tempD, zero, 0.0, extend2_trk, 1, trackGauge, drawColorBlack);
+ tempD.funcs->options = oldDrawOptions;
+ tempD.options = oldOptions;
+
+}
+
+/*
+ * If Track, make it red if the radius is below minimum
+ */
+void DrawTempCornu() {
+
+
+ DrawCornuCurve(&Da.trk1Seg,
+ &Da.ep1Segs[0],Da.ep1Segs_da_cnt,
+ (trkSeg_t *)Da.crvSegs_da.ptr,Da.crvSegs_da_cnt,
+ &Da.ep2Segs[0],Da.ep2Segs_da_cnt,
+ &Da.trk2Seg,
+ Da.extend[0]?&Da.extendSeg[0]:NULL,
+ Da.extend[1]?&Da.extendSeg[1]:NULL,
+ Da.minRadius<(GetLayoutMinTrackRadius()-EPSILON)?drawColorRed:drawColorBlack);
+
+}
+
+void CreateBothEnds(int selectPoint) {
+ BOOL_T selectable[2],modifyable[2];
+ selectable[0] = Da.trk[0] && !QueryTrack(Da.trk[0],Q_IS_CORNU);
+ modifyable[0] = Da.trk[0] && QueryTrack(Da.trk[0],Q_CORNU_CAN_MODIFY);
+ selectable[1] = Da.trk[1] && !QueryTrack(Da.trk[1],Q_IS_CORNU);
+ modifyable[1] = Da.trk[1] && QueryTrack(Da.trk[1],Q_CORNU_CAN_MODIFY);
+ if (selectPoint == -1) {
+ Da.ep1Segs_da_cnt = createEndPoint(Da.ep1Segs, Da.pos[0],FALSE,selectable[0],modifyable[0]);
+ Da.ep2Segs_da_cnt = createEndPoint(Da.ep2Segs, Da.pos[1],FALSE,selectable[1],modifyable[1]);
+ } else if (selectPoint == 0 || selectPoint == 1) {
+ Da.ep1Segs_da_cnt = createEndPoint(Da.ep1Segs, Da.pos[0],selectPoint == 0,selectable[0],modifyable[0]);
+ Da.ep2Segs_da_cnt = createEndPoint(Da.ep2Segs, Da.pos[1],selectPoint == 1,selectable[1],modifyable[1]);
+ } else {
+ Da.ep1Segs_da_cnt = createEndPoint(Da.ep1Segs, Da.pos[0],FALSE,selectable[0],modifyable[0]);
+ Da.ep2Segs_da_cnt = createEndPoint(Da.ep2Segs, Da.pos[1],FALSE,selectable[1],modifyable[1]);
+ }
+}
+
+BOOL_T GetConnectedTrackParms(track_p t, const coOrd pos, int end, EPINX_T track_end) {
+ trackParams_t trackParams;
+ if (!GetTrackParams(PARAMS_CORNU, t, pos, &trackParams)) return FALSE;
+ Da.radius[end] = 0.0;
+ Da.center[end] = zero;
+ Da.circleorHelix[end] = FALSE;
+ Da.trackType[end] = trackParams.type;
+ if (trackParams.type == curveTypeCurve) {
+ Da.arcA0[end] = trackParams.arcA0;
+ Da.arcA1[end] = trackParams.arcA1;
+ Da.radius[end] = trackParams.arcR;
+ Da.center[end] = trackParams.arcP;
+ if (trackParams.circleOrHelix) {
+ Da.circleorHelix[end] = TRUE;
+ Da.angle[end] = trackParams.track_angle; //For Now
+ } else {
+ Da.angle[end] = NormalizeAngle(trackParams.track_angle + (track_end?180:0));
+ }
+ } else if (trackParams.type == curveTypeBezier) {
+ Da.angle[end] = NormalizeAngle(trackParams.track_angle+(track_end?180:0));
+ if (trackParams.arcR == 0) {
+ Da.radius[end] = 0;
+ Da.center[end] = zero;
+ } else {
+ Da.arcA0[end] = trackParams.arcA0;
+ Da.arcA1[end] = trackParams.arcA1;
+ Da.radius[end] = trackParams.arcR;
+ Da.center[end] = trackParams.arcP;
+ }
+ } else if (trackParams.type == curveTypeCornu) {
+ int ep = trackParams.ep;
+ Da.angle[end] = NormalizeAngle(trackParams.cornuAngle[ep]+(track_end?180:0));
+ Da.radius[end] = trackParams.cornuRadius[ep];
+ Da.pos[end] = trackParams.cornuEnd[ep];
+ Da.center[end] = trackParams.cornuCenter[ep];
+ } else if (trackParams.type == curveTypeStraight) {
+ if (Da.ep[end]>=0)
+ Da.angle[end] = NormalizeAngle(GetTrkEndAngle(t,track_end)+180); //Ignore params.angle because it gives from nearest end
+ else {
+ Da.angle[end] = NormalizeAngle(trackParams.angle+180); //Turntable
+ Da.pos[end] = trackParams.lineEnd; //End moved to constrain angle
+ }
+ }
+ return TRUE;
+}
+
+void CorrectHelixAngles() {
+ if ( Da.circleorHelix[0] ) {
+ Da.ep[0] = PickArcEndPt( Da.center[0], Da.pos[0], Da.pos[1] );
+ if (Da.ep[0] == 1) Da.angle[0] = NormalizeAngle(Da.angle[0]+180);
+ }
+ if ( Da.circleorHelix[1] ) {
+ Da.ep[1] = PickArcEndPt( Da.center[1], Da.pos[1], Da.pos[0] );
+ if (Da.ep[1] == 1) Da.angle[1] = NormalizeAngle(Da.angle[1]+180);
+ }
+}
+
+BOOL_T CheckHelix(track_p trk) {
+ if ( Da.trk[0] && QueryTrack(Da.trk[0],Q_HAS_VARIABLE_ENDPOINTS)) {
+ track_p t = GetTrkEndTrk(Da.trk[0],Da.ep[0]);
+ if ( t != NULL && t != trk) {
+ ErrorMessage( MSG_TRK_ALREADY_CONN, _("First") );
+ return FALSE;
+ }
+ }
+ if ( Da.trk[1] && QueryTrack(Da.trk[1],Q_HAS_VARIABLE_ENDPOINTS)) {
+ track_p t = GetTrkEndTrk(Da.trk[1],Da.ep[1]);
+ if ( t != NULL && t != trk) {
+ ErrorMessage( MSG_TRK_ALREADY_CONN, _("Second") );
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+void SetUpCornuParms(cornuParm_t * cp) {
+ cp->center[0] = Da.center[0];
+ cp->angle[0] = Da.angle[0];
+ cp->radius[0] = Da.radius[0];
+ cp->center[1] = Da.center[1];
+ cp->angle[1] = Da.angle[1];
+ cp->radius[1] = Da.radius[1];
+}
+
+/*
+ * AdjustCornuCurve
+ *
+ * Called to adjust the curve either when creating it or modifying it
+ * States are "PICK_POINT" and "POINT_PICKED" and "TRACK_SELECTED".
+ *
+ * In PICK_POINT, the user can select an end-point to drag and release in POINT_PICKED. They can also
+ * hit Enter (which saves the changes) or ESC (which cancels them).
+ *
+ * Deal with extended tracks from ends.
+ *
+ */
+EXPORT STATUS_T AdjustCornuCurve(
+ wAction_t action,
+ coOrd pos,
+ cornuMessageProc message )
+{
+ track_p t;
+ DIST_T d;
+ ANGLE_T a, a2;
+ DIST_T dd;
+ EPINX_T ep;
+ cornuParm_t cp;
+
+
+ if (Da.state != PICK_POINT && Da.state != POINT_PICKED && Da.state != TRACK_SELECTED) return C_CONTINUE;
+
+ switch ( action & 0xFF) {
+
+ case C_START:
+ Da.selectPoint = -1;
+ Da.extend[0] = FALSE;
+ Da.extend[1] = FALSE;
+ CreateBothEnds(Da.selectPoint);
+ Da.crvSegs_da.cnt = 0;
+ SetUpCornuParms(&cp);
+ if (CallCornu(Da.pos,Da.trk,Da.ep,&Da.crvSegs_da,&cp)) Da.crvSegs_da_cnt = Da.crvSegs_da.cnt;
+ else Da.crvSegs_da_cnt = 0;
+ Da.minRadius = CornuMinRadius(Da.pos,Da.crvSegs_da);
+ InfoMessage( _("Select End-Point") );
+ DrawTempCornu();
+ return C_CONTINUE;
+
+ case C_DOWN:
+ if (Da.state != PICK_POINT) return C_CONTINUE;
+ dd = 10000.0;
+ Da.selectPoint = -1;
+ for (int i=0;i<2;i++) {
+ d = FindDistance(Da.pos[i],pos);
+ if (d < dd) {
+ dd = d;
+ Da.selectPoint = i;
+ }
+ }
+ if (!IsClose(dd) ) Da.selectPoint = -1;
+ if (Da.selectPoint == -1) {
+ wBeep();
+ InfoMessage( _("Not close enough to end point, reselect") );
+ return C_CONTINUE;
+ } else if (Da.trk[Da.selectPoint] && QueryTrack(Da.trk[Da.selectPoint],Q_IS_CORNU)){
+ wBeep();
+ InfoMessage( _("Is Cornu End -> Not Selectable") );
+ return C_CONTINUE;
+ } else {
+ pos = Da.pos[Da.selectPoint];
+ Da.state = POINT_PICKED;
+ InfoMessage( _("Drag point %d to new location and release it"),Da.selectPoint+1 );
+ }
+ DrawTempCornu(); //wipe out
+ CreateBothEnds(Da.selectPoint);
+ SetUpCornuParms(&cp);
+ if (CallCornu(Da.pos, Da.trk,Da.ep, &Da.crvSegs_da, &cp)) Da.crvSegs_da_cnt = Da.crvSegs_da.cnt;
+ else Da.crvSegs_da_cnt = 0;
+ Da.minRadius = CornuMinRadius(Da.pos, Da.crvSegs_da);
+ DrawTempCornu();
+ return C_CONTINUE;
+
+ case C_MOVE:
+ if (Da.state != POINT_PICKED) {
+ InfoMessage(_("Pick any circle to adjust it by dragging - Enter to accept, Esc to cancel"));
+ return C_CONTINUE;
+ }
+ //If locked, reset pos to be on line from other track
+ int sel = Da.selectPoint;
+ coOrd pos2 = pos;
+ BOOL_T inside = FALSE;
+ if (Da.trk[sel]) { //There is a track
+ if (OnTrack(&pos,FALSE,TRUE) == Da.trk[sel]) { //And the pos is on it
+ inside = TRUE;
+ if (!QueryTrack(Da.trk[Da.selectPoint],Q_CORNU_CAN_MODIFY)) { //Turnouts
+ InfoMessage(_("Track can't be split"));
+ if (Da.ep[sel]>=0) //Ignore if turntable
+ pos = GetTrkEndPos(Da.trk[sel],Da.ep[sel]);
+ }
+ } else {
+ pos = pos2; //Put Back to original position as outside track
+ }
+ // Stop the user extending right through the other track
+ if (Da.ep[sel]>=0 && QueryTrack(Da.trk[sel],Q_CORNU_CAN_MODIFY)) { //For non-turnouts
+ if ((!QueryTrack(Da.trk[sel],Q_CAN_ADD_ENDPOINTS)) // But Not Helix or Circle
+ && (!QueryTrack(Da.trk[sel],Q_HAS_VARIABLE_ENDPOINTS))) { // Not a Turntable
+ DIST_T ab = FindDistance(GetTrkEndPos(Da.trk[sel],Da.ep[sel]),GetTrkEndPos(Da.trk[sel],1-Da.ep[sel]));
+ DIST_T ac = FindDistance(GetTrkEndPos(Da.trk[sel],Da.ep[sel]),pos);
+ DIST_T cb = FindDistance(GetTrkEndPos(Da.trk[sel],1-Da.ep[sel]), pos);
+ if (cb<minLength) {
+ InfoMessage(_("Too close to other end of selected Track"));
+ return C_CONTINUE;
+ }
+ if ((ac>=cb) && (ac>=ab)) { //Closer to far end and as long as the track
+ pos = GetTrkEndPos(Da.trk[sel],1-Da.ep[sel]); //Make other end of track
+ }
+ }
+ }
+ }
+ DrawTempCornu(); //wipe out old
+ Da.extend[sel] = FALSE;
+ if(!Da.trk[sel]) { //Cornu with no ends
+ if (Da.radius[sel] == 0) { //Straight
+ Da.extendSeg[sel].type = SEG_STRTRK;
+ Da.extendSeg[sel].width = 0;
+ Da.extendSeg[sel].color = wDrawColorBlack;
+ Da.extendSeg[sel].u.l.pos[1-sel] = Da.pos[sel];
+ d = FindDistance( Da.extendSeg[sel].u.l.pos[1-sel], pos );
+ a = NormalizeAngle(Da.angle[sel]-FindAngle(pos,Da.pos[sel]));
+ if (cos(D2R(a))<=0) {
+ Translate( &Da.extendSeg[sel].u.l.pos[sel],
+ Da.extendSeg[sel].u.l.pos[1-sel],
+ Da.angle[sel], - d * cos(D2R(a)));
+ pos = Da.extendSeg[sel].u.l.pos[1-sel];
+ Da.extend[sel] = TRUE;
+ }
+ } else { //Curve
+ Da.extendSeg[sel].type = SEG_CRVTRK;
+ Da.extendSeg[sel].width = 0;
+ Da.extendSeg[sel].color = wDrawColorBlack;
+ Da.extendSeg[sel].u.c.center = Da.center[sel];
+ Da.extendSeg[sel].u.c.radius = Da.radius[sel];
+ a = FindAngle( Da.center[sel], pos );
+ PointOnCircle( &pos, Da.center[sel], Da.radius[sel], a );
+ a2 = FindAngle(Da.center[sel],Da.pos[sel]);
+ if (((Da.angle[sel] < 180) && (a2>90 && a2<270)) ||
+ ((Da.angle[sel] > 180) && (a2<90 || a2>270))) {
+ Da.extendSeg[sel].u.c.a0 = a;
+ Da.extendSeg[sel].u.c.a1 = NormalizeAngle(a2-a);
+ } else {
+ Da.extendSeg[sel].u.c.a0 = a2;
+ Da.extendSeg[sel].u.c.a1 = NormalizeAngle(a-a2);
+ }
+ if (Da.extendSeg[sel].u.c.a1 == 0 || Da.extendSeg[sel].u.c.a1 >180 )
+ Da.extend[sel] = FALSE;
+ else
+ Da.extend[sel] = TRUE;
+ }
+ } else { //Cornu with ends
+ if (inside) Da.pos[sel] = pos;
+ if (!GetConnectedTrackParms(Da.trk[sel],pos,sel,Da.ep[sel])) {
+ DrawTempCornu();
+ wBeep();
+ return C_CONTINUE; //Stop drawing
+ }
+ CorrectHelixAngles();
+ if (!inside) { //Extend the track
+ if (Da.trackType[sel] == curveTypeStraight) { //Extend with a straight
+ Da.extendSeg[sel].type = SEG_STRTRK;
+ Da.extendSeg[sel].width = 0;
+ Da.extendSeg[sel].color = wDrawColorBlack;
+ if (Da.ep[sel]>=0) {
+ Da.extendSeg[sel].u.l.pos[0] = GetTrkEndPos( Da.trk[sel], Da.ep[sel] );
+ a = NormalizeAngle(Da.angle[sel]-FindAngle(pos,GetTrkEndPos(Da.trk[sel],Da.ep[sel])));
+ } else { //Turntable when unconnected
+ Da.extendSeg[sel].u.l.pos[0] = Da.pos[sel];
+ a = NormalizeAngle(Da.angle[sel]-FindAngle(pos,Da.pos[sel]));
+ }
+ // Remove any extend in opposite direction for Turntable/Turnouts
+ if ((QueryTrack(Da.trk[sel],Q_CAN_ADD_ENDPOINTS) && Da.ep[sel]>=0)
+ && (!QueryTrack(Da.trk[sel],Q_CORNU_CAN_MODIFY))
+ && (a>90 && a<270)) {
+ Da.extend[sel] = FALSE; //Turntable with point and extension is other side of well
+ Da.pos[sel] = GetTrkEndPos(Da.trk[sel],Da.ep[sel]);
+ } else {
+ Da.extend[sel] = TRUE;
+ d = FindDistance( Da.extendSeg[sel].u.l.pos[0], pos );
+ Translate( &Da.extendSeg[sel].u.l.pos[1],
+ Da.extendSeg[sel].u.l.pos[0],
+ Da.angle[sel], -d * cos(D2R(a)));
+ Da.pos[sel] = pos = Da.extendSeg[sel].u.l.pos[1];
+ }
+ } else if (Da.trackType[sel] == curveTypeCurve) { //Extend with temp curve
+ Da.extendSeg[sel].type = SEG_CRVTRK;
+ Da.extendSeg[sel].width = 0;
+ Da.extendSeg[sel].color = wDrawColorBlack;
+ Da.extendSeg[sel].u.c.center = Da.center[sel];
+ Da.extendSeg[sel].u.c.radius = Da.radius[sel];
+ a = FindAngle( Da.center[sel], pos );
+ PointOnCircle( &pos, Da.center[sel], Da.radius[sel], a );
+ a2 = FindAngle(Da.center[sel],GetTrkEndPos(Da.trk[sel],Da.ep[sel]));
+ if ((Da.angle[sel] < 180 && (a2>90 && a2 <270)) ||
+ (Da.angle[sel] > 180 && (a2<90 || a2 >270))) {
+ Da.extendSeg[sel].u.c.a0 = a2;
+ Da.extendSeg[sel].u.c.a1 = NormalizeAngle(a-a2);
+ } else {
+ Da.extendSeg[sel].u.c.a0 = a;
+ Da.extendSeg[sel].u.c.a1 = NormalizeAngle(a2-a);
+ }
+ if (Da.extendSeg[sel].u.c.a1 == 0.0 || Da.extendSeg[sel].u.c.a1 >180
+ || (Da.extendSeg[sel].u.c.a0 >= Da.arcA0[sel] && Da.extendSeg[sel].u.c.a0 < Da.arcA0[sel]+Da.arcA1[sel]
+ && Da.extendSeg[sel].u.c.a0 + Da.extendSeg[sel].u.c.a1 <= Da.arcA0[sel] + Da.arcA1[sel])
+ ) {
+ Da.extend[sel] = FALSE;
+ Da.pos[sel] = pos;
+ } else {
+ Da.extend[sel] = TRUE;
+ Da.pos[sel] = pos;
+ }
+
+ } else { //Bezier and Cornu that we are joining TO can't extend
+ DrawTempCornu(); //put back
+ wBeep();
+ InfoMessage(_("Must be on the %s Track"),Da.trackType[sel]==curveTypeBezier?"Bezier":Da.trackType[sel]==curveTypeCornu?"Cornu":"Unknown Type");
+ pos = GetTrkEndPos(Da.trk[sel],Da.ep[sel]);
+ return C_CONTINUE;
+ }
+ }
+ }
+
+ CreateBothEnds(Da.selectPoint);
+ SetUpCornuParms(&cp); //In case we want to use these because the ends are not on the track
+
+ if (CallCornu(Da.pos, Da.trk, Da.ep, &Da.crvSegs_da,&cp)) Da.crvSegs_da_cnt = Da.crvSegs_da.cnt;
+ else Da.crvSegs_da_cnt = 0;
+ Da.minRadius = CornuMinRadius(Da.pos,Da.crvSegs_da);
+ DIST_T rin = Da.radius[0];
+ InfoMessage( _("Cornu : Min Radius=%s Max Rate of Radius Change=%s Length=%s Winding Arc=%s"),
+ FormatDistance(Da.minRadius),
+ FormatDistance(CornuMaxRateofChangeofCurvature(Da.pos,Da.crvSegs_da,&rin)),
+ FormatDistance(CornuLength(Da.pos,Da.crvSegs_da)),
+ FormatDistance(CornuTotalWindingArc(Da.pos,Da.crvSegs_da)));
+ DrawTempCornu();
+ return C_CONTINUE;
+
+ case C_UP:
+ if (Da.state != POINT_PICKED) return C_CONTINUE;
+ ep = 0;
+ DrawTempCornu(); //wipe out
+ Da.selectPoint = -1;
+ CreateBothEnds(Da.selectPoint);
+ SetUpCornuParms(&cp);
+ if (CallCornu(Da.pos,Da.trk,Da.ep,&Da.crvSegs_da,&cp)) Da.crvSegs_da_cnt = Da.crvSegs_da.cnt;
+ else Da.crvSegs_da_cnt = 0;
+ Da.minRadius = CornuMinRadius(Da.pos,Da.crvSegs_da);
+ InfoMessage(_("Pick on point to adjust it along track - Enter to confirm, ESC to abort"));
+ DrawTempCornu();
+ Da.state = PICK_POINT;
+ return C_CONTINUE;
+
+ case C_OK: //C_OK is not called by Modify.
+ if ( Da.state == PICK_POINT ) {
+ Da.minRadius = CornuMinRadius(Da.pos,Da.crvSegs_da);
+ if (CornuTotalWindingArc(Da.pos,Da.crvSegs_da)>4*360) {
+ wBeep();
+ InfoMessage(_("Cornu has too complex shape - adjust end pts"));
+ return C_CONTINUE;
+ }
+ if (!CheckHelix(NULL)) {
+ wBeep();
+ return C_CONTINUE;
+ }
+ for (int i=0;i<2;i++) {
+ if (FindDistance(Da.pos[i],GetTrkEndPos(Da.trk[i],1-Da.ep[i])) < minLength) {
+ wBeep();
+ InfoMessage(_("Cornu end %d too close to other end of connect track - reposition it"),i+1);
+ return C_CONTINUE;
+ }
+ }
+
+ DrawTempCornu();
+ UndoStart( _("Create Cornu"),"newCornu curve");
+ t = NewCornuTrack( Da.pos, Da.center, Da.angle, Da.radius,(trkSeg_p)Da.crvSegs_da.ptr, Da.crvSegs_da.cnt);
+ if (t==NULL) {
+ wBeep();
+ InfoMessage(_("Cornu Create Failed for p1[%0.3f,%0.3f] p2[%0.3f,%0.3f], c1[%0.3f,%0.3f] c2[%0.3f,%0.3f], a1=%0.3f a2=%0.3f, r1=%s r2=%s"),
+ Da.pos[0].x,Da.pos[0].y,
+ Da.pos[1].x,Da.pos[1].y,
+ Da.center[0].x,Da.center[0].y,
+ Da.center[1].x,Da.center[1].y,
+ Da.angle[0],Da.angle[1],
+ FormatDistance(Da.radius[0]),FormatDistance(Da.radius[1]));
+ return C_TERMINATE;
+ }
+
+ CopyAttributes( Da.trk[0], t );
+
+ for (int i=0;i<2;i++) {
+ UndoModify(Da.trk[i]);
+ MoveEndPt(&Da.trk[i],&Da.ep[i],Da.pos[i],0);
+ if ((GetTrkType(Da.trk[i])==T_BEZIER) || (GetTrkType(Da.trk[i])==T_CORNU)) { //Bezier split position not precise, so readjust Cornu
+ GetConnectedTrackParms(Da.trk[i],GetTrkEndPos(Da.trk[i],Da.ep[i]),i,Da.ep[i]);
+ ANGLE_T endAngle = NormalizeAngle(GetTrkEndAngle(Da.trk[i],Da.ep[i])+180);
+ SetCornuEndPt(t,i,GetTrkEndPos(Da.trk[i],Da.ep[i]),Da.center[i],endAngle,Da.radius[i]);
+ }
+ if (Da.ep[i]>=0)
+ ConnectTracks(Da.trk[i],Da.ep[i],t,i);
+ }
+ UndoEnd();
+ DrawNewTrack(t);
+ Da.state = NONE;
+ MainRedraw();
+ MapRedraw();
+ return C_TERMINATE;
+ }
+ return C_CONTINUE;
+
+ case C_REDRAW:
+ DrawTempCornu();
+ return C_CONTINUE;
+
+ default:
+ return C_CONTINUE;
+ }
+
+
+}
+
+struct extraData {
+ cornuData_t cornuData;
+ };
+
+/**
+ * CmdCornuModify
+ *
+ * Called from Modify Command - this function deals with the real (old) track and calls AdjustCornuCurve to tune up the new one
+ * Sequence is this -
+ * - The C_START is called from CmdModify C_DOWN action if a track has being selected. The old track is hidden, the editable one is shown.
+ * - C_MOVES will be ignored until a C_UP ends the track selection and moves the state to PICK_POINT,
+ * - C_DOWN then hides the track and shows the Cornu circles. Selects a point (if close enough and available) and the state moves to POINT_PICKED
+ * - C_MOVE drags the point around modifying the curve
+ * - C_UP puts the state back to PICK_POINT (pick another)
+ * - C_OK (Enter/Space) creates the new track, deletes the old and shows the changed track.
+ * - C_CANCEL (Esc) sets the state to NONE and reshows the original track unchanged.
+ *
+ */
+STATUS_T CmdCornuModify (track_p trk, wAction_t action, coOrd pos) {
+ track_p t;
+ struct extraData *xx = GetTrkExtraData(trk);
+
+ switch (action&0xFF) {
+ case C_START:
+ Da.state = NONE;
+ DYNARR_RESET(trkSeg_t,Da.crvSegs_da);
+ Da.ep1Segs_da_cnt = 0;
+ Da.ep2Segs_da_cnt = 0;
+ Da.extend[0] = FALSE;
+ Da.extend[1] = FALSE;
+ Da.selectPoint = -1;
+ Da.selectTrack = NULL;
+
+
+ Da.selectTrack = trk;
+ Da.trk[0] = GetTrkEndTrk( trk, 0 );
+ if (Da.trk[0]) Da.ep[0] = GetEndPtConnectedToMe(Da.trk[0],trk);
+ Da.trk[1] = GetTrkEndTrk( trk, 1 );
+ if (Da.trk[1]) Da.ep[1] = GetEndPtConnectedToMe(Da.trk[1],trk);
+
+ for (int i=0;i<2;i++) {
+ Da.pos[i] = xx->cornuData.pos[i]; //Copy parms from old trk
+ Da.radius[i] = xx->cornuData.r[i];
+ Da.angle[i] = xx->cornuData.a[i];
+ Da.center[i] = xx->cornuData.c[i];
+ }
+
+
+ if ((Da.trk[0] && (!QueryTrack(Da.trk[0],Q_CORNU_CAN_MODIFY) && !QueryTrack(Da.trk[0],Q_CAN_EXTEND))) &&
+ (Da.trk[1] && (!QueryTrack(Da.trk[1],Q_CORNU_CAN_MODIFY) && !QueryTrack(Da.trk[1],Q_CAN_EXTEND)))) {
+ wBeep();
+ ErrorMessage("Both Ends of this Cornu are UnAdjustable");
+ return C_TERMINATE;
+ }
+
+ InfoMessage(_("Track picked - now select a Point"));
+ Da.state = TRACK_SELECTED;
+ DrawTrack(Da.selectTrack,&mainD,wDrawColorWhite); //Wipe out real track, draw replacement
+ return AdjustCornuCurve(C_START, pos, InfoMessage);
+
+ case C_DOWN:
+ if (Da.state == TRACK_SELECTED) return C_CONTINUE; //Ignore until first up
+ return AdjustCornuCurve(C_DOWN, pos, InfoMessage);
+
+
+ case C_MOVE:
+ if (Da.state == TRACK_SELECTED) return C_CONTINUE; //Ignore until first up and down
+ return AdjustCornuCurve(C_MOVE, pos, InfoMessage);
+
+ case C_UP:
+ if (Da.state == TRACK_SELECTED) {
+ Da.state = PICK_POINT; //First time up, next time pick a point
+ }
+ return AdjustCornuCurve(C_UP, pos, InfoMessage); //Run Adjust
+
+ case C_TEXT:
+ if ((action>>8) != 32)
+ return C_CONTINUE;
+ /* no break */
+ case C_OK:
+ if (Da.state != PICK_POINT) { //Too early - abandon
+ InfoMessage(_("No changes made"));
+ Da.state = NONE;
+ //DYNARR_FREE(trkSeg_t,Da.crvSegs_da);
+ return C_CANCEL;
+ }
+ if (!CheckHelix(trk)) {
+ wBeep();
+ return C_CONTINUE;
+ }
+ UndoStart( _("Modify Cornu"), "newCornu - CR" );
+ for (int i=0;i<2;i++) {
+ if (!Da.trk[i] && Da.extend[i]) {
+ if (Da.extendSeg[i].type == SEG_STRTRK) {
+ Da.trk[i] = NewStraightTrack(Da.extendSeg[i].u.l.pos[0],Da.extendSeg[i].u.l.pos[1]);
+ if (Da.trk[i]) Da.ep[i] = 1-i;
+ } else {
+ Da.trk[i] = NewCurvedTrack(Da.extendSeg[i].u.c.center,fabs(Da.extendSeg[i].u.c.radius),
+ Da.extendSeg[i].u.c.a0,Da.extendSeg[i].u.c.a1,FALSE);
+ if (Da.angle[i]>180)
+ Da.ep[i] = (Da.extendSeg[i].u.c.a0>90 && Da.extendSeg[i].u.c.a0<270)?0:1;
+ else
+ Da.ep[i] = (Da.extendSeg[i].u.c.a0>90 && Da.extendSeg[i].u.c.a0<270)?1:0;
+ }
+ if (!Da.trk[i]) {
+ wBeep();
+ InfoMessage(_("Cornu Extension Create Failed for end %d"),i);
+ return C_TERMINATE;
+ }
+
+ }
+ }
+
+ t = NewCornuTrack( Da.pos, Da.center, Da.angle, Da.radius, (trkSeg_p)Da.crvSegs_da.ptr, Da.crvSegs_da.cnt);
+ if (t==NULL) {
+ wBeep();
+ InfoMessage(_("Cornu Create Failed for p1[%0.3f,%0.3f] p2[%0.3f,%0.3f], c1[%0.3f,%0.3f] c2[%0.3f,%0.3f], a1=%0.3f a2=%0.3f, r1=%s r2=%s"),
+ Da.pos[0].x,Da.pos[0].y,
+ Da.pos[1].x,Da.pos[1].y,
+ Da.center[0].x,Da.center[0].y,
+ Da.center[1].x,Da.center[1].y,
+ Da.angle[0],Da.angle[1],
+ FormatDistance(Da.radius[0]),FormatDistance(Da.radius[1]));
+ UndoUndo();
+ MainRedraw();
+ MapRedraw();
+ //DYNARR_FREE(trkSeg_t,Da.crvSegs_da);
+ return C_TERMINATE;
+ }
+
+ DeleteTrack(trk, TRUE);
+
+ if (Da.trk[0]) UndoModify(Da.trk[0]);
+ if (Da.trk[1]) UndoModify(Da.trk[1]);
+
+ for (int i=0;i<2;i++) { //Attach new track
+ if (Da.trk[i] && Da.ep[i] != -1) { //Like the old track
+ MoveEndPt(&Da.trk[i],&Da.ep[i],Da.pos[i],0);
+ if (GetTrkType(Da.trk[i])==T_BEZIER) { //Bezier split position not precise, so readjust Cornu
+ GetConnectedTrackParms(Da.trk[i],GetTrkEndPos(Da.trk[i],Da.ep[i]),i,Da.ep[i]);
+ ANGLE_T endAngle = NormalizeAngle(GetTrkEndAngle(Da.trk[i],Da.ep[i])+180);
+ SetCornuEndPt(t,i,GetTrkEndPos(Da.trk[i],Da.ep[i]),Da.center[i],endAngle,Da.radius[i]);
+ }
+ if (Da.ep[i]>= 0)
+ ConnectTracks(t,i,Da.trk[i],Da.ep[i]);
+ }
+ }
+ UndoEnd();
+ MainRedraw();
+ MapRedraw();
+ Da.state = NONE;
+ //DYNARR_FREE(trkSeg_t,Da.crvSegs_da);
+ return C_TERMINATE;
+
+ case C_CANCEL:
+ InfoMessage(_("Modify Cornu Cancelled"));
+ Da.state = NONE;
+ //DYNARR_FREE(trkSeg_t,Da.crvSegs_da);
+ MainRedraw();
+ MapRedraw();
+ return C_TERMINATE;
+
+ case C_REDRAW:
+ return AdjustCornuCurve(C_REDRAW, pos, InfoMessage);
+ }
+
+ return C_CONTINUE;
+
+}
+
+/*
+ * Find length by adding up the underlying segments. The segments can be straights, curves or bezier.
+ */
+DIST_T CornuLength(coOrd pos[4],dynArr_t segs) {
+
+ DIST_T dd = 0.0;
+ if (segs.cnt == 0 ) return dd;
+ for (int i = 0;i<segs.cnt;i++) {
+ trkSeg_t t = DYNARR_N(trkSeg_t, segs, i);
+ if (t.type == SEG_CRVTRK || t.type == SEG_CRVLIN) {
+ dd += fabs(t.u.c.radius*D2R(t.u.c.a1));
+ } else if (t.type == SEG_BEZLIN || t.type == SEG_BEZTRK) {
+ dd +=CornuLength(t.u.b.pos,t.bezSegs);
+ } else if (t.type == SEG_STRLIN || t.type == SEG_STRTRK ) {
+ dd += FindDistance(t.u.l.pos[0],t.u.l.pos[1]);
+ }
+ }
+ return dd;
+}
+
+DIST_T CornuMinRadius(coOrd pos[4],dynArr_t segs) {
+ DIST_T r = 100000.0, rr;
+ if (segs.cnt == 0 ) return r;
+ for (int i = 0;i<segs.cnt;i++) {
+ trkSeg_t t = DYNARR_N(trkSeg_t, segs, i);
+ if (t.type == SEG_CRVTRK || t.type == SEG_CRVLIN) {
+ rr = fabs(t.u.c.radius);
+ } else if (t.type == SEG_BEZLIN || t.type == SEG_BEZTRK) {
+ rr = CornuMinRadius(t.u.b.pos, t.bezSegs);
+ } else rr = 100000.00;
+ if (rr<r) r = rr;
+ }
+ return r;
+}
+
+DIST_T CornuTotalWindingArc(coOrd pos[4],dynArr_t segs) {
+ DIST_T rr = 0;
+ if (segs.cnt == 0 ) return 0;
+ for (int i = 0;i<segs.cnt;i++) {
+ trkSeg_t t = DYNARR_N(trkSeg_t, segs, i);
+ if (t.type == SEG_CRVTRK || t.type == SEG_CRVLIN) {
+ rr += t.u.c.a1;
+ } else if (t.type == SEG_BEZLIN || t.type == SEG_BEZTRK) {
+ rr += CornuTotalWindingArc(t.u.b.pos, t.bezSegs);
+ }
+ }
+ return rr;
+}
+
+DIST_T CornuMaxRateofChangeofCurvature(coOrd pos[4], dynArr_t segs, DIST_T * last_c) {
+ DIST_T r_max = 0.0, rc, lc = 0;
+ lc = * last_c;
+ segProcData_t segProcData;
+ if (segs.cnt == 0 ) return r_max;
+ for (int i = 0;i<segs.cnt;i++) {
+ trkSeg_t t = DYNARR_N(trkSeg_t, segs, i);
+ if (t.type == SEG_FILCRCL) continue;
+ SegProc(SEGPROC_LENGTH,&t,&segProcData);
+ if (t.type == SEG_CRVTRK || t.type == SEG_CRVLIN) {
+ rc = fabs(1/t.u.c.radius - lc)/segProcData.length.length/2;
+ lc = 1/t.u.c.radius;
+ } else if (t.type == SEG_BEZLIN || t.type == SEG_BEZTRK) {
+ rc = CornuMaxRateofChangeofCurvature(t.u.b.pos, t.bezSegs,&lc); //recurse
+ } else {
+ rc = fabs(0-lc)/segProcData.length.length/2;
+ lc = 0;
+ }
+ if (rc>r_max) r_max = rc;
+ }
+ * last_c = lc;
+ return r_max;
+}
+
+/*
+ * Create a Cornu Curve Track
+ * Sequence is
+ * 1. Place 1st End
+ * 2. Place 2nd End
+ * 3 to n. Select and drag around points until done
+ * n+1. Confirm with enter or Cancel with Esc
+ */
+STATUS_T CmdCornu( wAction_t action, coOrd pos )
+{
+ track_p t;
+ cornuParm_t cp;
+
+ Da.color = lineColor;
+ Da.width = (double)lineWidth/mainD.dpi;
+
+ switch (action&0xFF) {
+
+ case C_START:
+ Da.state = NONE;
+ Da. selectPoint = -1;
+ for (int i=0;i<2;i++) {
+ Da.pos[i] = zero;
+ }
+ Da.trk[0] = Da.trk[1] = NULL;
+ //tempD.orig = mainD.orig;
+
+ DYNARR_RESET(trkSeg_t,Da.crvSegs_da);
+ Da.ep1Segs_da_cnt = 0;
+ Da.ep2Segs_da_cnt = 0;
+ Da.extend[0] = FALSE;
+ Da.extend[1] = FALSE;
+ if (selectedTrackCount==0)
+ InfoMessage( _("Left click - join with Cornu track") );
+ else
+ InfoMessage( _("Left click - join with Cornu track, Shift Left click - move to join") );
+ return C_CONTINUE;
+
+ case C_DOWN:
+ if ( Da.state == NONE || Da.state == LOC_2) { //Set the first or second point
+ coOrd p = pos;
+ int end = Da.state==NONE?0:1;
+ EPINX_T ep;
+ if ((t = OnTrack(&p, FALSE, TRUE)) != NULL) {
+ if (QueryTrack(t,Q_HAS_VARIABLE_ENDPOINTS)) { //Circle/Helix find if there is an open slot and where
+ if ((GetTrkEndTrk(t,0) != NULL) && (GetTrkEndTrk(t,1) != NULL)) {
+ InfoMessage(_("Helix Already Connected"));
+ return C_CONTINUE;
+ }
+ ep = -1; //Not a real ep yet
+ } else ep = PickUnconnectedEndPointSilent(p, t);
+ if (ep>=0 && QueryTrack(t,Q_CAN_ADD_ENDPOINTS)) ep=-1; //Ignore Turntable Unconnected
+ else if (ep==-1 && (!QueryTrack(t,Q_CAN_ADD_ENDPOINTS) && !QueryTrack(t,Q_HAS_VARIABLE_ENDPOINTS))) { //No endpoints and not Turntable or Helix/Circle
+ wBeep();
+ InfoMessage(_("No Unconnected end point on that track"));
+ return C_CONTINUE;
+ }
+ Da.trk[end] = t;
+ Da.ep[end] = ep; // Note: -1 for Turntable or Circle
+ if (ep ==-1) pos = p;
+ else pos = GetTrkEndPos(t,ep);
+ Da.pos[end] = pos;
+ InfoMessage( _("Place 2nd end point of Cornu track on track with an unconnected end-point") );
+ } else {
+ wBeep();
+ InfoMessage(_("No Unconnected Track End there"));
+ return C_CONTINUE;
+ }
+ if (Da.state == NONE) {
+ if (!GetConnectedTrackParms(t, pos, 0, Da.ep[0])) {
+ Da.trk[0] = NULL;
+ return C_CONTINUE;
+ }
+ Da.state = POS_1;
+ Da.selectPoint = 0;
+ Da.ep1Segs_da_cnt = createEndPoint(Da.ep1Segs, Da.pos[0], FALSE, !QueryTrack(Da.trk[0],Q_IS_CORNU),QueryTrack(Da.trk[0],Q_CORNU_CAN_MODIFY));
+ DrawCornuCurve(NULL,Da.ep1Segs,Da.ep1Segs_da_cnt,NULL,0,NULL,0,NULL,NULL,NULL,drawColorBlack);
+ InfoMessage( _("Move 1st end point of Cornu track along track 1") );
+ } else {
+ if ( Da.trk[0] == t) {
+ ErrorMessage( MSG_JOIN_CORNU_SAME );
+ Da.trk[1] = NULL;
+ return C_CONTINUE;
+ }
+ if (!GetConnectedTrackParms(t, pos, 1, Da.ep[1])) {
+ Da.trk[1] = NULL; //Turntable Fail
+ return C_CONTINUE;
+ }
+ CorrectHelixAngles();
+ Da.selectPoint = 1;
+ Da.state = POINT_PICKED;
+ DrawCornuCurve(NULL,Da.ep1Segs,Da.ep1Segs_da_cnt,NULL,0,NULL,0,NULL,NULL,NULL,drawColorBlack); //Wipe out initial Arm
+ CreateBothEnds(1);
+ if (CallCornu(Da.pos,Da.trk,Da.ep,&Da.crvSegs_da, &cp))
+ Da.crvSegs_da_cnt = Da.crvSegs_da.cnt;
+ DrawTempCornu();
+ InfoMessage( _("Move 2nd end point of Cornu track along track 2") );
+ }
+ return C_CONTINUE;
+ } else {
+ return AdjustCornuCurve( action&0xFF, pos, InfoMessage );
+ }
+ return C_CONTINUE;
+
+ case C_MOVE:
+ if (Da.state == NONE) {
+ InfoMessage("Place 1st end point of Cornu track on unconnected end-point");
+ return C_CONTINUE;
+ }
+ if (Da.state == POS_1) {
+ EPINX_T ep = 0;
+ BOOL_T found = FALSE;
+ int end = Da.state==POS_1?0:1;
+ if(!QueryTrack(Da.trk[0],Q_CORNU_CAN_MODIFY) && !QueryTrack(Da.trk[0],Q_CAN_ADD_ENDPOINTS)) {
+ InfoMessage(_("Can't Split - Locked to End Point"));
+ return C_CONTINUE;
+ }
+ if (Da.trk[0] != OnTrack(&pos, FALSE, TRUE)) {
+ wBeep();
+ InfoMessage(_("Point not on track 1"));
+ Da.state = POS_1;
+ Da.selectPoint = 1;
+ return C_CONTINUE;
+ }
+ t = Da.trk[0];
+ DrawCornuCurve(NULL,Da.ep1Segs,Da.ep1Segs_da_cnt,NULL,0,NULL,0,NULL,NULL,NULL,drawColorBlack);
+ if (!GetConnectedTrackParms(t, pos, ep, Da.ep[ep])) {
+ Da.state = POS_1;
+ Da.selectPoint = 1;
+ return C_CONTINUE;
+ }
+ Da.pos[ep] = pos;
+ Da.ep1Segs_da_cnt = createEndPoint(Da.ep1Segs, Da.pos[0],TRUE,!QueryTrack(Da.trk[0],Q_IS_CORNU),QueryTrack(Da.trk[0],Q_CORNU_CAN_MODIFY));
+ DrawCornuCurve(NULL,Da.ep1Segs,Da.ep1Segs_da_cnt,NULL,0,NULL,0,NULL,NULL,NULL,drawColorBlack);
+ } else {
+ return AdjustCornuCurve( action&0xFF, pos, InfoMessage );
+ }
+ return C_CONTINUE;
+
+ case C_UP:
+ if (Da.state == POS_1 && Da.trk[0]) {
+ DrawCornuCurve(NULL,Da.ep1Segs,Da.ep1Segs_da_cnt,NULL,0,NULL,0,NULL,NULL,NULL,drawColorBlack);
+ Da.state = LOC_2;
+ InfoMessage( _("Put other end of Cornu on a track with an unconnected end point") );
+ Da.ep1Segs_da_cnt = createEndPoint(Da.ep1Segs, Da.pos[0], FALSE,!QueryTrack(Da.trk[0],Q_IS_CORNU),QueryTrack(Da.trk[0],Q_CORNU_CAN_MODIFY));
+ DrawCornuCurve(NULL,Da.ep1Segs,Da.ep1Segs_da_cnt,NULL,0,NULL,0,NULL,NULL,NULL,drawColorBlack);
+ return C_CONTINUE;
+ } else {
+ return AdjustCornuCurve( action&0xFF, pos, InfoMessage );
+ }
+ case C_TEXT:
+ if (Da.state != PICK_POINT || (action>>8) != 32) //Space is same as Enter.
+ return C_CONTINUE;
+ /* no break */
+ case C_OK:
+ if (Da.state != PICK_POINT) return C_CONTINUE;
+ return AdjustCornuCurve( C_OK, pos, InfoMessage);
+
+ case C_REDRAW:
+ if ( Da.state != NONE ) {
+ DrawCornuCurve(NULL,Da.ep1Segs,Da.ep1Segs_da_cnt,Da.ep2Segs,Da.ep2Segs_da_cnt,(trkSeg_t *)Da.crvSegs_da.ptr,Da.crvSegs_da.cnt, NULL, &Da.extendSeg[0],&Da.extendSeg[1],Da.color);
+ }
+ return C_CONTINUE;
+
+ case C_CANCEL:
+ if (Da.state != NONE) {
+ DrawCornuCurve(NULL,Da.ep1Segs,Da.ep1Segs_da_cnt,Da.ep2Segs,Da.ep2Segs_da_cnt,(trkSeg_t *)Da.crvSegs_da.ptr,Da.crvSegs_da.cnt, NULL, &Da.extendSeg[0],&Da.extendSeg[1],Da.color);
+ Da.ep1Segs_da_cnt = 0;
+ Da.ep2Segs_da_cnt = 0;
+ Da.crvSegs_da_cnt = 0;
+ for (int i=0;i<2;i++) {
+ Da.radius[i] = 0.0;
+ Da.angle[i] = 0.0;
+ Da.center[i] = zero;
+ Da.trk[i] = NULL;
+ Da.ep[i] = -1;
+ Da.pos[i] = zero;
+ }
+ //DYNARR_FREE(trkSeg_t,Da.crvSegs_da);
+ }
+ Da.state = NONE;
+ return C_CONTINUE;
+
+ default:
+
+ return C_CONTINUE;
+ }
+
+}
+
+
+EXPORT void InitCmdCornu( wMenu_p menu )
+{
+
+}
diff --git a/app/bin/ccornu.h b/app/bin/ccornu.h
new file mode 100644
index 0000000..a11e713
--- /dev/null
+++ b/app/bin/ccornu.h
@@ -0,0 +1,23 @@
+/*
+ * ccornu.h
+ *
+ * Created on: May 28, 2017
+ * Author: richardsa
+ */
+
+#ifndef APP_BIN_CCORNU_H_
+#define APP_BIN_CCORNU_H_
+
+
+typedef void (*cornuMessageProc)( char *, ... );
+
+
+#endif /* APP_BIN_CCORNU_H_ */
+
+STATUS_T CmdCornu( wAction_t action, coOrd pos );
+DIST_T CornuMinRadius(coOrd pos[4],dynArr_t segs);
+DIST_T CornuMaxRateofChangeofCurvature(coOrd pos[4],dynArr_t segs,DIST_T * last_c);
+DIST_T CornuLength(coOrd pos[4],dynArr_t segs);
+DIST_T CornuTotalWindingArc(coOrd pos[4],dynArr_t segs);
+
+STATUS_T CmdCornuModify (track_p trk, wAction_t action, coOrd pos);
diff --git a/app/bin/ccurve.c b/app/bin/ccurve.c
index b284669..58bb5c1 100644
--- a/app/bin/ccurve.c
+++ b/app/bin/ccurve.c
@@ -1,8 +1,5 @@
-/*
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/ccurve.c,v 1.4 2008-03-06 19:35:04 m_fischer Exp $
- *
+/** \file ccurve.c
* CURVE
- *
*/
/* XTrkCad - Model Railroad CAD
@@ -23,12 +20,25 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-#include "track.h"
+#include <math.h>
+#include <string.h>
+
#include "ccurve.h"
-#include "cstraigh.h"
+
#include "cjoin.h"
+#include "cstraigh.h"
+#include "cundo.h"
+#include "custom.h"
+#include "fileio.h"
#include "i18n.h"
+#include "messages.h"
+#include "param.h"
+#include "param.h"
+#include "track.h"
+#include "utility.h"
+#include "wlib.h"
+#include "cbezier.h"
/*
* STATE INFO
@@ -39,12 +49,15 @@ static struct {
coOrd pos0;
coOrd pos1;
curveData_t curveData;
+ track_p trk;
+ EPINX_T ep;
+ BOOL_T down;
} Da;
static long curveMode;
-static void DrawArrowHeads(
+EXPORT void DrawArrowHeads(
trkSeg_p sp,
coOrd pos,
ANGLE_T angle,
@@ -89,26 +102,34 @@ EXPORT STATUS_T CreateCurve(
long mode,
curveMessageProc message )
{
+ track_p t;
DIST_T d;
- ANGLE_T a;
- static coOrd pos0;
+ ANGLE_T a, angle1, angle2;
+ static coOrd pos0, p;
int inx;
switch ( action ) {
case C_START:
DYNARR_SET( trkSeg_t, tempSegs_da, 8 );
+ Da.down = FALSE; //Not got a valid start yet
switch ( curveMode ) {
case crvCmdFromEP1:
- InfoMessage( _("Drag from End-Point in direction of curve") );
+ if (track)
+ message(_("Drag from End-Point in direction of curve - Shift locks to track open end-point") );
+ else
+ message (_("Drag from End-Point in direction of curve") );
break;
case crvCmdFromTangent:
- InfoMessage( _("Drag from End-Point to Center") );
+ if (track)
+ message(_("Drag from End-Point to Center - Shift locks to track open end-point") );
+ else
+ message(_("Drag from End-Point to Center") );
break;
case crvCmdFromCenter:
- InfoMessage( _("Drag from Center to End-Point") );
+ message(_("Drag from Center to End-Point") );
break;
case crvCmdFromChord:
- InfoMessage( _("Drag to other end of chord") );
+ message(_("Drag from one to other end of chord") );
break;
}
return C_CONTINUE;
@@ -118,14 +139,40 @@ EXPORT STATUS_T CreateCurve(
tempSegs(inx).width = 0;
}
tempSegs_da.cnt = 0;
- SnapPos( &pos );
+ p = pos;
+ BOOL_T found = FALSE;
+ Da.trk = NULL;
+ if ((mode == crvCmdFromEP1 || mode == crvCmdFromTangent) && track && (MyGetKeyState() & WKEY_SHIFT) != 0) {
+ if ((t = OnTrack(&p, FALSE, TRUE)) != NULL) {
+ EPINX_T ep = PickUnconnectedEndPointSilent(p, t);
+ if (ep != -1) {
+ Da.trk = t;
+ Da.ep = ep;
+ pos = GetTrkEndPos(t, ep);
+ found = TRUE;
+ } else {
+ Da.pos0=pos;
+ message(_("No unconnected end-point on track - Try again or release Shift and click"));
+ return C_CONTINUE;
+ }
+ } else {
+ Da.pos0=pos;
+ message(_("Not on a track - Try again or release Shift and click"));
+ return C_CONTINUE;
+ }
+ Da.down = TRUE;
+ }
+ Da.down = TRUE;
+ if (!found) SnapPos( &pos );
pos0 = pos;
+ Da.pos0 = pos;
switch (mode) {
case crvCmdFromEP1:
tempSegs(0).type = (track?SEG_STRTRK:SEG_STRLIN);
tempSegs(0).color = color;
tempSegs(0).width = width;
- message( _("Drag to set angle") );
+ if (Da.trk) message(_("End Locked: Drag out curve start"));
+ else message(_("Drag along curve start") );
break;
case crvCmdFromTangent:
case crvCmdFromCenter:
@@ -135,12 +182,14 @@ EXPORT STATUS_T CreateCurve(
tempSegs(1).u.c.a0 = 0;
tempSegs(1).u.c.a1 = 360;
tempSegs(2).type = SEG_STRLIN;
- message( mode==crvCmdFromTangent?_("Drag from End-Point to Center"):_("Drag from Center to End-Point") );
+ if (Da.trk && mode==crvCmdFromTangent) message(_("End Locked: Drag out to center"));
+ else
+ message( mode==crvCmdFromTangent?_("Drag from End-Point to Center"):_("Drag from Center to End-Point") );
break;
case crvCmdFromChord:
tempSegs(0).type = (track?SEG_STRTRK:SEG_STRLIN);
tempSegs(0).color = color;
- tempSegs(0).width = width;
+ tempSegs(0).width = width;
message( _("Drag to other end of chord") );
break;
}
@@ -148,16 +197,34 @@ EXPORT STATUS_T CreateCurve(
return C_CONTINUE;
case C_MOVE:
+ if (!Da.down) return C_CONTINUE;
+ if (Da.trk) {
+ angle1 = NormalizeAngle(GetTrkEndAngle(Da.trk, Da.ep));
+ angle2 = NormalizeAngle(FindAngle(pos, pos0)-angle1);
+ if (mode ==crvCmdFromEP1) {
+ if (angle2 > 90.0 && angle2 < 270.0)
+ Translate( &pos, pos0, angle1, -FindDistance( pos0, pos )*cos(D2R(angle2)) );
+ else pos = pos0;
+ } else {
+ DIST_T dp = -FindDistance(pos0, pos)*sin(D2R(angle2));
+ if (angle2 > 180.0)
+ Translate( &pos, pos0, angle1+90.0, dp );
+ else
+ Translate( &pos, pos0, angle1-90.0, dp );
+ }
+ } else SnapPos(&pos);
tempSegs(0).u.l.pos[1] = pos;
d = FindDistance( pos0, pos );
a = FindAngle( pos0, pos );
switch ( mode ) {
case crvCmdFromEP1:
- message( _("Angle=%0.3f"), PutAngle(a) );
+ if (Da.trk) message( _("Start Locked: Drag out curve start - Angle=%0.3f"), PutAngle(a));
+ else message( _("Drag out curve start - Angle=%0.3f"), PutAngle(a) );
tempSegs_da.cnt = 1;
break;
case crvCmdFromTangent:
- message( _("Radius=%s Angle=%0.3f"), FormatDistance(d), PutAngle(a) );
+ if (Da.trk) message( _("Tangent Locked: Drag out center - Radius=%s Angle=%0.3f"), FormatDistance(d), PutAngle(a) );
+ else message( _("Drag out center - Radius=%s Angle=%0.3f"), FormatDistance(d), PutAngle(a) );
tempSegs(1).u.c.center = pos;
DrawArrowHeads( &tempSegs(2), pos0, FindAngle(pos0,pos)+90, TRUE, wDrawColorBlack );
tempSegs_da.cnt = 7;
@@ -181,10 +248,30 @@ EXPORT STATUS_T CreateCurve(
break;
}
return C_CONTINUE;
-
case C_UP:
+ if (!Da.down) return C_CONTINUE;
+ if (Da.trk) {
+ angle1 = NormalizeAngle(GetTrkEndAngle(Da.trk, Da.ep));
+ angle2 = NormalizeAngle(FindAngle(pos, pos0)-angle1);
+ if (mode == crvCmdFromEP1) {
+ if (angle2 > 90.0 && angle2 < 270.0) {
+ Translate( &pos, pos0, angle1, -FindDistance( pos0, pos )*cos(D2R(angle2)) );
+ Da.pos1 = pos;
+ } else {
+ ErrorMessage( MSG_TRK_TOO_SHORT, "Curved ", PutDim(0.0) );
+ return C_TERMINATE;
+ }
+ } else {
+ DIST_T dp = -FindDistance(pos0, pos)*sin(D2R(angle2));
+ if (angle2 > 180.0)
+ Translate( &pos, pos0, angle1+90.0, dp );
+ else
+ Translate( &pos, pos0, angle1-90.0, dp );
+ Da.pos1 = pos;
+ }
+ }
switch (mode) {
- case crvCmdFromEP1:
+ case crvCmdFromEP1:
DrawArrowHeads( &tempSegs(1), pos, FindAngle(pos,pos0)+90, TRUE, drawColorRed );
tempSegs_da.cnt = 6;
break;
@@ -221,7 +308,9 @@ static STATUS_T CmdCurve( wAction_t action, coOrd pos )
case C_START:
curveMode = (long)commandContext;
Da.state = -1;
+ Da.pos0 = pos;
tempSegs_da.cnt = 0;
+ STATUS_T rcode;
return CreateCurve( action, pos, TRUE, wDrawColorBlack, 0, curveMode, InfoMessage );
case C_TEXT:
@@ -232,25 +321,29 @@ static STATUS_T CmdCurve( wAction_t action, coOrd pos )
case C_DOWN:
if ( Da.state == -1 ) {
- SnapPos( &pos );
+ //SnapPos( &pos );
Da.pos0 = pos;
Da.state = 0;
- return CreateCurve( action, pos, TRUE, wDrawColorBlack, 0, curveMode, InfoMessage );
+ rcode = CreateCurve( action, pos, TRUE, wDrawColorBlack, 0, curveMode, InfoMessage );
+ if (!Da.down) Da.state = -1;
+ return rcode;
+ //Da.pos0 = pos;
} else {
tempSegs_da.cnt = segCnt;
return C_CONTINUE;
}
case C_MOVE:
+ if (Da.state<0) return C_CONTINUE;
mainD.funcs->options = wDrawOptTemp;
DrawSegs( &mainD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack );
if ( Da.state == 0 ) {
- SnapPos( &pos );
- Da.pos1 = pos;
+ Da.pos1 = pos;
rc = CreateCurve( action, pos, TRUE, wDrawColorBlack, 0, curveMode, InfoMessage );
} else {
- SnapPos( &pos );
- PlotCurve( curveMode, Da.pos0, Da.pos1, pos, &Da.curveData, TRUE );
+ // SnapPos( &pos );
+ if (Da.trk) PlotCurve( curveMode, Da.pos0, Da.pos1, pos, &Da.curveData, FALSE );
+ else PlotCurve( curveMode, Da.pos0, Da.pos1, pos, &Da.curveData, TRUE );
if (Da.curveData.type == curveTypeStraight) {
tempSegs(0).type = SEG_STRTRK;
tempSegs(0).u.l.pos[0] = Da.pos0;
@@ -290,6 +383,7 @@ static STATUS_T CmdCurve( wAction_t action, coOrd pos )
case C_UP:
+ if (Da.state<0) return C_CONTINUE;
mainD.funcs->options = wDrawOptTemp;
DrawSegs( &mainD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack );
if (Da.state == 0) {
@@ -313,6 +407,10 @@ static STATUS_T CmdCurve( wAction_t action, coOrd pos )
}
UndoStart( _("Create Straight Track"), "newCurve - straight" );
t = NewStraightTrack( Da.pos0, Da.curveData.pos1 );
+ if (Da.trk) {
+ EPINX_T ep = PickUnconnectedEndPoint(Da.pos0, t);
+ if (ep != -1) ConnectTracks(Da.trk, Da.ep, t, ep);
+ }
UndoEnd();
} else if (Da.curveData.type == curveTypeCurve) {
if ((d= Da.curveData.curveRadius * Da.curveData.a1 *2.0*M_PI/360.0) <= minLength) {
@@ -322,6 +420,10 @@ static STATUS_T CmdCurve( wAction_t action, coOrd pos )
UndoStart( _("Create Curved Track"), "newCurve - curve" );
t = NewCurvedTrack( Da.curveData.curvePos, Da.curveData.curveRadius,
Da.curveData.a0, Da.curveData.a1, 0 );
+ if (Da.trk) {
+ EPINX_T ep = PickUnconnectedEndPoint(Da.pos0, t);
+ if (ep != -1) ConnectTracks(Da.trk, Da.ep, t, ep);
+ }
UndoEnd();
} else {
return C_ERROR;
@@ -344,6 +446,7 @@ static STATUS_T CmdCurve( wAction_t action, coOrd pos )
DrawSegs( &mainD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack );
mainD.funcs->options = 0;
tempSegs_da.cnt = 0;
+ Da.trk = NULL;
}
Da.state = -1;
return C_CONTINUE;
@@ -381,12 +484,12 @@ static void ComputeHelix( paramGroup_p, int, void * );
static paramFloatRange_t r0_360 = { 0, 360 };
static paramFloatRange_t r0_1000000 = { 0, 1000000 };
static paramIntegerRange_t i1_1000000 = { 1, 1000000 };
-static paramFloatRange_t r1_1000000 = { 1, 1000000 };
+static paramFloatRange_t r1_10000 = { 1, 10000 };
static paramFloatRange_t r0_100= { 0, 100 };
static paramData_t helixPLs[] = {
{ PD_FLOAT, &helixElev, "elev", PDO_DIM, &r0_1000000, N_("Elevation Difference") },
- { PD_FLOAT, &helixRadius, "radius", PDO_DIM, &r1_1000000, N_("Radius") },
+ { PD_FLOAT, &helixRadius, "radius", PDO_DIM, &r1_10000, N_("Radius") },
{ PD_LONG, &helixTurns, "turns", 0, &i1_1000000, N_("Turns") },
{ PD_FLOAT, &helixAngSep, "angSep", 0, &r0_360, N_("Angular Separation") },
{ PD_FLOAT, &helixGrade, "grade", 0, &r0_100, N_("Grade") },
@@ -396,7 +499,7 @@ static paramData_t helixPLs[] = {
static paramGroup_t helixPG = { "helix", PGO_PREFMISCGROUP, helixPLs, sizeof helixPLs/sizeof helixPLs[0] };
static paramData_t circleRadiusPLs[] = {
- { PD_FLOAT, &circleRadius, "radius", PDO_DIM, &r1_1000000 } };
+ { PD_FLOAT, &circleRadius, "radius", PDO_DIM, &r1_10000 } };
static paramGroup_t circleRadiusPG = { "circle", 0, circleRadiusPLs, sizeof circleRadiusPLs/sizeof circleRadiusPLs[0] };
@@ -604,7 +707,19 @@ static STATUS_T CmdCircleCommon( wAction_t action, coOrd pos, BOOL_T helix )
case C_UP:
DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack );
+ if (helixRadius > mapD.size.x && helixRadius > mapD.size.y) {
+ ErrorMessage( MSG_RADIUS_TOO_BIG );
+ return C_ERROR;
+ }
+ if (circleRadius > mapD.size.x && circleRadius > mapD.size.y) {
+ ErrorMessage( MSG_RADIUS_TOO_BIG );
+ return C_ERROR;
+ }
if ( helix ) {
+ if (helixRadius > 10000) {
+ ErrorMessage( MSG_RADIUS_GTR_10000 );
+ return C_ERROR;
+ }
UndoStart( _("Create Helix Track"), "newHelix" );
t = NewCurvedTrack( tempSegs(0).u.c.center, helixRadius, 0.0, 0.0, helixTurns );
} else {
@@ -612,6 +727,10 @@ static STATUS_T CmdCircleCommon( wAction_t action, coOrd pos, BOOL_T helix )
ErrorMessage( MSG_RADIUS_GTR_0 );
return C_ERROR;
}
+ if ((circleRadius > 100000) || (helixRadius > 10000)) {
+ ErrorMessage( MSG_RADIUS_GTR_10000 );
+ return C_ERROR;
+ }
UndoStart( _("Create Circle Track"), "newCircle" );
t = NewCurvedTrack( tempSegs(0).u.c.center, circleRadius, 0.0, 0.0, 0 );
}
@@ -655,56 +774,15 @@ static STATUS_T CmdHelix( wAction_t action, coOrd pos )
return CmdCircleCommon( action, pos, TRUE );
}
-#ifdef LATER
-static struct {
- coOrd pos;
- DIST_T radius;
- } Dc2;
-
-
-static STATUS_T CmdCircle2( wAction_t action, coOrd pos )
-{
-
- switch (action) {
-
- case C_START:
- InfoMessage( _("Place circle center") );
- return C_CONTINUE;
-
- case C_DOWN:
- Dc2.pos = pos;
- InfoMessage( _("Drag to set radius") );
- return C_CONTINUE;
-
- case C_MOVE:
- dc2.radius = ConstrainR( FindDistance( Dc2.pos, pos ) );
- InfoMessage( "%s", FormatDistance(dc2.radius) );
- return C_CONTINUE;
-
- case C_UP:
- curCommand = cmdCircle;
- InfoMessage( _("Place circle") );
- return C_CONTINUE;
-
- default:
- return C_CONTINUE;
- }
-}
-#endif
-
-
-
-#include "bitmaps/helix.xpm"
#include "bitmaps/curve1.xpm"
#include "bitmaps/curve2.xpm"
#include "bitmaps/curve3.xpm"
#include "bitmaps/curve4.xpm"
+#include "bitmaps/bezier.xpm"
#include "bitmaps/circle1.xpm"
#include "bitmaps/circle2.xpm"
#include "bitmaps/circle3.xpm"
-
-
EXPORT void InitCmdCurve( wMenu_p menu )
{
@@ -713,6 +791,7 @@ EXPORT void InitCmdCurve( wMenu_p menu )
AddMenuButton( menu, CmdCurve, "cmdCurveTangent", _("Curve from Tangent"), wIconCreatePixMap( curve2_xpm ), LEVEL0_50, IC_STICKY|IC_POPUP2, ACCL_CURVE2, (void*)1 );
AddMenuButton( menu, CmdCurve, "cmdCurveCenter", _("Curve from Center"), wIconCreatePixMap( curve3_xpm ), LEVEL0_50, IC_STICKY|IC_POPUP2, ACCL_CURVE3, (void*)2 );
AddMenuButton( menu, CmdCurve, "cmdCurveChord", _("Curve from Chord"), wIconCreatePixMap( curve4_xpm ), LEVEL0_50, IC_STICKY|IC_POPUP2, ACCL_CURVE4, (void*)3 );
+ AddMenuButton( menu, CmdBezCurve, "cmdBezier", _("Bezier Curve"), wIconCreatePixMap(bezier_xpm), LEVEL0_50, IC_STICKY|IC_POPUP2, ACCL_BEZIER, (void*)bezCmdCreateTrack );
ButtonGroupEnd();
ButtonGroupBegin( _("Circle Track"), "cmdCurveSetCmd", _("Circle Tracks") );
@@ -726,10 +805,18 @@ EXPORT void InitCmdCurve( wMenu_p menu )
}
-EXPORT void InitCmdHelix( wMenu_p menu )
-{
- AddMenuButton( menu, CmdHelix, "cmdHelix", _("Helix"), wIconCreatePixMap(helix_xpm), LEVEL0_50, IC_STICKY|IC_POPUP2, ACCL_HELIX, NULL );
- ParamRegister( &helixPG );
- RegisterChangeNotification( ChangeHelixW );
+/**
+* Append the helix command to the pulldown menu. The helix doesn't use an icon, so it is only
+* available through the pulldown
+*
+* \param varname1 IN pulldown menu
+* \return
+*/
+void InitCmdHelix(wMenu_p menu)
+{
+ AddMenuButton(menu, CmdHelix, "cmdHelix", _("Helix"), NULL, LEVEL0_50,
+ IC_STICKY|IC_POPUP2, ACCL_HELIX, NULL);
+ ParamRegister(&helixPG);
+ RegisterChangeNotification(ChangeHelixW);
}
diff --git a/app/bin/ccurve.h b/app/bin/ccurve.h
index 1b2c7f6..c9d1c8c 100644
--- a/app/bin/ccurve.h
+++ b/app/bin/ccurve.h
@@ -1,5 +1,5 @@
-/*
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/ccurve.h,v 1.1 2005-12-07 15:47:36 rc-flyer Exp $
+/** \file ccurve.h
+ * Definitions for curve commands
*/
/* XTrkCad - Model Railroad CAD
@@ -20,6 +20,13 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
+#ifndef HAVE_CCURVE_H
+#define HAVE_CCURVE_H
+
+#include "draw.h"
+#include "track.h"
+#include "wlib.h"
+#include "utility.h"
typedef struct {
curveType_e type;
@@ -27,12 +34,14 @@ typedef struct {
coOrd pos1;
DIST_T curveRadius;
ANGLE_T a0, a1;
+ BOOL_T negative;
} curveData_t;
#define crvCmdFromEP1 (0)
#define crvCmdFromTangent (1)
#define crvCmdFromCenter (2)
#define crvCmdFromChord (3)
+#define crvCmdFromCornu (4)
#define circleCmdFixedRadius (0)
#define circleCmdFromTangent (1)
@@ -45,4 +54,7 @@ void PlotCurve( long, coOrd, coOrd, coOrd, curveData_t *, BOOL_T );
track_p NewCurvedTrack( coOrd, DIST_T, ANGLE_T, ANGLE_T, long );
DIST_T CurveDescriptionDistance( coOrd, track_p );
STATUS_T CurveDescriptionMove( track_p, wAction_t, coOrd );
-BOOL_T GetCurveMiddle( track_p, coOrd * );
+BOOL_T GetCurveMiddle( track_p , coOrd * );
+void DrawArrowHeads(trkSeg_p sp, coOrd pos, ANGLE_T angle, BOOL_T bidirectional, wDrawColor color );
+
+#endif // !HAVE_CCURVE_H
diff --git a/app/bin/cdraw.c b/app/bin/cdraw.c
index efdb51a..418f32a 100644
--- a/app/bin/cdraw.c
+++ b/app/bin/cdraw.c
@@ -20,12 +20,22 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-#include "track.h"
+#include <math.h>
+#include <stdint.h>
+#include <string.h>
+
#include "ccurve.h"
+#include "cbezier.h"
#include "drawgeom.h"
+#include "fileio.h"
#include "i18n.h"
+#include "messages.h"
+#include "param.h"
+#include "track.h"
+#include "utility.h"
+#include "misc.h"
-#include <stdint.h>
+extern TRKTYP_T T_BZRLIN;
extern void wSetSelectedFontSize(int size);
@@ -200,14 +210,15 @@ static struct {
wIndex_t dimenSize;
descPivot_t pivot;
wIndex_t fontSizeInx;
- char text[STR_SIZE];
- LAYER_T layer;
+ char text[STR_LONG_SIZE];
+ unsigned int layer;
+ char polyType[STR_SIZE];
} drawData;
-typedef enum { E0, E1, CE, RA, LN, AL, A1, A2, VC, LW, CO, BE, OR, DS, TP, TA, TS, TX, PV, LY } drawDesc_e;
+typedef enum { E0, E1, CE, RA, LN, AL, A1, A2, VC, LW, CO, BE, OR, DS, TP, TA, TS, TX, PV, LY, PT } drawDesc_e;
static descData_t drawDesc[] = {
-/*E0*/ { DESC_POS, N_("End Pt 1: X"), &drawData.endPt[0] },
-/*E1*/ { DESC_POS, N_("End Pt 2: X"), &drawData.endPt[1] },
-/*CE*/ { DESC_POS, N_("Center: X"), &drawData.center },
+/*E0*/ { DESC_POS, N_("End Pt 1: X,Y"), &drawData.endPt[0] },
+/*E1*/ { DESC_POS, N_("End Pt 2: X,Y"), &drawData.endPt[1] },
+/*CE*/ { DESC_POS, N_("Center: X,Y"), &drawData.center },
/*RA*/ { DESC_DIM, N_("Radius"), &drawData.radius },
/*LN*/ { DESC_DIM, N_("Length"), &drawData.length },
/*AL*/ { DESC_FLOAT, N_("Angle"), &drawData.angle },
@@ -219,12 +230,13 @@ static descData_t drawDesc[] = {
/*BE*/ { DESC_LIST, N_("Lumber"), &drawData.benchChoice },
/*OR*/ { DESC_LIST, N_("Orientation"), &drawData.benchOrient },
/*DS*/ { DESC_LIST, N_("Size"), &drawData.dimenSize },
-/*TP*/ { DESC_POS, N_("Origin: X"), &drawData.endPt[0] },
+/*TP*/ { DESC_POS, N_("Origin: X,Y"), &drawData.endPt[0] },
/*TA*/ { DESC_FLOAT, N_("Angle"), &drawData.angle },
/*TS*/ { DESC_EDITABLELIST, N_("Font Size"), &drawData.fontSizeInx },
-/*TX*/ { DESC_STRING, N_("Text"), &drawData.text },
+/*TX*/ { DESC_TEXT, N_("Text"), &drawData.text },
/*PV*/ { DESC_PIVOT, N_("Pivot"), &drawData.pivot },
/*LY*/ { DESC_LAYER, N_("Layer"), &drawData.layer },
+/*PT*/ { DESC_STRING, N_("Type"), &drawData.polyType },
{ DESC_NULL } };
int drawSegInx;
@@ -241,14 +253,15 @@ static void UpdateDraw( track_p trk, int inx, descData_p descUpd, BOOL_T final )
struct extraData *xx = GetTrkExtraData(trk);
trkSeg_p segPtr;
coOrd mid;
- const char * text;
long fontSize;
if ( drawSegInx==-1 )
return;
- if ( inx == -1 )
- return;
segPtr = &xx->segs[drawSegInx];
+ if ( inx == -1 ) {
+ if (segPtr->type != SEG_TEXT) return;
+ else inx = TX; //Always look at TextField for SEG_TEXT on "Done"
+ }
MainRedraw();
MapRedraw();
UndrawNewTrack( trk );
@@ -286,7 +299,7 @@ static void UpdateDraw( track_p trk, int inx, descData_p descUpd, BOOL_T final )
if ( segPtr->type != SEG_CRVLIN ) {
drawData.length = FindDistance( drawData.endPt[0], drawData.endPt[1] );
} else {
- drawData.length = segPtr->u.c.radius*2*M_PI*segPtr->u.c.a1/360.0;
+ drawData.length = fabs(segPtr->u.c.radius)*2*M_PI*segPtr->u.c.a1/360.0;
}
drawDesc[LN].mode |= DESC_CHANGE;
break;
@@ -375,12 +388,14 @@ static void UpdateDraw( track_p trk, int inx, descData_p descUpd, BOOL_T final )
segPtr->u.t.fontSize = fontSize;
break;
case TX:
- text = wStringGetValue( (wString_p)drawDesc[TX].control0 );
- if ( text && text[0] && strcmp( segPtr->u.t.string, text ) != 0 ) {
+ if ( wTextGetModified((wText_p)drawDesc[TX].control0 )) {
+ int len = wTextGetSize((wText_p)drawDesc[TX].control0);
MyFree( segPtr->u.t.string );
- segPtr->u.t.string = MyStrdup( text );
- /*(char*)drawDesc[TX].valueP = segPtr->u.t.string;*/
+ segPtr->u.t.string = (char *)MyMalloc(len+1);
+ wTextGetText((wText_p)drawDesc[TX].control0, segPtr->u.t.string, len+1);
+ segPtr->u.t.string[len] = '\0'; //Make sure of null term
}
+
break;
case LY:
SetTrkLayer( trk, drawData.layer);
@@ -400,6 +415,7 @@ static void DescribeDraw( track_p trk, char * str, CSIZE_T len )
trkSeg_p segPtr;
int inx;
char * title = NULL;
+ char * polyType = NULL;
DistanceSegs( xx->orig, xx->angle, xx->segCnt, xx->segs, &pos, &drawSegInx );
@@ -462,7 +478,7 @@ static void DescribeDraw( track_p trk, char * str, CSIZE_T len )
break;
case SEG_CRVLIN:
REORIGIN( drawData.center, segPtr->u.c.center, xx->angle, xx->orig );
- drawData.radius = segPtr->u.c.radius;
+ drawData.radius = fabs(segPtr->u.c.radius);
drawDesc[CE].mode =
drawDesc[RA].mode = 0;
if ( segPtr->u.c.a1 >= 360.0 ) {
@@ -479,7 +495,7 @@ static void DescribeDraw( track_p trk, char * str, CSIZE_T len )
break;
case SEG_FILCRCL:
REORIGIN( drawData.center, segPtr->u.c.center, xx->angle, xx->orig );
- drawData.radius = segPtr->u.c.radius;
+ drawData.radius = fabs(segPtr->u.c.radius);
drawDesc[CE].mode =
drawDesc[RA].mode = 0;
drawDesc[LW].mode = DESC_IGNORE;
@@ -488,21 +504,37 @@ static void DescribeDraw( track_p trk, char * str, CSIZE_T len )
case SEG_POLY:
drawData.pointCount = segPtr->u.p.cnt;
drawDesc[VC].mode = DESC_RO;
- title = _("Poly Line");
+ drawDesc[PT].mode = DESC_RO;
+ switch (segPtr->u.p.polyType) {
+ case RECTANGLE:
+ polyType = _("Rectangle");
+ break;
+ default:
+ polyType = _("Freeform");
+ }
+ strncpy( drawData.polyType, polyType, sizeof drawData.polyType );
+ title = _("Polygonal Line");
break;
case SEG_FILPOLY:
drawData.pointCount = segPtr->u.p.cnt;
drawDesc[VC].mode = DESC_RO;
drawDesc[LW].mode = DESC_IGNORE;
+ drawDesc[PT].mode = DESC_RO;
+ switch (segPtr->u.p.polyType) {
+ case RECTANGLE:
+ polyType =_("Rectangle");
+ break;
+ default:
+ polyType = _("Freeform");
+ }
+ strncpy( drawData.polyType, polyType, sizeof drawData.polyType );
title = _("Polygon");
break;
case SEG_TEXT:
REORIGIN( drawData.endPt[0], segPtr->u.t.pos, xx->angle, xx->orig );
- //drawData.angle = NormalizeAngle( segPtr->u.t.angle );
drawData.angle = NormalizeAngle( xx->angle );
strncpy( drawData.text, segPtr->u.t.string, sizeof drawData.text );
- /*drawData.fontSize = segPtr->u.t.fontSize;*/
- /*(char*)drawDesc[TX].valueP = segPtr->u.t.string;*/
+ drawData.text[sizeof drawData.text-1] ='\0';
drawDesc[TP].mode =
drawDesc[TS].mode =
drawDesc[TX].mode =
@@ -541,7 +573,7 @@ static void DescribeDraw( track_p trk, char * str, CSIZE_T len )
static void DrawDraw( track_p t, drawCmd_p d, wDrawColor color )
{
struct extraData * xx = GetTrkExtraData(t);
- if ( (d->options&DC_QUICK) == 0 )
+ if ( (d->funcs->options&DC_QUICK) == 0 )
DrawSegs( d, xx->orig, xx->angle, xx->segs, xx->segCnt, 0.0, color );
}
@@ -668,7 +700,7 @@ static ANGLE_T GetAngleDraw(
pos.x -= xx->orig.x;
pos.y -= xx->orig.y;
Rotate( &pos, zero, -xx->angle );
- angle = GetAngleSegs( xx->segCnt, xx->segs, pos, NULL );
+ angle = GetAngleSegs( xx->segCnt, xx->segs, &pos, NULL, NULL, NULL, NULL, NULL);
if ( ep0 ) *ep0 = -1;
if ( ep1 ) *ep1 = -1;
return NormalizeAngle( angle + xx->angle );
@@ -767,7 +799,69 @@ EXPORT BOOL_T OnTableEdgeEndPt( track_p trk, coOrd * pos )
return FALSE;
}
+EXPORT BOOL_T GetClosestEndPt( track_p trk, coOrd * pos)
+{
+ struct extraData *xx;
+
+ if (GetTrkType(trk) == T_DRAW) {
+ ignoredTableEdge = NULL;
+ xx = GetTrkExtraData(trk);
+ if (xx->segCnt < 1)
+ return FALSE;
+ DIST_T dd0,dd1;
+ coOrd p00,p0,p1;
+ p00 = *pos;
+ if (GetTrkType(trk) == T_DRAW) {
+ Rotate(&p00,xx->orig,-xx->angle);
+ p00.x -= xx->orig.x;
+ p00.y -= xx->orig.y;
+ switch (xx->segs[0].type) {
+ case SEG_CRVLIN:
+ PointOnCircle( &p0, xx->segs[0].u.c.center, fabs(xx->segs[0].u.c.radius), xx->segs[0].u.c.a0 );
+ dd0 = FindDistance( p00, p0);
+ PointOnCircle( &p1, xx->segs[0].u.c.center, fabs(xx->segs[0].u.c.radius), xx->segs[0].u.c.a0 + xx->segs[0].u.c.a1);
+ dd1 = FindDistance( p00, p1);
+ break;
+ case SEG_STRLIN:
+ dd0 = FindDistance( p00, xx->segs[0].u.l.pos[0]);
+ p0 = xx->segs[0].u.l.pos[0];
+ dd1 = FindDistance( p00, xx->segs[0].u.l.pos[1]);
+ p1 = xx->segs[0].u.l.pos[1];
+ break;
+ case SEG_BEZLIN:
+ dd0 = FindDistance( p00, xx->segs[0].u.b.pos[0]);
+ p0 = xx->segs[0].u.b.pos[0];
+ dd1 = FindDistance( p00, xx->segs[0].u.b.pos[3]);
+ p1 = xx->segs[0].u.b.pos[3];
+ break;
+ default:
+ return FALSE;
+ }
+ p0.x += xx->orig.x;
+ p0.y += xx->orig.y;
+ Rotate(&p0,xx->orig,xx->angle);
+ p1.x += xx->orig.x;
+ p1.y += xx->orig.y;
+ Rotate(&p1,xx->orig,xx->angle);
+ } else if (GetTrkType(trk) == T_BZRLIN) {
+ coOrd p0,p1;
+ xx = GetTrkExtraData(trk);
+ p0 = xx->segs[0].u.b.pos[0];
+ p1 = xx->segs[0].u.b.pos[3];
+ dd0 = FindDistance(p00,p0);
+ dd1 = FindDistance(p00,p1);
+ } else return FALSE;
+ if (dd0>dd1) {
+ * pos = p1;
+ return TRUE;
+ } else {
+ * pos = p0;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
static void DrawRedraw(void);
@@ -783,15 +877,13 @@ static void DrawRedraw( void )
MapRedraw();
}
-
static wIndex_t benchChoice;
static wIndex_t benchOrient;
static wIndex_t dimArrowSize;
-static wDrawColor lineColor;
+wDrawColor lineColor = 1;
+long lineWidth = 0;
static wDrawColor benchColor;
-#ifdef LATER
-static wIndex_t benchInx;
-#endif
+
static paramIntegerRange_t i0_100 = { 0, 100, 25 };
static paramData_t drawPLs[] = {
@@ -802,7 +894,11 @@ static paramData_t drawPLs[] = {
#define drawBenchColorPD (drawPLs[2])
{ PD_COLORLIST, &benchColor, "benchcolor", PDO_NORECORD, NULL, N_("Color") },
#define drawBenchChoicePD (drawPLs[3])
- { PD_DROPLIST, &benchChoice, "benchlist", PDO_NOPREF|PDO_NORECORD|PDO_LISTINDEX, (void*)80, N_("Lumber Type") },
+#ifdef WINDOWS
+ { PD_DROPLIST, &benchChoice, "benchlist", PDO_NOPREF|PDO_NORECORD|PDO_LISTINDEX, (void*)120, N_("Lumber Type") },
+#else
+ { PD_DROPLIST, &benchChoice, "benchlist", PDO_NOPREF|PDO_NORECORD|PDO_LISTINDEX, (void*)145, N_("Lumber Type") },
+#endif
#define drawBenchOrientPD (drawPLs[4])
#ifdef WINDOWS
{ PD_DROPLIST, &benchOrient, "benchorient", PDO_NOPREF|PDO_NORECORD|PDO_LISTINDEX, (void*)45, "", 0 },
@@ -832,6 +928,7 @@ static char * objectName[] = {
N_("Filled Circle"),
N_("Filled Box"),
N_("Polygon"),
+ N_("Bezier Line"),
NULL};
static STATUS_T CmdDraw( wAction_t action, coOrd pos )
@@ -842,6 +939,8 @@ static STATUS_T CmdDraw( wAction_t action, coOrd pos )
char * labels[3];
static char labelName[40];
+ wAction_t act2 = (action&0xFF) | (bezCmdCreateLine<<8);
+
switch (action&0xFF) {
case C_START:
@@ -870,6 +969,7 @@ static STATUS_T CmdDraw( wAction_t action, coOrd pos )
case OP_CIRCLE3:
case OP_BOX:
case OP_POLY:
+ case OP_BEZLIN:
controls[0] = drawWidthPD.control;
controls[1] = drawColorPD.control;
controls[2] = NULL;
@@ -879,6 +979,7 @@ static STATUS_T CmdDraw( wAction_t action, coOrd pos )
InfoSubstituteControls( controls, labels );
drawWidthPD.option &= ~PDO_NORECORD;
drawColorPD.option &= ~PDO_NORECORD;
+ lineWidth = drawCmdContext.Width;
break;
case OP_FILLCIRCLE2:
case OP_FILLCIRCLE3:
@@ -935,12 +1036,17 @@ static STATUS_T CmdDraw( wAction_t action, coOrd pos )
infoSubst = FALSE;
}
ParamGroupRecord( &drawPG );
+ if (drawCmdContext.Op == OP_BEZLIN) return CmdBezCurve(act2, pos);
DrawGeomMouse( C_START, pos, &drawCmdContext );
return C_CONTINUE;
case wActionLDown:
ParamLoadData( &drawPG );
+ if (drawCmdContext.Op == OP_BEZLIN) {
+ act2 = action | (bezCmdCreateLine<<8);
+ return CmdBezCurve(act2, pos);
+ }
if ( drawCmdContext.Op == OP_BENCH ) {
drawCmdContext.benchOption = GetBenchData( (long)wListGetItemContext((wList_p)drawBenchChoicePD.control, benchChoice ), benchOrient );
drawCmdContext.Color = benchColor;
@@ -963,22 +1069,29 @@ static STATUS_T CmdDraw( wAction_t action, coOrd pos )
case wActionRUp:
case wActionText:
case C_CMDMENU:
- SnapPos( &pos );
+ if (drawCmdContext.Op == OP_BEZLIN) return CmdBezCurve(act2, pos);
+ if (!((MyGetKeyState() & WKEY_SHIFT) != 0)) {
+ SnapPos( &pos );
+ }
return DrawGeomMouse( action, pos, &drawCmdContext );
case C_CANCEL:
InfoSubstituteControls( NULL, NULL );
+ if (drawCmdContext.Op == OP_BEZLIN) return CmdBezCurve(act2, pos);
return DrawGeomMouse( action, pos, &drawCmdContext );
case C_OK:
+ if (drawCmdContext.Op == OP_BEZLIN) return CmdBezCurve(act2, pos);
return DrawGeomMouse( (0x0D<<8|wActionText), pos, &drawCmdContext );
/*DrawOk( NULL );*/
case C_FINISH:
+ if (drawCmdContext.Op == OP_BEZLIN) return CmdBezCurve(act2, pos);
return DrawGeomMouse( (0x0D<<8|wActionText), pos, &drawCmdContext );
/*DrawOk( NULL );*/
case C_REDRAW:
+ if (drawCmdContext.Op == OP_BEZLIN) return CmdBezCurve(act2, pos);
return DrawGeomMouse( action, pos, &drawCmdContext );
default:
@@ -1004,6 +1117,7 @@ static STATUS_T CmdDraw( wAction_t action, coOrd pos )
#include "bitmaps/dfilbox.xpm"
#include "bitmaps/dpoly.xpm"
#include "bitmaps/dfilpoly.xpm"
+#include "bitmaps/dbezier.xpm"
typedef struct {
char **xpm;
@@ -1023,7 +1137,8 @@ static drawData_t dcurveCmds[] = {
{ dcurve1_xpm, OP_CURVE1, N_("Curve End"), N_("Draw Curve from End"), "cmdDrawCurveEndPt", ACCL_DRAWCURVE1 },
{ dcurve2_xpm, OP_CURVE2, N_("Curve Tangent"), N_("Draw Curve from Tangent"), "cmdDrawCurveTangent", ACCL_DRAWCURVE2 },
{ dcurve3_xpm, OP_CURVE3, N_("Curve Center"), N_("Draw Curve from Center"), "cmdDrawCurveCenter", ACCL_DRAWCURVE3 },
- { dcurve4_xpm, OP_CURVE4, N_("Curve Chord"), N_("Draw Curve from Chord"), "cmdDrawCurveChord", ACCL_DRAWCURVE4 } };
+ { dcurve4_xpm, OP_CURVE4, N_("Curve Chord"), N_("Draw Curve from Chord"), "cmdDrawCurveChord", ACCL_DRAWCURVE4 },
+ { dbezier_xpm, OP_BEZLIN, N_("Bezier Curve"), N_("Draw Bezier"), "cmdDrawBezierCurve", ACCL_DRAWBEZLINE } };
static drawData_t dcircleCmds[] = {
/*{ dcircle1_xpm, OP_CIRCLE1, "Circle Fixed Radius", "Draw Fixed Radius Circle", "cmdDrawCircleFixedRadius", ACCL_DRAWCIRCLE1 },*/
{ dcircle2_xpm, OP_CIRCLE3, N_("Circle Tangent"), N_("Draw Circle from Tangent"), "cmdDrawCircleTangent", ACCL_DRAWCIRCLE2 },
@@ -1052,12 +1167,11 @@ static drawStuff_t drawStuff[4];
static drawStuff_t drawStuff[4] = {
{ "cmdDrawLineSetCmd", N_("Straight Objects"), N_("Draw Straight Objects"), 4, dlineCmds },
- { "cmdDrawCurveSetCmd", N_("Curved Lines"), N_("Draw Curved Lines"), 4, dcurveCmds },
+ { "cmdDrawCurveSetCmd", N_("Curved Lines"), N_("Draw Curved Lines"), 5, dcurveCmds },
{ "cmdDrawCircleSetCmd", N_("Circle Lines"), N_("Draw Circles"), 4, dcircleCmds },
{ "cmdDrawShapeSetCmd", N_("Shapes"), N_("Draw Shapes"), 4, dshapeCmds} };
-
static void ChangeDraw( long changes )
{
wIndex_t choice, orient;
@@ -1079,6 +1193,15 @@ static void DrawDlgUpdate(
int inx,
void * valueP )
{
+ if (drawCmdContext.Op == OP_BEZLIN) {
+ if ( (inx == 0 && pg->paramPtr[inx].valueP == &drawCmdContext.Width) ||
+ (inx == 1 && pg->paramPtr[inx].valueP == &lineColor))
+ {
+ lineWidth = drawCmdContext.Width;
+ UpdateParms(lineColor, lineWidth);
+ }
+ }
+
if ( inx >= 0 && pg->paramPtr[inx].valueP == &benchChoice )
BenchUpdateOrientationList( (long)wListGetItemContext( (wList_p)drawBenchChoicePD.control, (wIndex_t)*(long*)valueP ), (wList_p)drawBenchOrientPD.control );
}
@@ -1108,9 +1231,7 @@ EXPORT void InitCmdDraw( wMenu_p menu )
ParamRegister( &drawPG );
RegisterChangeNotification( ChangeDraw );
-#ifdef LATER
- InitCommand( cmdDraw, N_("Draw"), draw_bits, LEVEL0_50, IC_POPUP|IC_CMDMENU, ACCL_DRAW );
-#endif
+
}
@@ -1168,7 +1289,6 @@ EXPORT track_p NewText(
return trk;
}
-
EXPORT BOOL_T ReadText( char * line )
{
coOrd pos;
@@ -1190,6 +1310,10 @@ EXPORT BOOL_T ReadText( char * line )
return FALSE;
}
+ char * old = text;
+ text = ConvertFromEscapedText(text);
+ MyFree(old);
+
trk = NewText( index, pos, angle, text, textSize, color );
SetTrkLayer( trk, layer );
MyFree(text);
diff --git a/app/bin/celev.c b/app/bin/celev.c
index 164ea43..2677f2e 100644
--- a/app/bin/celev.c
+++ b/app/bin/celev.c
@@ -1,5 +1,5 @@
-/*
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/celev.c,v 1.4 2008-03-06 19:35:05 m_fischer Exp $
+/** /file celev.c
+ * ELEVATION
*/
/* XTrkCad - Model Railroad CAD
@@ -19,16 +19,16 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
+#include <math.h>
+#include <string.h>
-#include "track.h"
#include "cselect.h"
+#include "cundo.h"
+#include "custom.h"
+#include "fileio.h"
#include "i18n.h"
-
-/*****************************************************************************
- *
- * ELEVATION
- *
- */
+#include "param.h"
+#include "track.h"
static wWin_p elevW;
diff --git a/app/bin/cgroup.c b/app/bin/cgroup.c
index 76b15ca..b173987 100644
--- a/app/bin/cgroup.c
+++ b/app/bin/cgroup.c
@@ -1,6 +1,4 @@
-/*
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/cgroup.c,v 1.2 2008-01-20 23:29:15 mni77 Exp $
- *
+/** \file cgroup.c
* Compound tracks: Group
*
*/
@@ -24,10 +22,23 @@
*/
#include <ctype.h>
-#include "track.h"
+#include <math.h>
+#include <string.h>
+
#include "compound.h"
-#include "shrtpath.h"
+#include "cundo.h"
+#include "custom.h"
+#include "fileio.h"
#include "i18n.h"
+#include "tbezier.h"
+#include "tcornu.h"
+#include "common.h"
+#include "messages.h"
+#include "param.h"
+#include "shrtpath.h"
+#include "track.h"
+#include "utility.h"
+
/*****************************************************************************
*
@@ -46,6 +57,10 @@ static char groupPartno[STR_SIZE];
static char groupTitle[STR_SIZE];
static int groupCompoundCount = 0;
+extern TRKTYP_T T_BZRTRK;
+extern TRKTYP_T T_BZRLIN;
+extern TRKTYP_T T_CORNU;
+
typedef struct {
int segInx;
EPINX_T segEP;
@@ -666,7 +681,11 @@ static char * FindPathBtwEP(
if ( ep1+ep2 != 1 )
AbortProg( "findPathBtwEP" );
*flip = ( ep1 == 1 );
- return "\1\0\0";
+ if (GetTrkType(trk) == T_CORNU ) { // Cornu doesn't have a path but lots of segs!
+ cp = CreateSegPathList(trk); // Make path
+LOG( log_group, 2, ( " Group: Cornu path:%s \n", cp ) )
+ } else cp = "\1\0\0"; //One segment (but could be a Bezier)
+ return cp;
}
cp = (char *)xx->paths;
pos1 = GetTrkEndPos(trk,ep1);
@@ -678,7 +697,7 @@ static char * FindPathBtwEP(
pos2.x -= xx->orig.x;
pos2.y -= xx->orig.y;
while ( cp[0] ) {
- cp += strlen(cp)+1;
+ cp += strlen(cp)+1; //Ignore Path Name
while ( cp[0] ) {
cp0 = cp;
epN = -1;
@@ -691,8 +710,8 @@ static char * FindPathBtwEP(
if ( epN != -1 ) {
GetSegInxEP( cp[-1], &segInx, &segEP );
if ( CheckTurnoutEndPoint( &xx->segs[segInx], epN==0?pos1:pos2, 1-segEP ) ) {
- *flip = epN==0;
- return cp0;
+ *flip = epN==0; // If its reversed, set up to be flipped or noted
+ return cp0; //Found path between EPs
}
}
cp++;
@@ -995,6 +1014,12 @@ static void GroupOk( void * junk )
DrawSegs( &groupD, xx->orig, xx->angle, segPtr, 1, trackGauge, wDrawColorBlack );
}
}
+ } else if (GetTrkType(trk) == T_BEZIER || GetTrkType(trk) == T_BZRLIN ) {
+ DYNARR_APPEND(trkSeg_t, trackSegs_da, 10);
+ segPtr = &trackSegs(trackSegs_da.cnt-1);
+ GetBezierSegmentFromTrack(trk,segPtr);
+ } else if (GetTrkType(trk) == T_CORNU) {
+ GetBezierSegmentsFromCornu(trk,&trackSegs_da); //Only give back Bezier - cant be undone
} else {
segCnt = tempSegs_da.cnt;
oldOptions = groupD.options;
@@ -1072,6 +1097,7 @@ static void GroupOk( void * junk )
}
/* Make sure no turnouts in groupTrk list have a path end which is not an EndPt */
+ //TODO Add Trap Points (which are Turnouts with a bumper track)
for ( inx=0; inx<groupTrk_da.cnt; inx++ ) {
trk = groupTrk(0).trk;
if ( GetTrkType( trk ) == T_TURNOUT ) {
@@ -1391,7 +1417,7 @@ if ( log_group >= 1 && logTable(log_group).level > log_group ) {
for ( pinx=0; pinx<trackSegs_da.cnt; pinx++ ) {
if ( segFlip(pinx) < 0 ) {
LOG( log_group, 1, ( "Flipping Segment %d\n", pinx+1 ) );
- SegProc( SEGPROC_FLIP, &trackSegs(pinx), NULL );
+ SegProc( SEGPROC_FLIP, &trackSegs(pinx), NULL );
}
}
@@ -1413,7 +1439,7 @@ LOG( log_group, 1, ( "Flipping Segment %d\n", pinx+1 ) );
if ( path == NULL )
AbortProg( "Missing Path T%d:%d.%d", GetTrkIndex(groupP->trk), ppp->ep2, ppp->ep1 );
if ( flip ) path += strlen((char *)path)-1;
- while ( *path ) {
+ while ( *path && (path >= ppp->path) ) { //Add Guard for flip backwards
DYNARR_APPEND( char, pathPtr_da, 10 );
pathChar = *path;
flip1 = flip;
@@ -1568,6 +1594,7 @@ EXPORT void DoGroup( void )
xx = NULL;
groupSegCnt = 0;
groupCompoundCount = 0;
+
while ( TrackIterate( &trk ) ) {
if ( GetTrkSelected( trk ) ) {
trkType = GetTrkType(trk);
@@ -1575,9 +1602,8 @@ EXPORT void DoGroup( void )
xx = GetTrkExtraData(trk);
groupSegCnt += xx->segCnt;
GroupCopyTitle( xtitle(xx) );
- } else {
+ } else
groupSegCnt += 1;
- }
}
}
if ( groupSegCnt <= 0 ) {
diff --git a/app/bin/chndldto.c b/app/bin/chndldto.c
index 2e1f826..fa88398 100644
--- a/app/bin/chndldto.c
+++ b/app/bin/chndldto.c
@@ -1,7 +1,5 @@
-/*
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/chndldto.c,v 1.4 2008-03-06 19:35:05 m_fischer Exp $
- *
- * CURVE
+/** \file chndlto.c
+ * Handlaid turnout
*
*/
@@ -23,13 +21,17 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-#include "track.h"
+#include <math.h>
+
#include "ccurve.h"
-#include "cstraigh.h"
#include "cjoin.h"
#include "compound.h"
-#include <math.h>
+#include "cstraigh.h"
+#include "cundo.h"
#include "i18n.h"
+#include "messages.h"
+#include "track.h"
+#include "utility.h"
#define PTRACE(X)
@@ -297,7 +299,7 @@ PTRACE(( " a2=%0.1f rA1=%0.1f\n", angle2, reverseA1 ))
ep2b = 0;
break;
case SEG_CRVTRK:
- trk2b = NewCurvedTrack( segP->u.c.center, segP->u.c.radius, segP->u.c.a0, segP->u.c.a1, 0 );
+ trk2b = NewCurvedTrack( segP->u.c.center, fabs(segP->u.c.radius), segP->u.c.a0, segP->u.c.a1, 0 );
ep2b = (right?0:1);
}
if (trk2 == NULL) {
diff --git a/app/bin/chotbar.c b/app/bin/chotbar.c
index f138cbb..188ad27 100644
--- a/app/bin/chotbar.c
+++ b/app/bin/chotbar.c
@@ -21,10 +21,13 @@
*/
#include <ctype.h>
-#include "track.h"
-#include "compound.h"
-
#include <stdint.h>
+#include <string.h>
+
+#include "compound.h"
+#include "fileio.h"
+#include "messages.h"
+#include "track.h"
EXPORT DIST_T curBarScale = -1;
EXPORT long hotBarLabels = 0;
@@ -73,7 +76,7 @@ static void HotBarHighlight( int inx )
{
wPos_t x0;
if ( inx >= hotBarCurrStart && inx < hotBarCurrEnd ) {
- x0 = (wPos_t)((hotBarMap(inx).x-hotBarMap((int)hotBarCurrStart).x)*hotBarD.dpi+2);
+ x0 = (wPos_t)((hotBarMap(inx).x-hotBarMap((int)hotBarCurrStart).x)*hotBarD.dpi);
wDrawFilledRectangle( hotBarD.d, x0, 0, (wPos_t)(hotBarMap(inx).w*hotBarD.dpi-2), hotBarHeight, wDrawColorBlack, wDrawOptTemp );
}
}
@@ -101,30 +104,32 @@ static void RedrawHotBar( wDraw_p dd, void * data, wPos_t w, wPos_t h )
}
if ( hotBarLabels && !hotBarFp )
hotBarFp = wStandardFont( F_HELV, FALSE, FALSE );
+ wPos_t textSize = wMessageGetHeight(0L);
for ( inx=hotBarCurrStart; inx < hotBarMap_da.cnt; inx++ ) {
tbm = &hotBarMap(inx);
barScale = tbm->barScale;
- x = tbm->x - hotBarMap(hotBarCurrStart).x + 2.0/hotBarD.dpi;
+ x = tbm->x - hotBarMap(hotBarCurrStart).x;
if ( x + tbm->w > barWidth ) {
break;
}
orig.y = hh/2.0*barScale - tbm->size.y/2.0 - tbm->orig.y;
if ( hotBarLabels ) {
- orig.y += 8/hotBarD.dpi*barScale;
+ orig.y += textSize/hotBarD.dpi*barScale;
if ( tbm->labelW > tbm->objectW ) {
x += (tbm->labelW-tbm->objectW)/2;
}
}
x *= barScale;
- orig.x = x - tbm->orig.x;
+ orig.x = x;
hotBarD.scale = barScale;
hotBarD.size.x = barWidth*barScale;
hotBarD.size.y = barHeight*barScale;
tbm->proc( HB_DRAW, tbm->context, &hotBarD, &orig );
if ( hotBarLabels ) {
- orig.x = x - (tbm->labelW-tbm->objectW)/2*barScale;
- orig.y = 2*barScale/hotBarD.dpi;
- DrawString( &hotBarD, orig, 0.0, tbm->proc( HB_BARTITLE, tbm->context, NULL, NULL ), hotBarFp, hotBarFs*barScale, drawColorBlack );
+ hotBarD.scale = 1.0;
+ orig.x = tbm->x - hotBarMap(hotBarCurrStart).x;
+ orig.y = 2.0/hotBarD.dpi; //Draw Label under icon
+ DrawString( &hotBarD, orig, 0.0, tbm->proc( HB_BARTITLE, tbm->context, NULL, NULL ), hotBarFp, hotBarFs, drawColorBlack );
}
}
hotBarCurrEnd = inx;
@@ -220,7 +225,8 @@ static void SelectHotBar( wDraw_p d, void * context, wAction_t action, wPos_t w,
}
x = w/hotBarD.dpi + hotBarMap(hotBarCurrStart).x;
for ( inx=hotBarCurrStart; inx<hotBarCurrEnd; inx++ ) {
- if ( x < hotBarMap(inx).x + hotBarMap(inx).w ) {
+ if ((x >= hotBarMap(inx).x) && //leave spaces between buttons
+ (x <= hotBarMap(inx).x + hotBarMap(inx).w )) {
break;
}
}
@@ -231,7 +237,7 @@ static void SelectHotBar( wDraw_p d, void * context, wAction_t action, wPos_t w,
px += (wPos_t)(tbm->w*hotBarD.dpi/2);
titleP = tbm->proc( HB_LISTTITLE, tbm->context, NULL, NULL );
px -= wLabelWidth( titleP ) / 2;
- wControlSetBalloon( (wControl_p)hotBarD.d, px, -5, titleP );
+ wControlSetBalloon( (wControl_p)hotBarD.d, px, -20, titleP );
switch (action & 0xff) {
case wActionLDown:
pos.x = mainD.size.x+mainD.orig.x;
@@ -380,7 +386,7 @@ EXPORT void AddHotBarElement(
tbm->w = tbm->labelW;
}
}
- hotBarWidth += tbm->w;
+ hotBarWidth += tbm->w + 2/hotBarD.dpi;
}
@@ -440,8 +446,9 @@ EXPORT void LayoutHotBar( void )
wWinGetSize( mainW, &winWidth, &winHeight );
hotBarHeight = hotBarDrawHeight;
- if ( hotBarLabels)
- hotBarHeight += 8;
+ if ( hotBarLabels) {
+ hotBarHeight += wMessageGetHeight(0L);
+ }
if (hotBarLeftB == NULL) {
wIcon_p bm_p;
if (winWidth < 50)
@@ -450,18 +457,18 @@ EXPORT void LayoutHotBar( void )
hotBarLeftB = wButtonCreate( mainW, 0, 0, "hotBarLeft", (char*)bm_p, BO_ICON, 0, DoHotBarLeft, NULL );
bm_p = wIconCreateBitMap( 16, 16, turnbarr_bits, wDrawColorBlack );
hotBarRightB = wButtonCreate( mainW, 0, 0, "hotBarRight", (char*)bm_p, BO_ICON, 0, DoHotBarRight, NULL );
- hotBarD.d = wDrawCreate( mainW, 0, 0, NULL, BD_NOCAPTURE, 100, hotBarHeight, NULL, RedrawHotBar, SelectHotBar );
+ hotBarD.d = wDrawCreate( mainW, 0, 0, NULL, BD_NOCAPTURE|BD_NOFOCUS, 100, hotBarHeight, NULL, RedrawHotBar, SelectHotBar );
hotBarD.dpi = wDrawGetDPI( hotBarD.d );
hotBarD.scale = 1.0;
initialize = TRUE;
}
buttonWidth = wControlGetWidth((wControl_p)hotBarLeftB);
wControlSetPos( (wControl_p)hotBarLeftB, 0, toolbarHeight );
- wControlSetPos( (wControl_p)hotBarRightB, winWidth-buttonWidth, toolbarHeight );
+ wControlSetPos( (wControl_p)hotBarRightB, winWidth-20-buttonWidth, toolbarHeight );
wControlSetPos( (wControl_p)hotBarD.d, buttonWidth, toolbarHeight );
- wDrawSetSize( hotBarD.d, winWidth-buttonWidth*2, hotBarHeight+2 );
- hotBarD.size.x = ((double)(winWidth-buttonWidth*2))/hotBarD.dpi*hotBarD.scale;
- hotBarD.size.y = (double)hotBarHeight/hotBarD.dpi*hotBarD.scale;
+ wDrawSetSize( hotBarD.d, winWidth-20-buttonWidth*2, hotBarHeight+2 );
+ hotBarD.size.x = ((double)(winWidth-20-buttonWidth*2))/hotBarD.dpi*hotBarD.scale;
+ hotBarD.size.y = (double)hotBarDrawHeight/hotBarD.dpi*hotBarD.scale; //Exclude Label from calc
wControlShow( (wControl_p)hotBarLeftB, TRUE );
wControlShow( (wControl_p)hotBarRightB, TRUE );
wControlShow( (wControl_p)hotBarD.d, TRUE );
diff --git a/app/bin/cjoin.c b/app/bin/cjoin.c
index e8d72eb..8cfa3d4 100644
--- a/app/bin/cjoin.c
+++ b/app/bin/cjoin.c
@@ -29,7 +29,15 @@
#include "ccurve.h"
#include "cstraigh.h"
#include "cjoin.h"
+#include "ccornu.h"
#include "i18n.h"
+#include "utility.h"
+#include "math.h"
+#include "messages.h"
+#include "param.h"
+#include "cundo.h"
+#include "cselect.h"
+#include "fileio.h"
static int log_join = 0;
@@ -444,18 +452,25 @@ static STATUS_T CmdJoin(
DIST_T eR[2];
BOOL_T ok;
- switch (action) {
+ switch (action&0xFF) {
case C_START:
- InfoMessage( _("Left click - join with track, Shift Left click - move to join") );
+ if (selectedTrackCount==0)
+ InfoMessage( _("Left click - join with track") );
+ else
+ InfoMessage( _("Left click - join with track, Shift Left click - move to join") );
Dj.state = 0;
Dj.joinMoveState = 0;
/*ParamGroupRecord( &easementPG );*/
+ if (easementVal < 0)
+ return CmdCornu(action, pos);
return C_CONTINUE;
case C_DOWN:
if ( (Dj.state == 0 && (MyGetKeyState() & WKEY_SHIFT) != 0) || Dj.joinMoveState != 0 )
return DoMoveToJoin( pos );
+ if (easementVal < 0.0)
+ return CmdCornu(action, pos);
DYNARR_SET( trkSeg_t, tempSegs_da, 3 );
tempSegs(0).color = drawColorBlack;
@@ -545,6 +560,8 @@ LOG( log_join, 1, ("P1=[%0.3f %0.3f]\n", pos.x, pos.y ) )
tempSegs_da.cnt = 0;
case C_MOVE:
+ if (easementVal < 0)
+ return CmdCornu(action, pos);
LOG( log_join, 3, ("P1=[%0.3f %0.3f]\n", pos.x, pos.y ) )
if (Dj.state != 2)
@@ -580,6 +597,8 @@ LOG( log_join, 3, ("P1=[%0.3f %0.3f]\n", pos.x, pos.y ) )
((Dj.inp[0].params.ep==0)?-90.0:90.0) );
break;
case curveTypeNone:
+ case curveTypeBezier:
+ case curveTypeCornu:
break;
}
@@ -655,6 +674,11 @@ LOG( log_join, 3, (" -E POS0=[%0.3f %0.3f] POS1=[%0.3f %0.3f]\n",
d = Dj.inp[0].params.arcR * a1 * 2.0*M_PI/360.0;
}
break;
+ case curveTypeCornu:
+ case curveTypeBezier:
+ case curveTypeNone:
+ InfoMessage( _("First Track Type not supported for non-Cornu Join") );
+ goto errorReturn;
default:
AbortProg( "cmdJoin - unknown type[0]" );
}
@@ -682,6 +706,11 @@ LOG( log_join, 3, (" -E POS0=[%0.3f %0.3f] POS1=[%0.3f %0.3f]\n",
d = Dj.inp[1].params.arcR * a1 * 2.0*M_PI/360.0;
}
break;
+ case curveTypeCornu:
+ case curveTypeBezier:
+ case curveTypeNone:
+ InfoMessage( _("Second Track Type not supported for non-Cornu Join") );
+ goto errorReturn;
default:
AbortProg( "cmdJoin - unknown type[1]" );
}
@@ -775,8 +804,13 @@ errorReturn:
return C_CONTINUE;
case C_UP:
- if (Dj.state == 0)
- return C_CONTINUE;
+
+ if (Dj.state == 0) {
+ if (easementVal<0)
+ return CmdCornu(action, pos);
+ else
+ return C_CONTINUE;
+ }
if (Dj.state == 1) {
InfoMessage( _("Select 2nd track") );
return C_CONTINUE;
@@ -801,6 +835,8 @@ errorReturn:
Dj.jRes.arcA0, Dj.jRes.arcA1, 0 );
break;
case curveTypeNone:
+ case curveTypeBezier:
+ case curveTypeCornu:
return C_CONTINUE;
}
@@ -822,65 +858,26 @@ errorReturn:
DrawNewTrack( trk );
return rc;
-#ifdef LATER
- case C_LCLICK:
- if ( (MyGetKeyState() & WKEY_SHIFT) == 0 ) {
- rc = CmdJoin( C_DOWN, pos );
- if (rc == C_TERMINATE)
- return rc;
- return CmdJoin( C_UP, pos );
- }
- if ( selectedTrackCount <= 0 ) {
- ErrorMessage( MSG_NO_SELECTED_TRK );
- return C_CONTINUE;
- }
- if ( (Dj.inp[Dj.joinMoveState].trk = OnTrack( &pos, TRUE, TRUE )) == NULL )
- return C_CONTINUE;
- if (!CheckTrackLayer( Dj.inp[Dj.joinMoveState].trk ) )
- return C_CONTINUE;
- Dj.inp[Dj.joinMoveState].params.ep = PickUnconnectedEndPoint( pos, Dj.inp[Dj.joinMoveState].trk ); /* CHECKME */
- if ( Dj.inp[Dj.joinMoveState].params.ep == -1 ) {
-#ifdef LATER
- ErrorMessage( MSG_NO_ENDPTS );
-#endif
- return C_CONTINUE;
- }
-#ifdef LATER
- if ( GetTrkEndTrk( Dj.inp[Dj.joinMoveState].trk, Dj.inp[Dj.joinMoveState].params.ep ) ) {
- ErrorMessage( MSG_SEL_EP_CONN );
- return C_CONTINUE;
- }
-#endif
- if (Dj.joinMoveState == 0) {
- Dj.joinMoveState++;
- InfoMessage( GetTrkSelected(Dj.inp[0].trk)?
- _("Click on an unselected End-Point"):
- _("Click on a selected End-Point") );
- return C_CONTINUE;
- }
- if ( GetTrkSelected(Dj.inp[0].trk) == GetTrkSelected(Dj.inp[1].trk) ) {
- ErrorMessage( MSG_2ND_TRK_NOT_SEL_UNSEL, GetTrkSelected(Dj.inp[0].trk)
- ? _("unselected") : _("selected") );
- return C_CONTINUE;
- }
- if (GetTrkSelected(Dj.inp[0].trk))
- MoveToJoin( Dj.inp[0].trk, Dj.inp[0].params.ep, Dj.inp[1].trk, Dj.inp[1].params.ep );
- else
- MoveToJoin( Dj.inp[1].trk, Dj.inp[1].params.ep, Dj.inp[0].trk, Dj.inp[0].params.ep );
- Dj.joinMoveState = 0;
- return C_TERMINATE;
- break;
-#endif
case C_CANCEL:
case C_REDRAW:
+
+
if ( Dj.joinMoveState == 1 || Dj.state == 1 ) {
DrawFillCircle( &tempD, Dj.inp[0].pos, 0.10*mainD.scale, selectedColor );
- }
+ } else if (easementVal<0 )
+ return CmdCornu(action,pos);
+
DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack );
break;
+ case C_TEXT:
+ case C_OK:
+ if (easementVal<0 )
+ return CmdCornu(action,pos);
}
+
+
return C_CONTINUE;
}
diff --git a/app/bin/cjoin.h b/app/bin/cjoin.h
index 021e0a1..8d9c7e3 100644
--- a/app/bin/cjoin.h
+++ b/app/bin/cjoin.h
@@ -1,7 +1,6 @@
-/*
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/cjoin.h,v 1.1 2005-12-07 15:47:39 rc-flyer Exp $
+/** \file cjoin.h
+ * Prototypes and definitions related to the "join" command
*/
-
/* XTrkCad - Model Railroad CAD
* Copyright (C) 2005 Dave Bullis
*
@@ -20,6 +19,13 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
+#ifndef HAVE_CJOIN_H
+#define HAVE_CJOIN_H
+
+#include "common.h"
+#include "wlib.h"
+#include "track.h"
+
#define E_NOTREQ (0)
#define E_REQ (1)
#define E_ERROR (2)
@@ -42,3 +48,6 @@ void UndoJoint( track_p, EPINX_T, track_p, EPINX_T );
void DrawJointTrack( drawCmd_p, coOrd, ANGLE_T, DIST_T, DIST_T, DIST_T, DIST_T, BOOL_T, BOOL_T, BOOL_T, track_p, EPINX_T, EPINX_T, DIST_T, wDrawColor, long );
DIST_T JointDistance( coOrd *, coOrd, ANGLE_T, DIST_T, DIST_T, DIST_T, DIST_T, BOOL_T, BOOL_T );
coOrd GetJointSegEndPos( coOrd, ANGLE_T, DIST_T, DIST_T, DIST_T, DIST_T, BOOL_T, BOOL_T, BOOL_T, EPINX_T, ANGLE_T * );
+
+#endif // !HAVE_CJOIN_H
+
diff --git a/app/bin/cmisc.c b/app/bin/cmisc.c
index 1e2ea39..227a7d0 100644
--- a/app/bin/cmisc.c
+++ b/app/bin/cmisc.c
@@ -20,16 +20,14 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-#include "track.h"
+#include <stdint.h>
+
#include "common.h"
+#include "cundo.h"
#include "i18n.h"
-
-/*****************************************************************************
- *
- * DESCRIPTION WINDOW
- *
- */
-
+#include "messages.h"
+#include "param.h"
+#include "track.h"
EXPORT wIndex_t describeCmdInx;
EXPORT BOOL_T inDescribeCmd;
@@ -45,6 +43,8 @@ static BOOL_T descNeedDrawHilite;
static wPos_t describeW_posy;
static wPos_t describeCmdButtonEnd;
+static unsigned int editableLayerList[NUM_LAYERS]; /**< list of non-frozen layers */
+static int * layerValue; /**pointer to current Layer (int *) */
static paramFloatRange_t rdata = { 0, 0, 100, PDO_NORANGECHECK_HIGH|PDO_NORANGECHECK_LOW };
static paramIntegerRange_t idata = { 0, 0, 100, PDO_NORANGECHECK_HIGH|PDO_NORANGECHECK_LOW };
@@ -52,225 +52,339 @@ static paramTextData_t tdata = { 300, 150 };
static char * pivotLabels[] = { N_("First"), N_("Middle"), N_("Second"), NULL };
static paramData_t describePLs[] = {
#define I_FLOAT_0 (0)
- { PD_FLOAT, NULL, "F1", 0, &rdata },
- { PD_FLOAT, NULL, "F2", 0, &rdata },
- { PD_FLOAT, NULL, "F3", 0, &rdata },
- { PD_FLOAT, NULL, "F4", 0, &rdata },
- { PD_FLOAT, NULL, "F5", 0, &rdata },
- { PD_FLOAT, NULL, "F6", 0, &rdata },
- { PD_FLOAT, NULL, "F7", 0, &rdata },
- { PD_FLOAT, NULL, "F8", 0, &rdata },
- { PD_FLOAT, NULL, "F9", 0, &rdata },
- { PD_FLOAT, NULL, "F10", 0, &rdata },
- { PD_FLOAT, NULL, "F11", 0, &rdata },
- { PD_FLOAT, NULL, "F12", 0, &rdata },
- { PD_FLOAT, NULL, "F13", 0, &rdata },
- { PD_FLOAT, NULL, "F14", 0, &rdata },
- { PD_FLOAT, NULL, "F15", 0, &rdata },
- { PD_FLOAT, NULL, "F16", 0, &rdata },
- { PD_FLOAT, NULL, "F17", 0, &rdata },
- { PD_FLOAT, NULL, "F18", 0, &rdata },
- { PD_FLOAT, NULL, "F19", 0, &rdata },
- { PD_FLOAT, NULL, "F20", 0, &rdata },
-#define I_FLOAT_N I_FLOAT_0+20
+ { PD_FLOAT, NULL, "F1", 0, &rdata },
+ { PD_FLOAT, NULL, "F2", 0, &rdata },
+ { PD_FLOAT, NULL, "F3", 0, &rdata },
+ { PD_FLOAT, NULL, "F4", 0, &rdata },
+ { PD_FLOAT, NULL, "F5", 0, &rdata },
+ { PD_FLOAT, NULL, "F6", 0, &rdata },
+ { PD_FLOAT, NULL, "F7", 0, &rdata },
+ { PD_FLOAT, NULL, "F8", 0, &rdata },
+ { PD_FLOAT, NULL, "F9", 0, &rdata },
+ { PD_FLOAT, NULL, "F10", 0, &rdata },
+ { PD_FLOAT, NULL, "F11", 0, &rdata },
+ { PD_FLOAT, NULL, "F12", 0, &rdata },
+ { PD_FLOAT, NULL, "F13", 0, &rdata },
+ { PD_FLOAT, NULL, "F14", 0, &rdata },
+ { PD_FLOAT, NULL, "F15", 0, &rdata },
+ { PD_FLOAT, NULL, "F16", 0, &rdata },
+ { PD_FLOAT, NULL, "F17", 0, &rdata },
+ { PD_FLOAT, NULL, "F18", 0, &rdata },
+ { PD_FLOAT, NULL, "F19", 0, &rdata },
+ { PD_FLOAT, NULL, "F20", 0, &rdata },
+ { PD_FLOAT, NULL, "F21", 0, &rdata },
+ { PD_FLOAT, NULL, "F22", 0, &rdata },
+ { PD_FLOAT, NULL, "F23", 0, &rdata },
+ { PD_FLOAT, NULL, "F24", 0, &rdata },
+ { PD_FLOAT, NULL, "F25", 0, &rdata },
+ { PD_FLOAT, NULL, "F26", 0, &rdata },
+ { PD_FLOAT, NULL, "F27", 0, &rdata },
+ { PD_FLOAT, NULL, "F28", 0, &rdata },
+ { PD_FLOAT, NULL, "F29", 0, &rdata },
+ { PD_FLOAT, NULL, "F30", 0, &rdata },
+ { PD_FLOAT, NULL, "F31", 0, &rdata },
+ { PD_FLOAT, NULL, "F32", 0, &rdata },
+ { PD_FLOAT, NULL, "F33", 0, &rdata },
+ { PD_FLOAT, NULL, "F34", 0, &rdata },
+ { PD_FLOAT, NULL, "F35", 0, &rdata },
+ { PD_FLOAT, NULL, "F36", 0, &rdata },
+ { PD_FLOAT, NULL, "F37", 0, &rdata },
+ { PD_FLOAT, NULL, "F38", 0, &rdata },
+ { PD_FLOAT, NULL, "F39", 0, &rdata },
+ { PD_FLOAT, NULL, "F40", 0, &rdata },
+#define I_FLOAT_N I_FLOAT_0+40
#define I_LONG_0 I_FLOAT_N
- { PD_LONG, NULL, "I1", 0, &idata },
- { PD_LONG, NULL, "I2", 0, &idata },
- { PD_LONG, NULL, "I3", 0, &idata },
- { PD_LONG, NULL, "I4", 0, &idata },
- { PD_LONG, NULL, "I5", 0, &idata },
+ { PD_LONG, NULL, "I1", 0, &idata },
+ { PD_LONG, NULL, "I2", 0, &idata },
+ { PD_LONG, NULL, "I3", 0, &idata },
+ { PD_LONG, NULL, "I4", 0, &idata },
+ { PD_LONG, NULL, "I5", 0, &idata },
#define I_LONG_N I_LONG_0+5
#define I_STRING_0 I_LONG_N
- { PD_STRING, NULL, "S1", 0, (void*)300 },
- { PD_STRING, NULL, "S2", 0, (void*)300 },
- { PD_STRING, NULL, "S3", 0, (void*)300 },
- { PD_STRING, NULL, "S4", 0, (void*)300 },
+ { PD_STRING, NULL, "S1", 0, (void*)300 },
+ { PD_STRING, NULL, "S2", 0, (void*)300 },
+ { PD_STRING, NULL, "S3", 0, (void*)300 },
+ { PD_STRING, NULL, "S4", 0, (void*)300 },
#define I_STRING_N I_STRING_0+4
#define I_LAYER_0 I_STRING_N
- { PD_DROPLIST, NULL, "Y1", 0, (void*)150, NULL, 0 },
+ { PD_DROPLIST, NULL, "Y1", 0, (void*)150, NULL, 0 },
#define I_LAYER_N I_LAYER_0+1
#define I_COLOR_0 I_LAYER_N
- { PD_COLORLIST, NULL, "C1", 0, NULL, N_("Color") },
+ { PD_COLORLIST, NULL, "C1", 0, NULL, N_("Color") },
#define I_COLOR_N I_COLOR_0+1
#define I_LIST_0 I_COLOR_N
- { PD_DROPLIST, NULL, "L1", 0, (void*)150, NULL, 0 },
- { PD_DROPLIST, NULL, "L2", 0, (void*)150, NULL, 0 },
+ { PD_DROPLIST, NULL, "L1", 0, (void*)150, NULL, 0 },
+ { PD_DROPLIST, NULL, "L2", 0, (void*)150, NULL, 0 },
#define I_LIST_N I_LIST_0+2
#define I_EDITLIST_0 I_LIST_N
- { PD_DROPLIST, NULL, "LE1", 0, (void*)150, NULL, BL_EDITABLE },
+ { PD_DROPLIST, NULL, "LE1", 0, (void*)150, NULL, BL_EDITABLE },
#define I_EDITLIST_N I_EDITLIST_0+1
#define I_TEXT_0 I_EDITLIST_N
- { PD_TEXT, NULL, "T1", 0, &tdata },
+ { PD_TEXT, NULL, "T1", 0, &tdata },
#define I_TEXT_N I_TEXT_0+1
#define I_PIVOT_0 I_TEXT_N
- { PD_RADIO, NULL, "P1", 0, pivotLabels, N_("Pivot"), BC_HORZ|BC_NOBORDER, 0 }
+ { PD_RADIO, NULL, "P1", 0, pivotLabels, N_("Pivot"), BC_HORZ|BC_NOBORDER, 0 }
#define I_PIVOT_N I_PIVOT_0+1
- };
+};
static paramGroup_t describePG = { "describe", 0, describePLs, sizeof describePLs/sizeof describePLs[0] };
+/**
+ * A mapping table is used to map the index in the dropdown list to the layer
+ * number. While usually this is a 1:1 translation, the values differ if there
+ * are frozen layer. Frozen layers are not shown in the dropdown list.
+ */
+
+void
+CreateEditableLayersList()
+{
+ int i = 0;
+ int j = 0;
+
+ while (i <= NUM_LAYERS) {
+ if (!GetLayerFrozen(i)) {
+ editableLayerList[j++] = i;
+ }
+
+ i++;
+ }
+}
+
+/**
+ * Search a layer in the list of editable layers.
+ *
+ * \param IN layer layer to search
+ * \return the index into the list
+ */
+
+static int
+SearchEditableLayerList(unsigned int layer)
+{
+ int i;
+
+ for (i = 0; i < NUM_LAYERS; i++) {
+ if (editableLayerList[i] == layer) {
+ return (i);
+ }
+ }
-static void DrawDescHilite( void )
+ return (-1);
+}
+
+static void DrawDescHilite(void)
{
- wPos_t x, y, w, h;
- if ( descNeedDrawHilite == FALSE )
- return;
- if (descColor==0)
- descColor = wDrawColorGray(87);
- w = (wPos_t)((descSize.x/mainD.scale)*mainD.dpi+0.5);
- h = (wPos_t)((descSize.y/mainD.scale)*mainD.dpi+0.5);
- mainD.CoOrd2Pix(&mainD,descOrig,&x,&y);
- wDrawFilledRectangle( mainD.d, x, y, w, h, descColor, wDrawOptTemp );
+ wPos_t x, y, w, h;
+
+ if (descNeedDrawHilite == FALSE) {
+ return;
+ }
+
+ if (descColor==0) {
+ descColor = wDrawColorGray(87);
+ }
+
+ w = (wPos_t)((descSize.x/mainD.scale)*mainD.dpi+0.5);
+ h = (wPos_t)((descSize.y/mainD.scale)*mainD.dpi+0.5);
+ mainD.CoOrd2Pix(&mainD,descOrig,&x,&y);
+ wDrawFilledRectangle(mainD.d, x, y, w, h, descColor, wDrawOptTemp);
}
static void DescribeUpdate(
- paramGroup_p pg,
- int inx,
- void * data )
+ paramGroup_p pg,
+ int inx,
+ void * data)
{
- coOrd hi, lo;
- descData_p ddp;
- if ( inx < 0 )
- return;
- ddp = (descData_p)pg->paramPtr[inx].context;
- if ( (ddp->mode&(DESC_RO|DESC_IGNORE)) != 0 )
- return;
- if ( ddp->type == DESC_PIVOT )
- return;
- if ( (ddp->mode&DESC_NOREDRAW) == 0 )
- DrawDescHilite();
- if ( !descUndoStarted ) {
- UndoStart( _("Change Track"), "Change Track" );
- descUndoStarted = TRUE;
- }
- if (!descTrk) return; // In case timer pops after OK
- UndoModify( descTrk );
- descUpdateFunc( descTrk, ddp-descData, descData, FALSE );
- if ( descTrk ) {
- GetBoundingBox( descTrk, &hi, &lo );
- if ( OFF_D( mapD.orig, mapD.size, descOrig, descSize ) ) {
- ErrorMessage( MSG_MOVE_OUT_OF_BOUNDS );
- }
- }
- if ( (ddp->mode&DESC_NOREDRAW) == 0 ) {
- descOrig = lo;
- descSize = hi;
- descOrig.x -= descBorder;
- descOrig.y -= descBorder;
- descSize.x -= descOrig.x-descBorder;
- descSize.y -= descOrig.y-descBorder;
- DrawDescHilite();
- }
- for ( inx = 0; inx < sizeof describePLs/sizeof describePLs[0]; inx++ ) {
- if ( (describePLs[inx].option & PDO_DLGIGNORE) != 0 )
- continue;
- ddp = (descData_p)describePLs[inx].context;
- if ( (ddp->mode&DESC_IGNORE) != 0 )
- continue;
- if ( (ddp->mode&DESC_CHANGE) == 0 )
- continue;
- ddp->mode &= ~DESC_CHANGE;
- ParamLoadControl( &describePG, inx );
- }
+ coOrd hi, lo;
+ descData_p ddp;
+
+ if (inx < 0) {
+ return;
+ }
+
+ ddp = (descData_p)pg->paramPtr[inx].context;
+
+ if ((ddp->mode&(DESC_RO|DESC_IGNORE)) != 0) {
+ return;
+ }
+
+ if (ddp->type == DESC_PIVOT) {
+ return;
+ }
+
+ if ((ddp->mode&DESC_NOREDRAW) == 0) {
+ DrawDescHilite();
+ }
+
+ if (!descUndoStarted) {
+ UndoStart(_("Change Track"), "Change Track");
+ descUndoStarted = TRUE;
+ }
+
+ if (!descTrk) {
+ return; // In case timer pops after OK
+ }
+
+ UndoModify(descTrk);
+ descUpdateFunc(descTrk, ddp-descData, descData, FALSE);
+
+ if (descTrk) {
+ GetBoundingBox(descTrk, &hi, &lo);
+
+ if (OFF_D(mapD.orig, mapD.size, descOrig, descSize)) {
+ ErrorMessage(MSG_MOVE_OUT_OF_BOUNDS);
+ }
+ }
+
+ if ((ddp->mode&DESC_NOREDRAW) == 0) {
+ descOrig = lo;
+ descSize = hi;
+ descOrig.x -= descBorder;
+ descOrig.y -= descBorder;
+ descSize.x -= descOrig.x-descBorder;
+ descSize.y -= descOrig.y-descBorder;
+ DrawDescHilite();
+ }
+
+ for (inx = 0; inx < sizeof describePLs/sizeof describePLs[0]; inx++) {
+ if ((describePLs[inx].option & PDO_DLGIGNORE) != 0) {
+ continue;
+ }
+
+ ddp = (descData_p)describePLs[inx].context;
+
+ if ((ddp->mode&DESC_IGNORE) != 0) {
+ continue;
+ }
+
+ if ((ddp->mode&DESC_CHANGE) == 0) {
+ continue;
+ }
+
+ ddp->mode &= ~DESC_CHANGE;
+ ParamLoadControl(&describePG, inx);
+ }
}
-static void DescOk( void * junk )
+static void DescOk(void * junk)
{
- wHide( describePG.win );
- if ( descTrk )
- DrawDescHilite();
-
- descUpdateFunc( descTrk, -1, descData, !descUndoStarted );
- descTrk = NULL;
- if (descUndoStarted) {
- UndoEnd();
- descUndoStarted = FALSE;
- }
- descNeedDrawHilite = FALSE;
- Reset();
+ wHide(describePG.win);
+
+ if (descTrk) {
+ DrawDescHilite();
+ }
+ if (layerValue && *layerValue>=0) {
+ SetTrkLayer(descTrk, editableLayerList[*layerValue]); //int found that is really in the parm controls.
+ }
+ layerValue = NULL; // wipe out reference
+ descUpdateFunc(descTrk, -1, descData, !descUndoStarted);
+ descTrk = NULL;
+
+ if (descUndoStarted) {
+ UndoEnd();
+ descUndoStarted = FALSE;
+ }
+
+ descNeedDrawHilite = FALSE;
+ Reset();
}
static struct {
- parameterType pd_type;
- long option;
- int first;
- int last;
- } descTypeMap[] = {
-/*NULL*/ { 0, 0 },
-/*POS*/ { PD_FLOAT, PDO_DIM, I_FLOAT_0, I_FLOAT_N },
-/*FLOAT*/ { PD_FLOAT, 0, I_FLOAT_0, I_FLOAT_N },
-/*ANGLE*/ { PD_FLOAT, PDO_ANGLE, I_FLOAT_0, I_FLOAT_N },
-/*LONG*/ { PD_LONG, 0, I_LONG_0, I_LONG_N },
-/*COLOR*/ { PD_LONG, 0, I_COLOR_0, I_COLOR_N },
-/*DIM*/ { PD_FLOAT, PDO_DIM, I_FLOAT_0, I_FLOAT_N },
-/*PIVOT*/ { PD_RADIO, 0, I_PIVOT_0, I_PIVOT_N },
-/*LAYER*/ { PD_DROPLIST,PDO_LISTINDEX, I_LAYER_0, I_LAYER_N },
-/*STRING*/ { PD_STRING,0, I_STRING_0, I_STRING_N },
-/*TEXT*/ { PD_TEXT, PDO_DLGNOLABELALIGN, I_TEXT_0, I_TEXT_N },
-/*LIST*/ { PD_DROPLIST, PDO_LISTINDEX, I_LIST_0, I_LIST_N },
-/*EDITABLELIST*/{ PD_DROPLIST, 0, I_EDITLIST_0, I_EDITLIST_N } };
-
-static wControl_p AllocateButt( descData_p ddp, void * valueP, char * label, wPos_t sep )
+ parameterType pd_type;
+ long option;
+ int first;
+ int last;
+} descTypeMap[] = {
+ /*NULL*/ { 0, 0 },
+ /*POS*/ { PD_FLOAT, PDO_DIM, I_FLOAT_0, I_FLOAT_N },
+ /*FLOAT*/ { PD_FLOAT, 0, I_FLOAT_0, I_FLOAT_N },
+ /*ANGLE*/ { PD_FLOAT, PDO_ANGLE, I_FLOAT_0, I_FLOAT_N },
+ /*LONG*/ { PD_LONG, 0, I_LONG_0, I_LONG_N },
+ /*COLOR*/ { PD_LONG, 0, I_COLOR_0, I_COLOR_N },
+ /*DIM*/ { PD_FLOAT, PDO_DIM, I_FLOAT_0, I_FLOAT_N },
+ /*PIVOT*/ { PD_RADIO, 0, I_PIVOT_0, I_PIVOT_N },
+ /*LAYER*/ { PD_DROPLIST,PDO_LISTINDEX, I_LAYER_0, I_LAYER_N },
+ /*STRING*/ { PD_STRING,0, I_STRING_0, I_STRING_N },
+ /*TEXT*/ { PD_TEXT, PDO_DLGNOLABELALIGN, I_TEXT_0, I_TEXT_N },
+ /*LIST*/ { PD_DROPLIST, PDO_LISTINDEX, I_LIST_0, I_LIST_N },
+ /*EDITABLELIST*/{ PD_DROPLIST, 0, I_EDITLIST_0, I_EDITLIST_N }
+};
+
+static wControl_p AllocateButt(descData_p ddp, void * valueP, char * label,
+ wPos_t sep)
{
- int inx;
-
- for ( inx = descTypeMap[ddp->type].first; inx<descTypeMap[ddp->type].last; inx++ ) {
- if ( (describePLs[inx].option & PDO_DLGIGNORE) != 0 ) {
- describePLs[inx].option = descTypeMap[ddp->type].option;
- if ( describeW_posy > describeCmdButtonEnd )
- describePLs[inx].option |= PDO_DLGUNDERCMDBUTT;
- describeW_posy += wControlGetHeight( describePLs[inx].control ) + sep;
- describePLs[inx].context = ddp;
- describePLs[inx].valueP = valueP;
- if ( label && ddp->type != DESC_TEXT ) {
- wControlSetLabel( describePLs[inx].control, label );
- describePLs[inx].winLabel = label;
- }
- return describePLs[inx].control;
- }
- }
- AbortProg( "allocateButt: can't find %d", ddp->type );
- return NULL;
+ int inx;
+
+ for (inx = descTypeMap[ddp->type].first; inx<descTypeMap[ddp->type].last;
+ inx++) {
+ if ((describePLs[inx].option & PDO_DLGIGNORE) != 0) {
+ describePLs[inx].option = descTypeMap[ddp->type].option;
+
+ if (describeW_posy > describeCmdButtonEnd) {
+ describePLs[inx].option |= PDO_DLGUNDERCMDBUTT;
+ }
+
+ if (sep)
+ describeW_posy += wControlGetHeight(describePLs[inx].control) + sep;
+ describePLs[inx].context = ddp;
+ describePLs[inx].valueP = valueP;
+
+ if (label && ddp->type != DESC_TEXT) {
+ wControlSetLabel(describePLs[inx].control, label);
+ describePLs[inx].winLabel = label;
+ }
+
+ return describePLs[inx].control;
+ }
+ }
+
+ AbortProg("allocateButt: can't find %d", ddp->type);
+ return NULL;
}
static void DescribeLayout(
- paramData_t * pd,
- int inx,
- wPos_t colX,
- wPos_t * x,
- wPos_t * y )
+ paramData_t * pd,
+ int inx,
+ wPos_t colX,
+ wPos_t * x,
+ wPos_t * y)
{
- descData_p ddp;
- wPos_t w, h;
-
- if ( inx < 0 )
- return;
- if ( pd->context == NULL )
- return;
- ddp = (descData_p)pd->context;
- *y = ddp->posy;
- if ( ddp->type == DESC_POS &&
- ddp->control0 != pd->control ) {
- *y += wControlGetHeight( pd->control ) + 3;
- } else if ( ddp->type == DESC_TEXT ) {
- w = tdata.width;
- h = tdata.height;
- wTextSetSize( (wText_p)pd->control, w, h );
- }
- wControlShow( pd->control, TRUE );
+ descData_p ddp;
+ wPos_t w, h;
+
+ if (inx < 0) {
+ return;
+ }
+
+ if (pd->context == NULL) {
+ return;
+ }
+
+ ddp = (descData_p)pd->context;
+ *y = ddp->posy;
+
+ if (ddp->type == DESC_POS &&
+ ddp->control0 != pd->control) {
+ *x += wControlGetWidth(pd->control) + 3;
+ } else if (ddp->type == DESC_TEXT) {
+ w = tdata.width;
+ h = tdata.height;
+ wTextSetSize((wText_p)pd->control, w, h);
+ }
+
+ wControlShow(pd->control, TRUE);
}
@@ -278,89 +392,109 @@ static void DescribeLayout(
* Creation and modification of the Describe dialog box is handled here. As the number
* of values for a track element depends on the specific type, this dialog is dynamically
* updated to hsow the changable parameters only
- *
+ *
* \param IN title Description of the selected part, shown in window title bar
* \param IN trk Track element to be described
* \param IN data
* \param IN update
*
*/
+
static wList_p setLayerL;
-void DoDescribe( char * title, track_p trk, descData_p data, descUpdate_t update )
+void DoDescribe(char * title, track_p trk, descData_p data, descUpdate_t update)
{
- int inx;
- descData_p ddp;
- char * label;
- int ro_mode;
-
- if (!inDescribeCmd)
- return;
-
- descTrk = trk;
- descData = data;
- descUpdateFunc = update;
- describeW_posy = 0;
- if ( describePG.win == NULL ) {
- /* SDB 5.13.2005 */
- ParamCreateDialog( &describePG, _("Description"), _("Done"), DescOk,
- (paramActionCancelProc) DescribeCancel,
- TRUE, DescribeLayout, F_RECALLPOS,
- DescribeUpdate );
- describeCmdButtonEnd = wControlBelow( (wControl_p)describePG.helpB );
- }
- for ( inx=0; inx<sizeof describePLs/sizeof describePLs[0]; inx++ ) {
- describePLs[inx].option = PDO_DLGIGNORE;
- wControlShow( describePLs[inx].control, FALSE );
- }
- ro_mode = (GetLayerFrozen(GetTrkLayer(trk))?DESC_RO:0);
- if (ro_mode)
- for ( ddp=data; ddp->type != DESC_NULL; ddp++ ) {
- if ( ddp->mode&DESC_IGNORE )
- continue;
- ddp->mode |= ro_mode;
- }
- for ( ddp=data; ddp->type != DESC_NULL; ddp++ ) {
- if ( ddp->mode&DESC_IGNORE )
- continue;
- label = _(ddp->label);
-
- ddp->posy = describeW_posy;
- ddp->control0 = AllocateButt( ddp, ddp->valueP, label, (ddp->type == DESC_POS?3:3) );
- wControlActive( ddp->control0, ((ddp->mode|ro_mode)&DESC_RO)==0 );
- switch (ddp->type) {
- case DESC_POS:
- ddp->control1 = AllocateButt( ddp,
- &((coOrd*)(ddp->valueP))->y,
- "Y",
- 3 );
- wControlActive( ddp->control1, ((ddp->mode|ro_mode)&DESC_RO)==0 );
- break;
- case DESC_LAYER:
- wListClear((wList_p)ddp->control0); // Rebuild list on each invovation
- for ( inx = 0; inx<NUM_LAYERS; inx++ ) {
- if (!GetLayerFrozen(inx)) // Avoid Frozen layers.
- {
- sprintf( message, "%2d : %s", inx+1, GetLayerName(inx) );
- wListAddValue( (wList_p)ddp->control0, message, NULL, (void*)(intptr_t)inx );
- }
- }
- break;
- default:
- break;
- }
- }
- ParamLayoutDialog( &describePG );
- ParamLoadControls( &describePG );
- sprintf( message, "%s (T%d)", title, GetTrkIndex(trk) );
- wWinSetTitle( describePG.win, message );
- wShow( describePG.win );
+ int inx;
+ descData_p ddp;
+ char * label;
+ int ro_mode;
+
+ if (!inDescribeCmd) {
+ return;
+ }
+
+ CreateEditableLayersList();
+ descTrk = trk;
+ descData = data;
+ descUpdateFunc = update;
+ describeW_posy = 0;
+
+ if (describePG.win == NULL) {
+ /* SDB 5.13.2005 */
+ ParamCreateDialog(&describePG, _("Description"), _("Done"), DescOk,
+ (paramActionCancelProc) DescribeCancel,
+ TRUE, DescribeLayout, F_RECALLPOS,
+ DescribeUpdate);
+ describeCmdButtonEnd = wControlBelow((wControl_p)describePG.helpB);
+ }
+
+ for (inx=0; inx<sizeof describePLs/sizeof describePLs[0]; inx++) {
+ describePLs[inx].option = PDO_DLGIGNORE;
+ wControlShow(describePLs[inx].control, FALSE);
+ }
+
+ ro_mode = (GetLayerFrozen(GetTrkLayer(trk))?DESC_RO:0);
+
+ if (ro_mode)
+ for (ddp=data; ddp->type != DESC_NULL; ddp++) {
+ if (ddp->mode&DESC_IGNORE) {
+ continue;
+ }
+
+ ddp->mode |= ro_mode;
+ }
+
+ for (ddp=data; ddp->type != DESC_NULL; ddp++) {
+ if (ddp->mode&DESC_IGNORE) {
+ continue;
+ }
+
+ label = _(ddp->label);
+ ddp->posy = describeW_posy;
+ ddp->control0 = AllocateButt(ddp, ddp->valueP, label,
+ (ddp->type == DESC_POS?3:3));
+ wControlActive(ddp->control0, ((ddp->mode|ro_mode)&DESC_RO)==0);
+
+ switch (ddp->type) {
+ case DESC_POS:
+ ddp->control1 = AllocateButt(ddp,
+ &((coOrd*)(ddp->valueP))->y,
+ NULL,
+ 0);
+ wControlActive(ddp->control1, ((ddp->mode|ro_mode)&DESC_RO)==0);
+ break;
+
+ case DESC_LAYER:
+ wListClear((wList_p)ddp->control0); // Rebuild list on each invovation
+
+ for (inx = 0; inx<NUM_LAYERS; inx++) {
+ char *layerFormattedName;
+ layerFormattedName = FormatLayerName(editableLayerList[inx]);
+ wListAddValue((wList_p)ddp->control0, layerFormattedName, NULL, (void*)(long)inx);
+ free(layerFormattedName);
+ }
+
+ *(int *)(ddp->valueP) = SearchEditableLayerList(*(int *)(ddp->valueP));
+ layerValue = (int *)(ddp->valueP);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ ParamLayoutDialog(&describePG);
+ ParamLoadControls(&describePG);
+ sprintf(message, "%s (T%d)", title, GetTrkIndex(trk));
+ wWinSetTitle(describePG.win, message);
+ wShow(describePG.win);
}
-static void DescChange( long changes )
+static void DescChange(long changes)
{
- if ( (changes&CHANGE_UNITS) && describePG.win && wWinIsVisible(describePG.win) )
- ParamLoadControls( &describePG );
+ if ((changes&CHANGE_UNITS) && describePG.win && wWinIsVisible(describePG.win)) {
+ ParamLoadControls(&describePG);
+ }
}
/*****************************************************************************
@@ -370,81 +504,93 @@ static void DescChange( long changes )
*/
-EXPORT void DescribeCancel( void )
+EXPORT void DescribeCancel(void)
{
- if ( describePG.win && wWinIsVisible(describePG.win) ) {
- if ( descTrk ) {
- descUpdateFunc( descTrk, -1, descData, TRUE );
- descTrk = NULL;
- DrawDescHilite();
- }
- wHide( describePG.win );
- if ( descUndoStarted ) {
- UndoEnd();
- descUndoStarted = FALSE;
- }
- }
- descNeedDrawHilite = FALSE;
+ if (describePG.win && wWinIsVisible(describePG.win)) {
+ if (descTrk) {
+ descUpdateFunc(descTrk, -1, descData, TRUE);
+ descTrk = NULL;
+ DrawDescHilite();
+ }
+
+ wHide(describePG.win);
+
+ if (descUndoStarted) {
+ UndoEnd();
+ descUndoStarted = FALSE;
+ }
+ }
+
+ descNeedDrawHilite = FALSE;
}
-static STATUS_T CmdDescribe( wAction_t action, coOrd pos )
+static STATUS_T CmdDescribe(wAction_t action, coOrd pos)
{
- track_p trk;
- char msg[STR_SIZE];
-
- switch (action) {
- case C_START:
- InfoMessage( _("Select track to describe") );
- descUndoStarted = FALSE;
- return C_CONTINUE;
-
- case C_DOWN:
- if ((trk = OnTrack( &pos, FALSE, FALSE )) != NULL) {
- if ( describePG.win && wWinIsVisible(describePG.win) && descTrk ) {
- DrawDescHilite();
- descUpdateFunc( descTrk, -1, descData, TRUE );
- descTrk = NULL;
- }
- descBorder = mainD.scale*0.1;
- if ( descBorder < trackGauge )
- descBorder = trackGauge;
- inDescribeCmd = TRUE;
- GetBoundingBox( trk, &descSize, &descOrig );
- descOrig.x -= descBorder;
- descOrig.y -= descBorder;
- descSize.x -= descOrig.x-descBorder;
- descSize.y -= descOrig.y-descBorder;
- descNeedDrawHilite = TRUE;
- DrawDescHilite();
- DescribeTrack( trk, msg, 255 );
- inDescribeCmd = FALSE;
- InfoMessage( msg );
- } else
- InfoMessage( "" );
- return C_CONTINUE;
-
- case C_REDRAW:
- if (describePG.win && wWinIsVisible(describePG.win) && descTrk)
- DrawDescHilite();
- break;
-
- case C_CANCEL:
- DescribeCancel();
- return C_CONTINUE;
- }
- return C_CONTINUE;
+ track_p trk;
+ char msg[STR_SIZE];
+
+ switch (action) {
+ case C_START:
+ InfoMessage(_("Select track to describe"));
+ descUndoStarted = FALSE;
+ return C_CONTINUE;
+
+ case C_DOWN:
+ if ((trk = OnTrack(&pos, FALSE, FALSE)) != NULL) {
+ if (describePG.win && wWinIsVisible(describePG.win) && descTrk) {
+ DrawDescHilite();
+ descUpdateFunc(descTrk, -1, descData, TRUE);
+ descTrk = NULL;
+ }
+
+ descBorder = mainD.scale*0.1;
+
+ if (descBorder < trackGauge) {
+ descBorder = trackGauge;
+ }
+
+ inDescribeCmd = TRUE;
+ GetBoundingBox(trk, &descSize, &descOrig);
+ descOrig.x -= descBorder;
+ descOrig.y -= descBorder;
+ descSize.x -= descOrig.x-descBorder;
+ descSize.y -= descOrig.y-descBorder;
+ descNeedDrawHilite = TRUE;
+ DrawDescHilite();
+ DescribeTrack(trk, msg, 255);
+ inDescribeCmd = FALSE;
+ InfoMessage(msg);
+ } else {
+ InfoMessage("");
+ }
+
+ return C_CONTINUE;
+
+ case C_REDRAW:
+ if (describePG.win && wWinIsVisible(describePG.win) && descTrk) {
+ DrawDescHilite();
+ }
+
+ break;
+
+ case C_CANCEL:
+ DescribeCancel();
+ return C_CONTINUE;
+ }
+
+ return C_CONTINUE;
}
#include "bitmaps/describe.xpm"
-void InitCmdDescribe( wMenu_p menu )
+void InitCmdDescribe(wMenu_p menu)
{
- describeCmdInx = AddMenuButton( menu, CmdDescribe, "cmdDescribe", _("Properties"), wIconCreatePixMap(describe_xpm),
- LEVEL0, IC_CANCEL|IC_POPUP, ACCL_DESCRIBE, NULL );
- RegisterChangeNotification( DescChange );
- ParamRegister( &describePG );
- /*AddPlaybackProc( "DESCRIBE", playbackDescribe, NULL );*/
+ describeCmdInx = AddMenuButton(menu, CmdDescribe, "cmdDescribe",
+ _("Properties"), wIconCreatePixMap(describe_xpm),
+ LEVEL0, IC_CANCEL|IC_POPUP, ACCL_DESCRIBE, NULL);
+ RegisterChangeNotification(DescChange);
+ ParamRegister(&describePG);
}
diff --git a/app/bin/cmodify.c b/app/bin/cmodify.c
index 6828ff9..594d742 100644
--- a/app/bin/cmodify.c
+++ b/app/bin/cmodify.c
@@ -1,6 +1,4 @@
-/*
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/cmodify.c,v 1.4 2008-03-06 19:35:05 m_fischer Exp $
- *
+/** \file cmodify.c
* TRACK MODIFY
*/
@@ -22,18 +20,20 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-#include "track.h"
+#include <math.h>
+
#include "cjoin.h"
#include "ccurve.h"
+#include "cbezier.h"
+#include "ccornu.h"
#include "cstraigh.h"
+#include "cundo.h"
+#include "fileio.h"
#include "i18n.h"
-
-/*****************************************************************************
- *
- * MODIFY
- *
- */
-
+#include "messages.h"
+#include "param.h"
+#include "track.h"
+#include "utility.h"
static struct {
track_p Trk;
@@ -49,12 +49,73 @@ static struct {
static int log_modify;
+static BOOL_T modifyBezierMode;
+static BOOL_T modifyCornuMode;
+
+/*
+ * Call cbezier.c CmdBezModify to alter Bezier Track and Lines.
+ * Picking a Bezier will allow control point(s) modifications until terminated with "Enter"
+ */
+static STATUS_T ModifyBezier(wAction_t action, coOrd pos) {
+ STATUS_T rc = C_CONTINUE;
+ if (Dex.Trk == NULL) return C_ERROR; //No track picked yet!
+ switch (action&0xFF) {
+ case C_START:
+ case C_DOWN:
+ case C_MOVE:
+ case C_UP:
+ case C_OK:
+ case C_TEXT:
+ rc = CmdBezModify(Dex.Trk, action, pos);
+ break;
+ case C_TERMINATE:
+ rc = CmdBezModify(Dex.Trk, action, pos);
+ Dex.Trk = NULL;
+ modifyBezierMode = FALSE;
+ break;
+ case C_REDRAW:
+ rc = CmdBezModify(Dex.Trk, action, pos);
+ break;
+ }
+ return rc;
+}
+
+/*
+ * Call ccornu.c CmdCornuModify to alter Cornu Track and Lines.
+ * Picking a Cornu will allow end point(s) modifications until terminated with "Enter"
+ */
+static STATUS_T ModifyCornu(wAction_t action, coOrd pos) {
+ STATUS_T rc = C_CONTINUE;
+ if (Dex.Trk == NULL) return C_ERROR; //No track picked yet!
+ switch (action&0xFF) {
+ case C_START:
+ case C_DOWN:
+ case C_MOVE:
+ case C_UP:
+ case C_OK:
+ case C_TEXT:
+ rc = CmdCornuModify(Dex.Trk, action, pos);
+ break;
+ case C_TERMINATE:
+ rc = CmdCornuModify(Dex.Trk, action, pos);
+ Dex.Trk = NULL;
+ modifyCornuMode = FALSE;
+ break;
+ case C_REDRAW:
+ rc = CmdCornuModify(Dex.Trk, action, pos);
+ break;
+ }
+ return rc;
+}
static STATUS_T CmdModify(
wAction_t action,
coOrd pos )
/*
- * Extend a track with a curve or straight.
+ * Extend and alter a track.
+ * Extend a track with a curve or straight and optionally an easement.
+ * Alter a ruler.
+ * Modify a Bezier.
*/
{
@@ -86,10 +147,15 @@ static STATUS_T CmdModify(
/*ChangeParameter( &easementPD );*/
trackGauge = 0.0;
changeTrackMode = modifyRulerMode = FALSE;
+ modifyBezierMode = FALSE;
+ modifyCornuMode = FALSE;
return C_CONTINUE;
case C_DOWN:
- changeTrackMode = modifyRulerMode = FALSE;
+ if (modifyBezierMode)
+ return ModifyBezier(C_DOWN, pos);
+ if (modifyCornuMode)
+ return ModifyCornu(C_DOWN, pos);
DYNARR_SET( trkSeg_t, tempSegs_da, 2 );
tempSegs(0).color = wDrawColorBlack;
tempSegs(0).width = 0;
@@ -107,9 +173,29 @@ static STATUS_T CmdModify(
Dex.Trk = NULL;
return C_CONTINUE;
}
+ if (QueryTrack( Dex.Trk, Q_CAN_MODIFY_CONTROL_POINTS )) { //Bezier
+ modifyBezierMode = TRUE;
+ if (ModifyBezier(C_START, pos) != C_CONTINUE) { //Call Start with track
+ modifyBezierMode = FALSE; //Function rejected Bezier
+ Dex.Trk =NULL;
+ tempSegs_da.cnt = 0;
+ }
+ return C_CONTINUE; //That's it
+ }
+ if (QueryTrack( Dex.Trk, Q_IS_CORNU )) { //Bezier
+ modifyCornuMode = TRUE;
+ if (ModifyCornu(C_START, pos) != C_CONTINUE) { //Call Start with track
+ modifyCornuMode = FALSE; //Function rejected Bezier
+ Dex.Trk =NULL;
+ tempSegs_da.cnt = 0;
+ }
+ return C_CONTINUE; //That's it
+ }
+
+
trackGauge = (IsTrack(Dex.Trk)?GetTrkGauge(Dex.Trk):0.0);
if ( (MyGetKeyState()&WKEY_SHIFT) &&
- QueryTrack( Dex.Trk, Q_CAN_MODIFYRADIUS ) &&
+ QueryTrack( Dex.Trk, Q_CAN_MODIFYRADIUS )&&
(inx=PickUnconnectedEndPoint(pos,Dex.Trk)) >= 0 ) {
trk = Dex.Trk;
while ( (trk1=GetTrkEndTrk(trk,1-inx)) &&
@@ -146,6 +232,10 @@ static STATUS_T CmdModify(
return ModifyRuler( C_MOVE, pos );
if (Dex.Trk == NULL)
return C_CONTINUE;
+ if ( modifyBezierMode )
+ return ModifyBezier(C_MOVE, pos);
+ if ( modifyCornuMode )
+ return ModifyCornu(C_MOVE, pos);
DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack );
tempSegs_da.cnt = 0;
SnapPos( &pos );
@@ -165,6 +255,10 @@ static STATUS_T CmdModify(
return C_CONTINUE;
if ( modifyRulerMode )
return ModifyRuler( C_MOVE, pos );
+ if ( modifyBezierMode )
+ return ModifyBezier( C_UP, pos);
+ if (modifyCornuMode)
+ return ModifyCornu(C_UP, pos);
DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack );
tempSegs_da.cnt = 0;
SnapPos( &pos );
@@ -181,6 +275,8 @@ static STATUS_T CmdModify(
case C_RDOWN:
changeTrackMode = TRUE;
modifyRulerMode = FALSE;
+ modifyBezierMode = FALSE;
+ modifyCornuMode = FALSE;
Dex.Trk = OnTrack( &pos, TRUE, TRUE );
if (Dex.Trk) {
if (!CheckTrackLayer( Dex.Trk ) ) {
@@ -212,10 +308,7 @@ LOG( log_modify, 1, ("extend endPt[%d] = [%0.3f %0.3f] A%0.3f\n",
Dex.first = TRUE;
MainRedraw();
MapRedraw();
-#ifdef LATER
- return C_CONTINUE;
-#endif
-
+ /* no break */
case C_RMOVE:
DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack );
tempSegs_da.cnt = 0;
@@ -226,7 +319,21 @@ LOG( log_modify, 1, ("extend endPt[%d] = [%0.3f %0.3f] A%0.3f\n",
return C_CONTINUE;
Dex.first = FALSE;
Dex.pos01 = Dex.pos00;
- PlotCurve( crvCmdFromEP1, Dex.pos00, Dex.pos00x, pos, &Dex.curveData, TRUE );
+ if (Dex.params.type == curveTypeCornu) { //Restrict Cornu drag out to match end
+ ANGLE_T angle2 = NormalizeAngle(FindAngle(pos, Dex.pos00)-Dex.angle);
+ if (angle2 > 90.0 && angle2 < 270.0) {
+ if (Dex.params.cornuRadius[Dex.params.ep] == 0) {
+ Translate( &pos, Dex.pos00, Dex.angle, FindDistance( Dex.pos00, pos ) );
+ } else {
+ ANGLE_T angle = FindAngle(Dex.params.cornuCenter[Dex.params.ep],pos)-
+ FindAngle(Dex.params.cornuCenter[Dex.params.ep],Dex.pos00);
+ pos=Dex.pos00;
+ Rotate(&pos,Dex.params.cornuCenter[Dex.params.ep],angle);
+ }
+ } else pos = Dex.pos00; //Only out from end
+ PlotCurve( crvCmdFromCornu, Dex.pos00, Dex.pos00x, pos, &Dex.curveData, FALSE );
+ } else
+ PlotCurve( crvCmdFromEP1, Dex.pos00, Dex.pos00x, pos, &Dex.curveData, TRUE );
curveType = Dex.curveData.type;
if ( curveType == curveTypeStraight ) {
Dex.r1 = 0.0;
@@ -382,6 +489,8 @@ LOG( log_modify, 1, ("A0 = %0.3f, A1 = %0.3f\n",
return C_TERMINATE;
case C_REDRAW:
+ if (modifyBezierMode) return ModifyBezier(C_REDRAW, pos);
+ if (modifyCornuMode) return ModifyCornu(C_REDRAW, pos);
if ( (!changeTrackMode) && Dex.Trk && !QueryTrack( Dex.Trk, Q_MODIFY_REDRAW_DONT_UNDRAW_TRACK ) )
UndrawNewTrack( Dex.Trk );
DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack );
@@ -390,9 +499,15 @@ LOG( log_modify, 1, ("A0 = %0.3f, A1 = %0.3f\n",
case C_TEXT:
if ( !Dex.Trk )
return C_CONTINUE;
+ if (modifyBezierMode)
+ return ModifyBezier(action, pos);
+ if (modifyCornuMode)
+ return ModifyCornu(action, pos);
return ModifyTrack( Dex.Trk, action, pos );
default:
+ if (modifyBezierMode) return ModifyBezier(action, pos);
+ if (modifyCornuMode) return ModifyCornu(action, pos);
return C_CONTINUE;
}
}
diff --git a/app/bin/cnote.c b/app/bin/cnote.c
index 88c9986..3cbd28d 100644
--- a/app/bin/cnote.c
+++ b/app/bin/cnote.c
@@ -1,5 +1,5 @@
-/*
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/cnote.c,v 1.6 2008-03-10 18:59:53 m_fischer Exp $
+/** \file cnote.c
+ * NOTE
*/
/* XTrkCad - Model Railroad CAD
@@ -19,23 +19,23 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
+#include <string.h>
-#include "track.h"
+#include "cundo.h"
+#include "custom.h"
+#include "fileio.h"
#include "i18n.h"
-
-/*****************************************************************************
- *
- * NOTE
- *
- */
+#include "param.h"
+#include "track.h"
+#include "utility.h"
static TRKTYP_T T_NOTE = -1;
static wDrawBitMap_p note_bm;
struct extraData {
- coOrd pos;
- char * text;
- };
+ coOrd pos;
+ char * text;
+};
extern BOOL_T inDescribeCmd;
@@ -49,56 +49,63 @@ static paramTextData_t noteTextData = { 300, 150 };
static paramData_t notePLs[] = {
#define I_NOTETEXT (0)
#define noteT ((wText_p)notePLs[I_NOTETEXT].control)
- { PD_TEXT, NULL, "text", PDO_DLGRESIZE, &noteTextData } };
+ { PD_TEXT, NULL, "text", PDO_DLGRESIZE, &noteTextData }
+};
static paramGroup_t notePG = { "note", 0, notePLs, sizeof notePLs/sizeof notePLs[0] };
-static track_p NewNote( wIndex_t index, coOrd p, long size )
+static track_p NewNote(wIndex_t index, coOrd p, long size)
{
- track_p t;
- struct extraData * xx;
- t = NewTrack( index, T_NOTE, 0, sizeof *xx );
- xx = GetTrkExtraData(t);
- xx->pos = p;
- xx->text = (char*)MyMalloc( (int)size + 2 );
- SetBoundingBox( t, p, p );
- return t;
+ track_p t;
+ struct extraData * xx;
+ t = NewTrack(index, T_NOTE, 0, sizeof *xx);
+ xx = GetTrkExtraData(t);
+ xx->pos = p;
+ xx->text = (char*)MyMalloc((int)size + 2);
+ SetBoundingBox(t, p, p);
+ return t;
}
-void ClearNote( void )
+void ClearNote(void)
{
- if (mainText) {
- MyFree(mainText);
- mainText = NULL;
- }
+ if (mainText) {
+ MyFree(mainText);
+ mainText = NULL;
+ }
}
-static void NoteOk( void * junk )
+static void NoteOk(void * junk)
{
- int len;
- if ( wTextGetModified(noteT) ) {
- ClearNote();
- len = wTextGetSize( noteT );
- mainText = (char*)MyMalloc( len+2 );
- wTextGetText( noteT, mainText, len );
- if (mainText[len-1] != '\n') {
- mainText[len++] = '\n';
- }
- mainText[len] = '\0';
- }
- wHide( noteW );
+ if (wTextGetModified(noteT)) {
+ int len;
+ ClearNote();
+ len = wTextGetSize(noteT);
+ mainText = (char*)MyMalloc(len+2);
+ wTextGetText(noteT, mainText, len);
+
+ if (mainText[len-1] != '\n') {
+ mainText[len++] = '\n';
+ }
+
+ mainText[len] = '\0';
+ }
+
+ wHide(noteW);
}
-void DoNote( void )
+void DoNote(void)
{
- if ( noteW == NULL ) {
- noteW = ParamCreateDialog( &notePG, MakeWindowTitle(_("Note")), _("Ok"), NoteOk, NULL, FALSE, NULL, F_RESIZE, NULL );
- }
- wTextClear( noteT );
- wTextAppend( noteT, mainText?mainText:_("Replace this text with your layout notes") );
- wTextSetReadonly( noteT, FALSE );
- wShow( noteW );
+ if (noteW == NULL) {
+ noteW = ParamCreateDialog(&notePG, MakeWindowTitle(_("Note")), _("Ok"), NoteOk,
+ NULL, FALSE, NULL, F_RESIZE, NULL);
+ }
+
+ wTextClear(noteT);
+ wTextAppend(noteT, mainText?mainText:
+ _("Replace this text with your layout notes"));
+ wTextSetReadonly(noteT, FALSE);
+ wShow(noteW);
}
@@ -107,238 +114,287 @@ void DoNote( void )
* NOTE OBJECT
*/
-static void DrawNote( track_p t, drawCmd_p d, wDrawColor color )
+static void DrawNote(track_p t, drawCmd_p d, wDrawColor color)
{
- struct extraData *xx = GetTrkExtraData(t);
- coOrd p[4];
- DIST_T dist;
- if (d->scale >= 16)
- return;
- if ( (d->funcs->options & wDrawOptTemp) == 0 ) {
- DrawBitMap( d, xx->pos, note_bm, color );
- } else {
- dist = 0.1*d->scale;
- p[0].x = p[1].x = xx->pos.x-dist;
- p[2].x = p[3].x = xx->pos.x+dist;
- p[1].y = p[2].y = xx->pos.y-dist;
- p[3].y = p[0].y = xx->pos.y+dist;
- DrawLine( d, p[0], p[1], 0, color );
- DrawLine( d, p[1], p[2], 0, color );
- DrawLine( d, p[2], p[3], 0, color );
- DrawLine( d, p[3], p[0], 0, color );
- }
+ struct extraData *xx = GetTrkExtraData(t);
+ coOrd p[4];
+
+ if (d->scale >= 16) {
+ return;
+ }
+
+ if ((d->funcs->options & wDrawOptTemp) == 0) {
+ DrawBitMap(d, xx->pos, note_bm, color);
+ } else {
+ DIST_T dist;
+ dist = 0.1*d->scale;
+ p[0].x = p[1].x = xx->pos.x-dist;
+ p[2].x = p[3].x = xx->pos.x+dist;
+ p[1].y = p[2].y = xx->pos.y-dist;
+ p[3].y = p[0].y = xx->pos.y+dist;
+ DrawLine(d, p[0], p[1], 0, color);
+ DrawLine(d, p[1], p[2], 0, color);
+ DrawLine(d, p[2], p[3], 0, color);
+ DrawLine(d, p[3], p[0], 0, color);
+ }
}
-static DIST_T DistanceNote( track_p t, coOrd * p )
+static DIST_T DistanceNote(track_p t, coOrd * p)
{
- struct extraData *xx = GetTrkExtraData(t);
- DIST_T d;
- d = FindDistance( *p, xx->pos );
- if (d < 1.0)
- return d;
- return 100000.0;
+ struct extraData *xx = GetTrkExtraData(t);
+ DIST_T d;
+ d = FindDistance(*p, xx->pos);
+
+ if (d < 1.0) {
+ return d;
+ }
+
+ return 100000.0;
}
static struct {
- coOrd pos;
- } noteData;
+ coOrd pos;
+ unsigned int layer;
+} noteData;
typedef enum { OR, LY, TX } noteDesc_e;
static descData_t noteDesc[] = {
-/*OR*/ { DESC_POS, N_("Position"), &noteData.pos },
-/*LY*/ { DESC_LAYER, N_("Layer"), NULL },
-/*TX*/ { DESC_TEXT, NULL, NULL },
- { DESC_NULL } };
-
-static void UpdateNote( track_p trk, int inx, descData_p descUpd, BOOL_T needUndoStart )
+ /*OR*/ { DESC_POS, N_("Position"), &noteData.pos },
+ /*LY*/ { DESC_LAYER, N_("Layer"), &noteData.layer },
+ /*TX*/ { DESC_TEXT, NULL, NULL },
+ { DESC_NULL }
+};
+
+static void UpdateNote(track_p trk, int inx, descData_p descUpd,
+ BOOL_T needUndoStart)
{
- struct extraData *xx = GetTrkExtraData(trk);
- int len;
-
- switch ( inx ) {
- case OR:
- UndrawNewTrack( trk );
- xx->pos = noteData.pos;
- SetBoundingBox( trk, xx->pos, xx->pos );
- DrawNewTrack( trk );
- break;
- case -1:
- if ( wTextGetModified((wText_p)noteDesc[TX].control0) ) {
- if ( needUndoStart )
- UndoStart( _("Change Track"), "Change Track" );
- UndoModify( trk );
- MyFree( xx->text );
- len = wTextGetSize( (wText_p)noteDesc[TX].control0 );
- xx->text = (char*)MyMalloc( len+2 );
- wTextGetText( (wText_p)noteDesc[TX].control0, xx->text, len );
- if (xx->text[len-1] != '\n') {
- xx->text[len++] = '\n';
- }
- xx->text[len] = '\0';
- }
- break;
- default:
- break;
- }
+ struct extraData *xx = GetTrkExtraData(trk);
+
+ switch (inx) {
+ case OR:
+ xx->pos = noteData.pos;
+ SetBoundingBox(trk, xx->pos, xx->pos);
+ MainRedraw();
+ break;
+
+ case LY:
+ SetTrkLayer(trk, noteData.layer);
+ MainRedraw();
+ break;
+
+ case -1:
+ if (wTextGetModified((wText_p)noteDesc[TX].control0)) {
+ int len;
+
+ if (needUndoStart) {
+ UndoStart(_("Change Track"), "Change Track");
+ }
+
+ UndoModify(trk);
+ MyFree(xx->text);
+ len = wTextGetSize((wText_p)noteDesc[TX].control0);
+ xx->text = (char*)MyMalloc(len+2);
+ wTextGetText((wText_p)noteDesc[TX].control0, xx->text, len);
+
+ if (xx->text[len-1] != '\n') {
+ xx->text[len++] = '\n';
+ }
+
+ xx->text[len] = '\0';
+ }
+ MainRedraw();
+ break;
+
+ default:
+ break;
+ }
}
-static void DescribeNote( track_p trk, char * str, CSIZE_T len )
+static void DescribeNote(track_p trk, char * str, CSIZE_T len)
{
- struct extraData * xx = GetTrkExtraData(trk);
-
- strcpy( str, _("Note: ") );
- len -= strlen(_("Note: "));
- str += strlen(_("Note: "));
- strncpy( str, xx->text, len );
- for (;*str;str++) {
- if (*str=='\n')
- *str = ' ';
- }
- noteData.pos = xx->pos;
- noteDesc[TX].valueP = xx->text;
- noteDesc[OR].mode = 0;
- noteDesc[TX].mode = 0;
- noteDesc[LY].mode = DESC_RO;
- DoDescribe( _("Note"), trk, noteDesc, UpdateNote );
+ struct extraData * xx = GetTrkExtraData(trk);
+ strcpy(str, _("Note: "));
+ len -= strlen(_("Note: "));
+ str += strlen(_("Note: "));
+ strncpy(str, xx->text, len);
+
+ for (; *str; str++) {
+ if (*str=='\n') {
+ *str = ' ';
+ }
+ }
+
+ noteData.pos = xx->pos;
+ noteDesc[TX].valueP = xx->text;
+ noteDesc[OR].mode = 0;
+ noteDesc[TX].mode = 0;
+ noteDesc[LY].mode = DESC_NOREDRAW;
+ DoDescribe(_("Note"), trk, noteDesc, UpdateNote);
}
-static void DeleteNote( track_p t )
+static void DeleteNote(track_p t)
{
- struct extraData *xx = GetTrkExtraData(t);
- if (xx->text)
- MyFree( xx->text );
+ struct extraData *xx = GetTrkExtraData(t);
+
+ if (xx->text) {
+ MyFree(xx->text);
+ }
}
-static BOOL_T WriteNote( track_p t, FILE * f )
+static BOOL_T WriteNote(track_p t, FILE * f)
{
- struct extraData *xx = GetTrkExtraData(t);
- int len;
- BOOL_T addNL = FALSE;
- BOOL_T rc = TRUE;
- len = strlen(xx->text);
- if ( xx->text[len-1] != '\n' ) {
- len++;
- addNL = TRUE;
- }
- rc &= fprintf(f, "NOTE %d %d 0 0 %0.6f %0.6f 0 %d\n", GetTrkIndex(t), GetTrkLayer(t),
- xx->pos.x, xx->pos.y, len )>0;
- rc &= fprintf(f, "%s%s", xx->text, addNL?"\n":"" )>0;
- rc &= fprintf(f, " END\n")>0;
- return rc;
+ struct extraData *xx = GetTrkExtraData(t);
+ int len;
+ BOOL_T addNL = FALSE;
+ BOOL_T rc = TRUE;
+ len = strlen(xx->text);
+
+ if (xx->text[len-1] != '\n') {
+ len++;
+ addNL = TRUE;
+ }
+
+ rc &= fprintf(f, "NOTE %d %d 0 0 %0.6f %0.6f 0 %d\n", GetTrkIndex(t),
+ GetTrkLayer(t),
+ xx->pos.x, xx->pos.y, len)>0;
+ rc &= fprintf(f, "%s%s", xx->text, addNL?"\n":"")>0;
+ rc &= fprintf(f, " END\n")>0;
+ return rc;
}
-static void ReadNote( char * line )
+static void ReadNote(char * line)
{
- coOrd pos;
- DIST_T elev;
- CSIZE_T size;
- char * cp;
- track_p t;
- struct extraData *xx;
- int len;
- wIndex_t index;
- wIndex_t layer;
- int lineCount;
-
- if ( strncmp( line, "NOTE MAIN", 9 ) == 0 ){
- if ( !GetArgs( line+9, paramVersion<3?"d":"0000d", &size ) )
- return;
- if (mainText)
- MyFree( mainText );
- mainText = (char*)MyMalloc( size+2 );
- cp = mainText;
- } else {
- if ( !GetArgs( line+5, paramVersion<3?"XXpYd":paramVersion<9?"dL00pYd":"dL00pfd",
- &index, &layer, &pos, &elev, &size ) ) {
- return;
- }
- t = NewNote( index, pos, size+2 );
- SetTrkLayer( t, layer );
- xx = GetTrkExtraData(t);
- cp = xx->text;
- }
- lineCount = 0;
- while (1) {
- line = GetNextLine();
- if (strncmp(line, " END", 7) == 0)
- break;
- len = strlen(line);
- if (size > 0 && size < len) {
- InputError( "NOTE text overflow", TRUE );
- size = -1;
- }
- if (size > 0) {
- if ( lineCount != 0 ) {
- strcat( cp, "\n" );
- cp++;
- size--;
- }
- strcpy( cp, line );
- cp += len;
- size -= len;
- }
- lineCount++;
- }
- if (cp[-1] != '\n')
- *cp++ = '\n';
- *cp = '\0';
+ coOrd pos;
+ DIST_T elev;
+ CSIZE_T size;
+ char * cp;
+ struct extraData *xx;
+ wIndex_t index;
+ wIndex_t layer;
+ int lineCount;
+
+ if (strncmp(line, "NOTE MAIN", 9) == 0) {
+ if (!GetArgs(line+9, paramVersion<3?"d":"0000d", &size)) {
+ return;
+ }
+
+ if (mainText) {
+ MyFree(mainText);
+ }
+
+ mainText = (char*)MyMalloc(size+2);
+ cp = mainText;
+ } else {
+ track_p t;
+
+ if (!GetArgs(line+5, paramVersion<3?"XXpYd":paramVersion<9?"dL00pYd":"dL00pfd",
+ &index, &layer, &pos, &elev, &size)) {
+ return;
+ }
+
+ t = NewNote(index, pos, size+2);
+ SetTrkLayer(t, layer);
+ xx = GetTrkExtraData(t);
+ cp = xx->text;
+ }
+
+ lineCount = 0;
+
+ while (1) {
+ int len;
+ line = GetNextLine();
+
+ if (strncmp(line, " END", 7) == 0) {
+ break;
+ }
+
+ len = strlen(line);
+
+ if (size > 0 && size < len) {
+ InputError("NOTE text overflow", TRUE);
+ size = -1;
+ }
+
+ if (size > 0) {
+ if (lineCount != 0) {
+ strcat(cp, "\n");
+ cp++;
+ size--;
+ }
+
+ strcpy(cp, line);
+ cp += len;
+ size -= len;
+ }
+
+ lineCount++;
+ }
+
+ if (cp[-1] != '\n') {
+ *cp++ = '\n';
+ }
+
+ *cp = '\0';
}
-static void MoveNote( track_p trk, coOrd orig )
+static void MoveNote(track_p trk, coOrd orig)
{
- struct extraData * xx = GetTrkExtraData( trk );
- xx->pos.x += orig.x;
- xx->pos.y += orig.y;
- SetBoundingBox( trk, xx->pos, xx->pos );
+ struct extraData * xx = GetTrkExtraData(trk);
+ xx->pos.x += orig.x;
+ xx->pos.y += orig.y;
+ SetBoundingBox(trk, xx->pos, xx->pos);
}
-static void RotateNote( track_p trk, coOrd orig, ANGLE_T angle )
+static void RotateNote(track_p trk, coOrd orig, ANGLE_T angle)
{
- struct extraData * xx = GetTrkExtraData( trk );
- Rotate( &xx->pos, orig, angle );
- SetBoundingBox( trk, xx->pos, xx->pos );
+ struct extraData * xx = GetTrkExtraData(trk);
+ Rotate(&xx->pos, orig, angle);
+ SetBoundingBox(trk, xx->pos, xx->pos);
}
-static void RescaleNote( track_p trk, FLOAT_T ratio )
+static void RescaleNote(track_p trk, FLOAT_T ratio)
{
- struct extraData * xx = GetTrkExtraData( trk );
- xx->pos.x *= ratio;
- xx->pos.y *= ratio;
+ struct extraData * xx = GetTrkExtraData(trk);
+ xx->pos.x *= ratio;
+ xx->pos.y *= ratio;
}
static trackCmd_t noteCmds = {
- "NOTE",
- DrawNote,
- DistanceNote,
- DescribeNote,
- DeleteNote,
- WriteNote,
- ReadNote,
- MoveNote,
- RotateNote,
- RescaleNote,
- NULL, /* audit */
- NULL, /* getAngle */
- NULL, /* split */
- NULL, /* traverse */
- NULL, /* enumerate */
- NULL /* redraw */ };
-
-
-BOOL_T WriteMainNote( FILE* f )
+ "NOTE",
+ DrawNote,
+ DistanceNote,
+ DescribeNote,
+ DeleteNote,
+ WriteNote,
+ ReadNote,
+ MoveNote,
+ RotateNote,
+ RescaleNote,
+ NULL, /* audit */
+ NULL, /* getAngle */
+ NULL, /* split */
+ NULL, /* traverse */
+ NULL, /* enumerate */
+ NULL /* redraw */
+};
+
+
+BOOL_T WriteMainNote(FILE* f)
{
- BOOL_T rc = TRUE;
- if (mainText && *mainText) {
- rc &= fprintf(f, "NOTE MAIN 0 0 0 0 %d\n", strlen(mainText) )>0;
- rc &= fprintf(f, "%s", mainText )>0;
- rc &= fprintf(f, " END\n")>0;
- }
- return rc;
+ BOOL_T rc = TRUE;
+
+ if (mainText && *mainText) {
+ rc &= fprintf(f, "NOTE MAIN 0 0 0 0 %lu\n", strlen(mainText))>0;
+ rc &= fprintf(f, "%s", mainText)>0;
+ rc &= fprintf(f, " END\n")>0;
+ }
+
+ return rc;
}
/*****************************************************************************
@@ -347,63 +403,71 @@ BOOL_T WriteMainNote( FILE* f )
-static STATUS_T CmdNote( wAction_t action, coOrd pos )
+static STATUS_T CmdNote(wAction_t action, coOrd pos)
{
- static coOrd oldPos;
- track_p trk;
- struct extraData * xx;
- const char* tmpPtrText;
-
- switch (action) {
- case C_START:
- InfoMessage( _("Place a note on the layout") );
- return C_CONTINUE;
- case C_DOWN:
- DrawBitMap( &tempD, pos, note_bm, normalColor );
- oldPos = pos;
- return C_CONTINUE;
- case C_MOVE:
- DrawBitMap( &tempD, oldPos, note_bm, normalColor );
- DrawBitMap( &tempD, pos, note_bm, normalColor );
- oldPos = pos;
- return C_CONTINUE;
- break;
- case C_UP:
- UndoStart( _("New Note"), "New Note" );
- trk = NewNote( -1, pos, 2 );
- DrawNewTrack( trk );
- xx = GetTrkExtraData(trk);
-
- tmpPtrText = _("Replace this text with your note");
- xx->text = (char*)MyMalloc( strlen(tmpPtrText) + 1 );
- strcpy( xx->text, tmpPtrText);
-
- inDescribeCmd = TRUE;
- DescribeNote( trk, message, sizeof message );
- inDescribeCmd = FALSE;
- return C_CONTINUE;
- case C_REDRAW:
- DrawBitMap( &tempD, oldPos, note_bm, normalColor );
- return C_CONTINUE;
- case C_CANCEL:
- DescribeCancel();
- return C_CONTINUE;
- }
- return C_INFO;
+ static coOrd oldPos;
+ track_p trk;
+ struct extraData * xx;
+ const char* tmpPtrText;
+ static int state_on = FALSE;
+
+ switch (action) {
+ case C_START:
+ InfoMessage(_("Place a note on the layout"));
+ return C_CONTINUE;
+
+ case C_DOWN:
+ state_on = TRUE;
+ oldPos = pos;
+ MainRedraw();
+ return C_CONTINUE;
+
+ case C_MOVE:
+ oldPos = pos;
+ MainRedraw();
+ return C_CONTINUE;
+ break;
+
+ case C_UP:
+ UndoStart(_("New Note"), "New Note");
+ state_on = FALSE;
+ MainRedraw();
+ trk = NewNote(-1, pos, 2);
+ DrawNewTrack(trk);
+ xx = GetTrkExtraData(trk);
+ tmpPtrText = _("Replace this text with your note");
+ xx->text = (char*)MyMalloc(strlen(tmpPtrText) + 1);
+ strcpy(xx->text, tmpPtrText);
+ inDescribeCmd = TRUE;
+ DescribeNote(trk, message, sizeof message);
+ inDescribeCmd = FALSE;
+ return C_CONTINUE;
+
+ case C_REDRAW:
+ if (state_on) DrawBitMap(&tempD, oldPos, note_bm, normalColor);
+ return C_CONTINUE;
+
+ case C_CANCEL:
+ DescribeCancel();
+ return C_CONTINUE;
+ }
+
+ return C_INFO;
}
#include "bitmaps/note.xbm"
#include "bitmaps/cnote.xpm"
-void InitCmdNote( wMenu_p menu )
+void InitCmdNote(wMenu_p menu)
{
- ParamRegister( &notePG );
- AddMenuButton( menu, CmdNote, "cmdNote", _("Note"), wIconCreatePixMap(cnote_xpm), LEVEL0_50, IC_POPUP2, ACCL_NOTE, NULL );
+ ParamRegister(&notePG);
+ AddMenuButton(menu, CmdNote, "cmdNote", _("Note"), wIconCreatePixMap(cnote_xpm),
+ LEVEL0_50, IC_POPUP2, ACCL_NOTE, NULL);
}
-void InitTrkNote( void )
+void InitTrkNote(void)
{
- note_bm = wDrawBitMapCreate( mainD.d, note_width, note_width, 8, 8, note_bits );
- T_NOTE = InitObject( &noteCmds );
+ note_bm = wDrawBitMapCreate(mainD.d, note_width, note_width, 8, 8, note_bits);
+ T_NOTE = InitObject(&noteCmds);
}
diff --git a/app/bin/common.h b/app/bin/common.h
index e238e33..255e8d7 100644
--- a/app/bin/common.h
+++ b/app/bin/common.h
@@ -1,5 +1,5 @@
-/*
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/common.h,v 1.2 2008-02-23 07:27:15 m_fischer Exp $
+/** \file common.h
+ * Defnitions of basic types
*/
/* XTrkCad - Model Railroad CAD
@@ -23,6 +23,8 @@
#ifndef COMMON_H
#define COMMON_H
+#include <stdlib.h>
+
#ifndef TRUE
#define TRUE (1)
#define FALSE (0)
@@ -99,21 +101,18 @@ typedef struct {
abort(); \
} \
(DA).cnt = N; }
+#define DYNARR_FREE(T,DA) \
+ { if ((DA).ptr) { \
+ MyFree( (DA).ptr); \
+ (DA).ptr = NULL; \
+ } \
+ (DA).max = 0; \
+ (DA).cnt = 0; }
#ifdef WINDOWS
-#ifdef FAR
-#undef FAR
-#endif
-#ifndef WIN32
-#define FAR _far
-#else
-#define FAR
-#endif
#define M_PI 3.14159
#define strcasecmp _stricmp
#define strncasecmp _strnicmp
-#else
-#define FAR
#endif
#if _MSC_VER >1300
diff --git a/app/bin/compound.c b/app/bin/compound.c
index ed585f7..972ff82 100644
--- a/app/bin/compound.c
+++ b/app/bin/compound.c
@@ -22,15 +22,20 @@
*/
#include <ctype.h>
-#include "track.h"
-#include "compound.h"
-#include "shrtpath.h"
+#include <math.h>
+#include <string.h>
+
+
+#include "tbezier.h"
#include "cjoin.h"
+#include "common.h"
+#include "compound.h"
+#include "cundo.h"
+#include "fileio.h"
#include "i18n.h"
-
-#if _MSC_VER >=1400
-#define strdup _strdup
-#endif
+#include "shrtpath.h"
+#include "track.h"
+#include "utility.h"
/*****************************************************************************
*
@@ -326,41 +331,6 @@ void DrawCompoundDescription(
Rotate( &p1, zero, xx->angle );
p1.x += xx->orig.x + xx->descriptionOff.x;
p1.y += xx->orig.y + xx->descriptionOff.y;
-#ifdef LATER
- maxInx = -1;
- for ( inx=0,a=0.0; a<360.0; inx++,a+=45 ) {
- Translate( &p1, p0, a, trackGauge*3 );
- dists[inx].p = p1;
- if ((trk1 = dists[inx].trk = OnTrack( &p1, FALSE, TRUE )) == NULL ||
- trk1 == trk ) {
- p1 = dists[inx].p;
- dists[inx].d = DistanceSegs( xx->orig, xx->angle, xx->segCnt, xx->segs, &p1, NULL );
- } else if ( GetTrkType(trk1) == T_TURNOUT ) {
- struct extraData *yy = GetTrkExtraData(trk1);
- dists[inx].d = DistanceSegs( yy->orig, yy->angle, yy->segCnt, yy->segs, &p1, NULL );
- } else {
- dists[inx].d = FindDistance( p0, p1 );
- }
- }
- maxD = 0; maxInx = -1;
- for ( inx=0,a=0.0; a<360.0; inx++,a+=45 ) {
- if (dists[inx].trk == NULL || dists[inx].trk == trk) {
- if (dists[inx].d > maxD) {
- maxD = dists[inx].d;
- maxInx = inx;
- }
- }
- }
- if (maxInx == -1) {
- if (dists[inx].d > maxD) {
- maxD = dists[inx].d;
- maxInx = inx;
- }
- }
- if (maxInx != -1) {
- p0 = dists[maxInx].p;
- }
-#endif
fp = wStandardFont( F_TIMES, FALSE, FALSE );
DrawBoxedString( (xx->special==TOpier)?BOX_INVERT:BOX_NONE, d, p1, desc, fp, (wFontSize_t)descriptionFontSize, color, 0.0 );
}
@@ -374,6 +344,8 @@ DIST_T CompoundDescriptionDistance(
coOrd p1;
if (GetTrkType(trk) != T_TURNOUT && GetTrkType(trk) != T_STRUCTURE)
return 100000;
+ if ( (GetTrkBits( trk ) & TB_HIDEDESC) != 0 )
+ return 100000;
p1 = xx->descriptionOrig;
Rotate( &p1, zero, xx->angle );
p1.x += xx->orig.x + xx->descriptionOff.x;
@@ -389,30 +361,37 @@ STATUS_T CompoundDescriptionMove(
{
struct extraData *xx = GetTrkExtraData(trk);
static coOrd p0, p1;
+ static BOOL_T editMode;
wDrawColor color;
switch (action) {
case C_DOWN:
+ editMode = TRUE;
REORIGIN( p0, xx->descriptionOrig, xx->angle, xx->orig )
case C_MOVE:
case C_UP:
- if (action != C_DOWN)
- DrawLine( &tempD, p0, p1, 0, wDrawColorBlack );
color = GetTrkColor( trk, &mainD );
- DrawCompoundDescription( trk, &tempD, color );
xx->descriptionOff.x = (pos.x-p0.x);
xx->descriptionOff.y = (pos.y-p0.y);
p1 = xx->descriptionOrig;
Rotate( &p1, zero, xx->angle );
p1.x += xx->orig.x + xx->descriptionOff.x;
p1.y += xx->orig.y + xx->descriptionOff.y;
- DrawCompoundDescription( trk, &tempD, color );
- if (action != C_UP)
- DrawLine( &tempD, p0, p1, 0, wDrawColorBlack );
- MainRedraw();
+ if (action == C_UP) {
+ editMode = FALSE;
+ }
+ MainRedraw();
+ MapRedraw();
return action==C_UP?C_TERMINATE:C_CONTINUE;
+ break;
+ case C_REDRAW:
+ if (editMode) {
+ DrawLine( &tempD, p0, p1, 0, wDrawColorBlack );
+ }
}
+
+
return C_CONTINUE;
}
@@ -494,8 +473,11 @@ DIST_T DistanceCompound(
static struct {
- coOrd endPt[2];
- FLOAT_T elev[2];
+ coOrd endPt[4];
+ ANGLE_T endAngle[4];
+ DIST_T endRadius[4];
+ coOrd endCenter[4];
+ FLOAT_T elev[4];
coOrd orig;
ANGLE_T angle;
char manuf[STR_SIZE];
@@ -503,23 +485,40 @@ static struct {
char partno[STR_SIZE];
long epCnt;
long segCnt;
+ long pathCnt;
FLOAT_T grade;
DIST_T length;
- LAYER_T layerNumber;
+ unsigned int layerNumber;
} compoundData;
-typedef enum { E0, Z0, E1, Z1, GR, OR, AN, MN, NM, PN, EC, SC, LY } compoundDesc_e;
+typedef enum { E0, A0, C0, R0, Z0, E1, A1, C1, R1, Z1, E2, A2, C2, R2, Z2, E3, A3, C3, R3, Z3, GR, OR, AN, MN, NM, PN, EC, SC, LY } compoundDesc_e;
static descData_t compoundDesc[] = {
-/*E0*/ { DESC_POS, N_("End Pt 1: X"), &compoundData.endPt[0] },
-/*Z0*/ { DESC_DIM, N_("Z"), &compoundData.elev[0] },
-/*E1*/ { DESC_POS, N_("End Pt 2: X"), &compoundData.endPt[1] },
-/*Z1*/ { DESC_DIM, N_("Z"), &compoundData.elev[1] },
+/*E0*/ { DESC_POS, N_("End Pt 1: X,Y"), &compoundData.endPt[0] },
+/*A0*/ { DESC_ANGLE, N_("Angle"), &compoundData.endAngle[0] },
+/*C0*/ { DESC_POS, N_("Center X,Y"), &compoundData.endCenter[0] },
+/*R0*/ { DESC_DIM, N_("Radius"), &compoundData.endRadius[0] },
+/*Z0*/ { DESC_DIM, N_("Z1"), &compoundData.elev[0] },
+/*E1*/ { DESC_POS, N_("End Pt 2: X,Y"), &compoundData.endPt[1] },
+/*A1*/ { DESC_ANGLE, N_("Angle"), &compoundData.endAngle[1] },
+/*C1*/ { DESC_POS, N_("Center X,Y"), &compoundData.endCenter[1] },
+/*R1*/ { DESC_DIM, N_("Radius"), &compoundData.endRadius[1] },
+/*Z1*/ { DESC_DIM, N_("Z2"), &compoundData.elev[1] },
+/*E2*/ { DESC_POS, N_("End Pt 3: X,Y"), &compoundData.endPt[2] },
+/*A2*/ { DESC_ANGLE, N_("Angle"), &compoundData.endAngle[2] },
+/*C2*/ { DESC_POS, N_("Center X,Y"), &compoundData.endCenter[2] },
+/*R2*/ { DESC_DIM, N_("Radius"), &compoundData.endRadius[2] },
+/*Z2*/ { DESC_DIM, N_("Z3"), &compoundData.elev[2] },
+/*E3*/ { DESC_POS, N_("End Pt 4: X,Y"), &compoundData.endPt[3] },
+/*A3*/ { DESC_ANGLE, N_("Angle"), &compoundData.endAngle[3] },
+/*C3*/ { DESC_POS, N_("Center X,Y"), &compoundData.endCenter[3] },
+/*R3*/ { DESC_DIM, N_("Radius"), &compoundData.endRadius[3] },
+/*Z3*/ { DESC_DIM, N_("Z4"), &compoundData.elev[3] },
/*GR*/ { DESC_FLOAT, N_("Grade"), &compoundData.grade },
-/*OR*/ { DESC_POS, N_("Origin: X"), &compoundData.orig },
+/*OR*/ { DESC_POS, N_("Origin: X,Y"), &compoundData.orig },
/*AN*/ { DESC_ANGLE, N_("Angle"), &compoundData.angle },
/*MN*/ { DESC_STRING, N_("Manufacturer"), &compoundData.manuf },
/*NM*/ { DESC_STRING, N_("Name"), &compoundData.name },
/*PN*/ { DESC_STRING, N_("Part No"), &compoundData.partno },
-/*EC*/ { DESC_LONG, N_("# End Pt"), &compoundData.epCnt },
+/*EC*/ { DESC_LONG, N_("# End Pts"), &compoundData.epCnt },
/*SC*/ { DESC_LONG, N_("# Segments"), &compoundData.segCnt },
/*LY*/ { DESC_LAYER, N_("Layer"), &compoundData.layerNumber },
{ DESC_NULL } };
@@ -616,36 +615,55 @@ static void UpdateCompound( track_p trk, int inx, descData_p descUpd, BOOL_T nee
MoveTrack( trk, pos );
ComputeCompoundBoundingBox( trk );
break;
+ case A0:
+ case A1:
+ case A2:
+ case A3:
+ if (inx==E3) ep=3;
+ else if (inx==E2) ep=2;
+ else if (inx==E1) ep=1;
+ else ep=0;
+ RotateTrack( trk, xx->orig, NormalizeAngle( compoundData.endAngle[ep]-xx->angle ) );
+ ComputeCompoundBoundingBox( trk );
+ compoundData.angle = xx->angle;
+ compoundDesc[AN].mode |= DESC_CHANGE;
+ break;
case AN:
RotateTrack( trk, xx->orig, NormalizeAngle( compoundData.angle-xx->angle ) );
ComputeCompoundBoundingBox( trk );
break;
case E0:
case E1:
- ep = (inx==E0?0:1);
+ case E2:
+ case E3:
+ if (inx==E3) ep=3;
+ else if (inx==E2) ep=2;
+ else if (inx==E1) ep=1;
+ else ep=0;
pos = GetTrkEndPos(trk,ep);
pos.x = compoundData.endPt[ep].x - pos.x;
pos.y = compoundData.endPt[ep].y - pos.y;
MoveTrack( trk, pos );
ComputeCompoundBoundingBox( trk );
- if ( compoundData.epCnt >= 2 ) {
- compoundData.endPt[1-ep] = GetTrkEndPos(trk,1-ep);
- compoundDesc[inx==E0?E1:E0].mode |= DESC_CHANGE;
- }
break;
case Z0:
case Z1:
- ep = (inx==Z0?0:1);
+ case Z2:
+ case Z3:
+ ep = (inx==Z0?0:(inx==Z1?1:(inx==Z2?2:3)));
UpdateTrkEndElev( trk, ep, GetTrkEndElevUnmaskedMode(trk,ep), compoundData.elev[ep], NULL );
if ( GetTrkEndPtCnt(trk) == 1 )
break;
- ComputeElev( trk, 1-ep, FALSE, &compoundData.elev[1-ep], NULL );
+ for (int i=0;i<compoundData.epCnt;i++) {
+ if (i==ep) continue;
+ ComputeElev( trk, i, FALSE, &compoundData.elev[i], NULL );
+ }
if ( compoundData.length > minLength )
compoundData.grade = fabs( (compoundData.elev[0]-compoundData.elev[1])/compoundData.length )*100.0;
else
compoundData.grade = 0.0;
compoundDesc[GR].mode |= DESC_CHANGE;
- compoundDesc[inx==Z0?Z1:Z0].mode |= DESC_CHANGE;
+ compoundDesc[Z0+(E1-E0)*inx].mode |= DESC_CHANGE;
break;
case LY:
SetTrkLayer( trk, compoundData.layerNumber);
@@ -653,6 +671,35 @@ static void UpdateCompound( track_p trk, int inx, descData_p descUpd, BOOL_T nee
default:
break;
}
+ switch ( inx ) {
+ case A0:
+ case A1:
+ case A2:
+ case A3:
+ case E0:
+ case E1:
+ case E2:
+ case E3:
+ case AN:
+ case OR:
+ for (int i=0;i<compoundData.epCnt;i++) {
+ compoundData.endPt[i] = GetTrkEndPos(trk,i);
+ compoundDesc[i*(E1-E0)+E0].mode |= DESC_CHANGE;
+ trackParams_t params;
+ compoundData.endAngle[i] = GetTrkEndAngle(trk,i);
+ compoundDesc[i*(E1-E0)+A0].mode |= DESC_CHANGE;
+ GetTrackParams(PARAMS_CORNU,trk,compoundData.endPt[i],&params);
+ compoundData.endRadius[i] = params.arcR;
+ if (params.arcR != 0.0) {
+ compoundData.endCenter[i] = params.arcP;
+ compoundDesc[i*(E1-E0)+C0].mode |= DESC_CHANGE;
+ }
+ }
+ break;
+ default:
+ break;
+ };
+
DrawNewTrack( trk );
}
@@ -691,9 +738,11 @@ void DescribeCompound(
epCnt = GetTrkEndPtCnt(trk);
fix = 0;
+ mode = 0;
for ( ep=0; ep<epCnt; ep++ ) {
if (GetTrkEndTrk(trk,ep)) {
fix = 1;
+ mode = DESC_RO;
break;
}
}
@@ -735,10 +784,15 @@ void DescribeCompound(
compoundData.segCnt = xx->segCnt;
compoundData.length = 0;
compoundData.layerNumber = GetTrkLayer( trk );
- compoundDesc[E0].mode =
- compoundDesc[Z0].mode =
- compoundDesc[E1].mode =
- compoundDesc[Z1].mode =
+
+ for ( int i=0 ; i<4 ; i++) {
+ compoundDesc[E0+(E1-E0)*i].mode = DESC_IGNORE;
+ compoundDesc[A0+(E1-E0)*i].mode = DESC_IGNORE;
+ compoundDesc[R0+(E1-E0)*i].mode = DESC_IGNORE;
+ compoundDesc[C0+(E1-E0)*i].mode = DESC_IGNORE;
+ compoundDesc[Z0+(E1-E0)*i].mode = DESC_IGNORE;
+ }
+
compoundDesc[GR].mode = DESC_IGNORE;
compoundDesc[OR].mode =
compoundDesc[AN].mode = fix?DESC_RO:0;
@@ -746,37 +800,37 @@ void DescribeCompound(
compoundDesc[NM].mode =
compoundDesc[PN].mode = 0 /*DESC_NOREDRAW*/;
compoundDesc[EC].mode =
- compoundDesc[SC].mode =
+ compoundDesc[SC].mode = DESC_RO;
compoundDesc[LY].mode = DESC_NOREDRAW;
- if ( compoundData.epCnt ) {
- if ( compoundData.epCnt <=2 ) {
- if ( GetTrkEndTrk(trk,0) || (compoundData.epCnt==2 && GetTrkEndTrk(trk,1)) )
- mode = DESC_RO;
- else
- mode = 0;
- compoundDesc[OR].mode = DESC_IGNORE;
- compoundDesc[AN].mode = DESC_IGNORE;
- compoundDesc[EC].mode = DESC_IGNORE;
- compoundData.endPt[0] = GetTrkEndPos(trk,0);
- ComputeElev( trk, 0, FALSE, &compoundData.elev[0], NULL );
- compoundDesc[E0].mode = (int)mode;
- compoundDesc[Z0].mode = (EndPtIsDefinedElev(trk,0)?0:DESC_RO)|DESC_NOREDRAW;
- if ( compoundData.epCnt == 2 ) {
- compoundData.length = GetTrkLength( trk, 0, 1 );
- compoundData.endPt[1] = GetTrkEndPos(trk,1);
- ComputeElev( trk, 1, FALSE, &compoundData.elev[1], NULL );
- compoundDesc[E1].mode = (int)mode;
- compoundDesc[Z1].mode = (EndPtIsDefinedElev(trk,1)?0:DESC_RO)|DESC_NOREDRAW;
- compoundDesc[GR].mode = DESC_RO;
- if ( compoundData.length > minLength )
- compoundData.grade = fabs( (compoundData.elev[0]-compoundData.elev[1])/compoundData.length )*100.0;
- else
- compoundData.grade = 0.0;
+ if (compoundData.epCnt >0) {
+ for (int i=0;i<compoundData.epCnt;i++) {
+ compoundDesc[A0+(E1-E0)*i].mode = (int)mode;
+ compoundDesc[R0+(E1-E0)*i].mode = DESC_RO;
+ compoundDesc[C0+(E1-E0)*i].mode = DESC_RO;
+ compoundDesc[E0+(E1-E0)*i].mode = (int)mode;
+ compoundData.endPt[i] = GetTrkEndPos(trk,i);
+ compoundData.endAngle[i] = GetTrkEndAngle(trk,i);
+ trackParams_t params;
+ GetTrackParams(PARAMS_CORNU,trk,compoundData.endPt[i],&params);
+ compoundData.endRadius[i] = params.arcR;
+ if (params.arcR != 0.0) {
+ compoundData.endCenter[i] = params.arcP;
+ } else {
+ compoundDesc[C0+(E1-E0)*i].mode = DESC_IGNORE;
+ compoundDesc[R0+(E1-E0)*i].mode = DESC_IGNORE;
}
+ ComputeElev( trk, i, FALSE, &compoundData.elev[i], NULL );
+ compoundDesc[Z0+(E1-E0)*i].mode = (EndPtIsDefinedElev(trk,i)?0:DESC_RO)|DESC_NOREDRAW;
}
+ compoundDesc[GR].mode = DESC_RO;
+ }
+ if ( compoundData.length > minLength && compoundData.epCnt > 1)
+ compoundData.grade = fabs( (compoundData.elev[0]-compoundData.elev[1])/compoundData.length )*100.0;
+ else
+ compoundData.grade = 0.0;
+ if ( compoundData.epCnt >1 ) {
DoDescribe( compoundData.epCnt>2?_("Turnout"):_("Sectional Track"), trk, compoundDesc, UpdateCompound );
} else {
- compoundDesc[EC].mode |= DESC_IGNORE;
DoDescribe( _("Structure"), trk, compoundDesc, UpdateCompound );
}
}
@@ -785,6 +839,9 @@ void DescribeCompound(
void DeleteCompound(
track_p t )
{
+ struct extraData *xx = GetTrkExtraData(t);
+ FreeFilledDraw( xx->segCnt, xx->segs );
+ MyFree( xx->segs );
}
@@ -894,6 +951,8 @@ EXPORT track_p NewCompound(
xx->pathCurr = xx->paths;
xx->segCnt = segCnt;
xx->segs = memdup( segs, segCnt * sizeof *segs );
+ trkSeg_p p = xx->segs;
+ FixUpBezierSegs(xx->segs,xx->segCnt);
ComputeCompoundBoundingBox( trk );
SetDescriptionOrig( trk );
for ( ep=0; ep<epCnt; ep++ )
diff --git a/app/bin/compound.h b/app/bin/compound.h
index a0de926..4845f78 100644
--- a/app/bin/compound.h
+++ b/app/bin/compound.h
@@ -1,4 +1,6 @@
-/* $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/compound.h,v 1.1 2005-12-07 15:47:08 rc-flyer Exp $ */
+/** \file compound.h
+ * Definitions and function prototypes for complex elements (eg. turnouts)
+ */
/* XTrkCad - Model Railroad CAD
* Copyright (C) 2005 Dave Bullis
@@ -21,6 +23,9 @@
#ifndef COMPOUND_H
#define COMPOUND_H
+#include "common.h"
+#include "track.h"
+
typedef enum { TOnormal, TOadjustable, TOpierInfo, TOpier, TOcarDesc, TOlast } TOspecial_e;
typedef struct {
@@ -91,6 +96,9 @@ struct extraData {
extern TRKTYP_T T_TURNOUT;
extern TRKTYP_T T_STRUCTURE;
+extern TRKTYP_T T_BEZIER;
+extern TRKTYP_T T_BZRLIN;
+extern TRKTYP_T T_CORNU;
extern DIST_T curBarScale;
extern dynArr_t turnoutInfo_da;
extern dynArr_t structureInfo_da;
diff --git a/app/bin/cparalle.c b/app/bin/cparalle.c
index 28e3513..8e70408 100644
--- a/app/bin/cparalle.c
+++ b/app/bin/cparalle.c
@@ -1,8 +1,5 @@
-/*
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/cparalle.c,v 1.5 2009-05-25 18:11:03 m_fischer Exp $
- *
+/** \file cparalle.c
* PARALLEL
- *
*/
/* XTrkCad - Model Railroad CAD
@@ -23,10 +20,16 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-#include "track.h"
+#include <math.h>
+
#include "ccurve.h"
#include "cstraigh.h"
+#include "cundo.h"
#include "i18n.h"
+#include "messages.h"
+#include "param.h"
+#include "track.h"
+#include "utility.h"
static struct {
track_p Trk;
@@ -90,6 +93,7 @@ static STATUS_T CmdParallel( wAction_t action, coOrd pos )
}
if ( !QueryTrack( Dpa.Trk, Q_CAN_PARALLEL ) ) {
Dpa.Trk = NULL;
+ InfoMessage(_(" Track doesn't support parallel"));
return C_CONTINUE;
}
/* in case query has changed things (eg joint) */
@@ -101,6 +105,7 @@ static STATUS_T CmdParallel( wAction_t action, coOrd pos )
tempSegs_da.cnt = 0;
case C_MOVE:
+
if (Dpa.Trk == NULL) return C_CONTINUE;
DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack );
if ( !MakeParallelTrack( Dpa.Trk, pos, parSeparation, NULL, &p0, &p1 ) ) {
diff --git a/app/bin/cprint.c b/app/bin/cprint.c
index d89d1e2..88a9151 100644
--- a/app/bin/cprint.c
+++ b/app/bin/cprint.c
@@ -1,7 +1,5 @@
/** \file cprint.c
* Printing functions.
- *
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/cprint.c,v 1.6 2009-08-16 13:26:41 m_fischer Exp $
*/
/* XTrkCad - Model Railroad CAD
@@ -26,9 +24,16 @@
#include <time.h>
#include <string.h>
#include <ctype.h>
-#include "track.h"
-#include "i18n.h"
+#include <math.h>
+#include "custom.h"
+#include "fileio.h"
+#include "i18n.h"
+#include "layout.h"
+#include "messages.h"
+#include "param.h"
+#include "track.h"
+#include "utility.h"
#define PRINT_GAUDY (0)
#define PRINT_PLAIN (1)
@@ -73,6 +78,7 @@ static long printRoadbed = 0;
static DIST_T printRoadbedWidth = 0.0;
static BOOL_T printRotate = FALSE;
static BOOL_T rotateCW = FALSE;
+static long printCenterLine = 0;
static double printScale = 16;
static long iPrintScale = 16;
@@ -101,6 +107,7 @@ static char * printPhysSizeLabels[] = { N_("Ignore Page Margins"), NULL };
static char * printGridLabels[] = { N_("Print Snap Grid"), NULL };
static char * printRulerLabels[] = { N_("Print Rulers"), NULL };
static char * printRoadbedLabels[] = { N_("Print Roadbed Outline"), NULL };
+static char * printCenterLineLabels[] = { N_("Print Centerline below Scale 1:1"), NULL };
static paramIntegerRange_t rminScale_999 = { 1, 999, 0, PDO_NORANGECHECK_HIGH };
static paramFloatRange_t r0_ = { 0, 0, 0, PDO_NORANGECHECK_HIGH };
static paramFloatRange_t r1_ = { 1, 0, 0, PDO_NORANGECHECK_HIGH };
@@ -124,19 +131,22 @@ static paramData_t printPLs[] = {
/*10*/ { PD_TOGGLE, &printGrid, "grid", PDO_DLGNOLABELALIGN, printGridLabels, NULL, BC_HORZ|BC_NOBORDER },
#define I_RULER (11)
/*11*/ { PD_TOGGLE, &printRuler, "ruler", PDO_DLGNOLABELALIGN, printRulerLabels, NULL, BC_HORZ|BC_NOBORDER },
-#define I_ROADBED (12)
-/*12*/{ PD_TOGGLE, &printRoadbed, "roadbed", PDO_DLGNOLABELALIGN, printRoadbedLabels, NULL, BC_HORZ|BC_NOBORDER },
-#define I_ROADBEDWIDTH (13)
-/*13*/{ PD_FLOAT, &printRoadbedWidth, "roadbedWidth", PDO_DIM|PDO_DLGBOXEND, &r0_, N_("Width") },
-/*14*/{ PD_FLOAT, &newPrintGrid.orig.x, "origx", PDO_DIM|PDO_DLGRESETMARGIN, &r_10_99999, N_("Origin: X"), 0, (void*)2 },
-/*15*/ { PD_FLOAT, &newPrintGrid.orig.y, "origy", PDO_DIM, &r_10_99999, N_("Y"), 0, (void*)2 },
-/*16*/ { PD_BUTTON, (void*)DoResetGrid, "reset", PDO_DLGHORZ, NULL, N_("Reset") },
-/*17*/ { PD_FLOAT, &newPrintGrid.angle, "origa", PDO_ANGLE|PDO_DLGBOXEND, &r0_360, N_("Angle"), 0, (void*)2 },
-/*18*/ { PD_BUTTON, (void*)DoPrintSetup, "setup", PDO_DLGCMDBUTTON, NULL, N_("Setup") },
-/*19*/ { PD_BUTTON, (void*)PrintClear, "clear", 0, NULL, N_("Clear") },
-#define I_PAGECNT (20)
-/*20*/ { PD_MESSAGE, N_("0 pages"), NULL, 0, (void*)80 },
-/*21*/ { PD_MESSAGE, N_("selected"), NULL, 0, (void*)80 } };
+#define I_CENTERLINE (12)
+/*12*/ { PD_TOGGLE, &printCenterLine, "centerLine", PDO_DLGNOLABELALIGN, printCenterLineLabels, NULL, BC_HORZ|BC_NOBORDER },
+#define I_ROADBED (13)
+/*13*/{ PD_TOGGLE, &printRoadbed, "roadbed", PDO_DLGNOLABELALIGN, printRoadbedLabels, NULL, BC_HORZ|BC_NOBORDER },
+#define I_ROADBEDWIDTH (14)
+/*14*/{ PD_FLOAT, &printRoadbedWidth, "roadbedWidth", PDO_DIM|PDO_DLGBOXEND, &r0_, N_("Width") },
+/*15*/{ PD_FLOAT, &newPrintGrid.orig.x, "origx", PDO_DIM|PDO_DLGRESETMARGIN, &r_10_99999, N_("Origin: X"), 0, (void*)2 },
+/*16*/ { PD_FLOAT, &newPrintGrid.orig.y, "origy", PDO_DIM, &r_10_99999, N_("Y"), 0, (void*)2 },
+/*17*/ { PD_BUTTON, (void*)DoResetGrid, "reset", PDO_DLGHORZ, NULL, N_("Reset") },
+/*18*/ { PD_FLOAT, &newPrintGrid.angle, "origa", PDO_ANGLE|PDO_DLGBOXEND, &r0_360, N_("Angle"), 0, (void*)2 },
+/*19*/ { PD_BUTTON, (void*)DoPrintSetup, "setup", PDO_DLGCMDBUTTON, NULL, N_("Setup") },
+/*20*/ { PD_BUTTON, (void*)PrintClear, "clear", 0, NULL, N_("Clear") },
+#define I_PAGECNT (21)
+/*21*/ { PD_MESSAGE, N_("0 pages"), NULL, 0, (void*)80 },
+/*22*/ { PD_MESSAGE, N_("selected"), NULL, 0, (void*)80 }
+};
static paramGroup_t printPG = { "print", PGO_PREFMISCGROUP, printPLs, sizeof printPLs/sizeof printPLs[0] };
@@ -158,33 +168,6 @@ static void ChangeDim( void )
MapGrid( zero, mapD.size, 0.0, currPrintGrid.orig, currPrintGrid.angle, currPrintGrid.size.x, currPrintGrid.size.y,
&x0, &x1, &y0, &y1 );
-#ifdef LATER
- d0 = sqrt( mapD.size.x * mapD.size.x + mapD.size.y * mapD.size.y );
-
- Translate( &p1, currPrintGrid.orig, currPrintGrid.angle, d0 );
- p0 = currPrintGrid.orig;
- ClipLine( &p0, &p1, zero, 0.0, mapD.size );
- d1 = FindDistance( currPrintGrid.orig, p1 );
- y1 = (int)ceil(d1/currPrintGrid.size.y);
-
- Translate( &p1, currPrintGrid.orig, currPrintGrid.angle+180, d0 );
- p0 = currPrintGrid.orig;
- ClipLine( &p0, &p1, zero, 0.0, mapD.size );
- d1 = FindDistance( currPrintGrid.orig, p1 );
- y0 = -(int)floor(d1/currPrintGrid.size.y);
-
- Translate( &p1, currPrintGrid.orig, currPrintGrid.angle+90, d0 );
- p0 = currPrintGrid.orig;
- ClipLine( &p0, &p1, zero, 0.0, mapD.size );
- d1 = FindDistance( currPrintGrid.orig, p1 );
- x1 = (int)ceil(d1/currPrintGrid.size.x);
-
- Translate( &p1, currPrintGrid.orig, currPrintGrid.angle+270, d0 );
- p0 = currPrintGrid.orig;
- ClipLine( &p0, &p1, zero, 0.0, mapD.size );
- d1 = FindDistance( currPrintGrid.orig, p1 );
- x0 = -(int)floor(d1/currPrintGrid.size.x);
-#endif
if ( x0==bm.x0 && x1==bm.x1 && y0==bm.y0 && y1==bm.y1 )
return;
@@ -385,20 +368,20 @@ static void PrintGaudyBox(
DrawString( &page_d, p00, 0.0, dat, fp, 16.0, wDrawColorBlack );
p00.y = 0.5+0.05;
- DrawTextSize( &mainD, Title1, fp, 16.0, FALSE, &textsize );
+ DrawTextSize( &mainD, GetLayoutTitle(), fp, 16.0, FALSE, &textsize );
p00.x = (pageW/2.0)-(textsize.x/2.0);
p00.y = 0.75+0.05;
- DrawString( &page_d, p00, 0.0, Title1, fp, 16.0, wDrawColorBlack );
- DrawTextSize( &mainD, Title2, fp, 16.0, FALSE, &textsize );
+ DrawString( &page_d, p00, 0.0, GetLayoutTitle(), fp, 16.0, wDrawColorBlack );
+ DrawTextSize( &mainD, GetLayoutSubtitle(), fp, 16.0, FALSE, &textsize );
p00.x = (pageW/2.0)-(textsize.x/2.0);
p00.y = 0.50+0.05;
- DrawString( &page_d, p00, 0.0, Title2, fp, 16.0, wDrawColorBlack );
+ DrawString( &page_d, p00, 0.0, GetLayoutSubtitle(), fp, 16.0, wDrawColorBlack );
sprintf( dat, _("PrintScale 1:%ld Room %s x %s Model Scale %s File %s"),
(long)printScale,
FormatDistance( roomSize.x ),
FormatDistance( roomSize.y ),
- curScaleName, curFileName );
+ curScaleName, GetLayoutFilename() );
p00.x = 0.05; p00.y = 0.25+0.05;
DrawString( &page_d, p00, 0.0, dat, fp, 16.0, wDrawColorBlack );
}
@@ -480,30 +463,17 @@ static void PrintEnableControls( void )
ParamLoadControl( &printPG, I_ROADBED );
ParamControlActive( &printPG, I_ROADBED, TRUE );
ParamControlActive( &printPG, I_ROADBEDWIDTH, TRUE );
+ ParamControlActive( &printPG, I_CENTERLINE, TRUE);
} else {
printRoadbed = 0;
ParamLoadControl( &printPG, I_ROADBED );
ParamControlActive( &printPG, I_ROADBED, FALSE );
ParamControlActive( &printPG, I_ROADBEDWIDTH, FALSE );
+ ParamControlActive( &printPG, I_CENTERLINE, FALSE );
}
}
-#ifdef LATER
-static void PrintSetOrient( void )
-/*
- * Called when print landscape/portrait toggled
- */
-{
- DrawPrintGrid();
- ParamLoadData( &printPG );
- currPrintGrid = newPrintGrid;
- ChangeDim();
- DrawPrintGrid();
-}
-#endif
-
-
static void PrintUpdate( int inx0 )
/*
* Called when print page size (x or y) is changed.
@@ -918,6 +888,7 @@ static BOOL_T PrintPage(
if (printGrid)
DrawSnapGrid( &print_d, mapD.size, FALSE );
roadbedWidth = printRoadbed?printRoadbedWidth:0.0;
+ printCenterLines = printCenterLine;
DrawTracks( &print_d, print_d.scale, minP, maxP );
if (printRegistrationMarks && printScale == 1)
DrawRegistrationMarks( &print_d );
@@ -950,7 +921,7 @@ static void DoPrintPrint( void * junk )
print_d.CoOrd2Pix = page_d.CoOrd2Pix = mainD.CoOrd2Pix;
wSetCursor( wCursorWait );
- if (!wPrintDocStart( Title1, pageCount, &copies )) {
+ if (!wPrintDocStart(GetLayoutTitle(), pageCount, &copies )) {
wSetCursor( wCursorNormal );
return;
}
diff --git a/app/bin/cprofile.c b/app/bin/cprofile.c
index d8bbc24..49c3289 100644
--- a/app/bin/cprofile.c
+++ b/app/bin/cprofile.c
@@ -1,5 +1,5 @@
-/*
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/cprofile.c,v 1.4 2008-03-06 19:35:06 m_fischer Exp $
+/* \file cprofile.c
+ * Track profile
*/
/* XTrkCad - Model Railroad CAD
@@ -20,11 +20,17 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-#include "track.h"
-#include "cselect.h"
#include <math.h>
-#include "shrtpath.h"
+
+#include "custom.h"
+#include "cselect.h"
+#include "cundo.h"
#include "i18n.h"
+#include "layout.h"
+#include "messages.h"
+#include "param.h"
+#include "shrtpath.h"
+#include "track.h"
/*
@@ -484,7 +490,7 @@ static void DoProfilePrint( void * junk )
screenRatio = screenSize.y/screenSize.x;
printProfileD.size.x = w;
printProfileD.size.y = h;
- sprintf( message, _("%s Profile: %s"), sProdName, Title1 );
+ sprintf( message, _("%s Profile: %s"), sProdName, GetLayoutTitle() );
fp = wStandardFont( F_TIMES, FALSE, FALSE );
DrawTextSize( &mainD, message, fp, 24, FALSE, &textsize );
titleH = textsize.y + 6.0/mainD.dpi;
diff --git a/app/bin/cpull.c b/app/bin/cpull.c
index a10f426..d7f7c80 100644
--- a/app/bin/cpull.c
+++ b/app/bin/cpull.c
@@ -1,8 +1,5 @@
-/*
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/cpull.c,v 1.4 2008-03-06 19:35:06 m_fischer Exp $
- *
+/** \file cpull.c
* Pull and Tighten commands
- *
*/
/* XTrkCad - Model Railroad CAD
@@ -24,14 +21,15 @@
*/
#include <math.h>
-#include "track.h"
+
#include "cselect.h"
#include "compound.h"
+#include "cundo.h"
+#include "fileio.h"
#include "i18n.h"
-
-/*
- * pull track endpoint together
- */
+#include "messages.h"
+#include "track.h"
+#include "utility.h"
int debugPull = 0;
@@ -454,12 +452,20 @@ static void PullTracks(
int cnt1, cnt2;
int rc;
+ if (QueryTrack(trk1,Q_CAN_ADD_ENDPOINTS) || QueryTrack(trk2,Q_CAN_ADD_ENDPOINTS)) {
+ ConnectTurntableTracks(trk1, ep1, trk2, ep2 );
+ return;
+ }
+
+ if (ep1<0 || ep1<0 ) return;
+
if (ConnectAbuttingTracks( trk1, ep1, trk2, ep2 ))
return;
if (ConnectAdjustableTracks( trk1, ep1, trk2, ep2 ))
return;
+
p1 = GetTrkEndPos( trk1, ep1 );
p2 = GetTrkEndPos( trk2, ep2 );
a1 = GetTrkEndAngle( trk1, ep1 );
@@ -593,12 +599,22 @@ static STATUS_T CmdPull(
static EPINX_T ep1;
track_p trk2;
EPINX_T ep2;
+ static BOOL_T turntable;
+
+ int countTracksR0 = 0, countTracksR1 = 0, possibleEndPoints = 0;
+ BOOL_T found = FALSE;
+ ANGLE_T a;
+ DIST_T d;
switch (action) {
case C_START:
- InfoMessage( _("Select first End-Point to connect") );
+ if (selectedTrackCount==0)
+ InfoMessage( _("Select first end-point to connect") );
+ else
+ InfoMessage( _("Select first end-point to connect, or Right-Click for connecting selected tracks") );
trk1 = NULL;
+ turntable = FALSE;
return C_CONTINUE;
case C_LCLICK:
@@ -606,10 +622,14 @@ static STATUS_T CmdPull(
if (trk1 == NULL) {
if ((trk1 = OnTrack( &pos, TRUE, FALSE )) != NULL) {
if ((ep1 = PickUnconnectedEndPoint( pos, trk1 )) < 0) {
- trk1 = NULL;
+ if (QueryTrack(trk1, Q_CAN_ADD_ENDPOINTS)) {
+ turntable = TRUE;
+ ep1 = -1;
+ } else trk1 = NULL;
} else {
- InfoMessage( _("Select second End-Point to connect") );
+ InfoMessage( _("Select second end-point to connect") );
}
+
}
} else {
if ((trk2 = OnTrack( &pos, TRUE, FALSE )) != NULL) {
@@ -619,6 +639,15 @@ static STATUS_T CmdPull(
inError = TRUE;
return C_TERMINATE;
}
+ if (!turntable && QueryTrack(trk2, Q_CAN_ADD_ENDPOINTS)) {
+ ep2 = -1;
+ turntable = TRUE;
+ PullTracks( trk2, ep2, trk1, ep1);
+ trk1 = NULL;
+ inError = TRUE;
+ turntable = FALSE;
+ return C_TERMINATE;
+ }
}
}
} else {
@@ -635,6 +664,53 @@ static STATUS_T CmdPull(
}
return C_CONTINUE;
+ case C_RCLICK:
+ if (selectedTrackCount==0) {
+ ErrorMessage(_("Connect Multiple Tracks - Select multiple tracks to join first"));
+ return C_CONTINUE;
+ }
+ if (NoticeMessage(_("Try to Connect all Selected Tracks?"), _("Yes"), _("No"))<=0) return C_CONTINUE;
+ trk1 = NULL;
+ trk2 = NULL;
+ UndoStart( _("ReConnect"),"Try to reconnect all selected tracks");
+ for (int i=0;i<2;i++) { // Try twice - in case later joins help earlier ones and to try close ones first
+ while ( TrackIterate( &trk1 ) ) {
+ found = FALSE;
+ if ( GetTrkSelected( trk1 ) ) {
+ for (ep1=0; ep1<GetTrkEndPtCnt(trk1); ep1++) {
+ if (!GetTrkEndTrk( trk1, ep1 )) {
+ trk2 = NULL;
+ while (!found && TrackIterate(&trk2) ) {
+ if (trk1 == trk2) continue;
+ for (ep2=0; ep2<GetTrkEndPtCnt(trk2); ep2++) {
+ if (GetTrkEndTrk( trk2, ep2 )) continue;
+ d = FindDistance(GetTrkEndPos(trk1,ep1),GetTrkEndPos(trk2,ep2));
+ a = NormalizeAngle( 180+GetTrkEndAngle( trk1, ep1 ) - GetTrkEndAngle( trk2, ep2 )+(connectAngle/2.0));
+ // Take two passes. In round one favor closer connections. In round two try anything.
+ if ( (i==0 && (d < connectDistance) && (a < connectAngle)) ||
+ (i>0 && (d<3.0 && a<7.5))) { // Match PullTracks criteria in round 2
+ PullTracks(trk1,ep1,trk2,ep2);
+ if (GetTrkEndTrk( trk2, ep2 )) {
+ found = TRUE;
+ if (i==0)
+ countTracksR0++;
+ else
+ countTracksR1++;
+ break; //Stop looking
+ } else if (i==1) possibleEndPoints++;
+ }
+ }
+ }
+ if (found) break; //Next EndPoint
+ }
+ }
+ }
+ }
+ }
+ UndoEnd();
+ NoticeMessage(_("Round 1 %d and Round 2 %d tracks connected, %d close pairs of end Points were not connected"), _("Ok"), NULL, countTracksR0, countTracksR1, possibleEndPoints);
+ return C_TERMINATE;
+
case C_REDRAW:
return C_CONTINUE;
@@ -658,5 +734,5 @@ static STATUS_T CmdPull(
void InitCmdPull( wMenu_p menu )
{
- AddMenuButton( menu, CmdPull, "cmdConnect", _("Connect Sectional Tracks"), wIconCreatePixMap(pull_xpm), LEVEL0_50, IC_STICKY|IC_LCLICK|IC_POPUP2, ACCL_CONNECT, NULL );
+ AddMenuButton( menu, CmdPull, "cmdConnect", _("Connect Two Tracks"), wIconCreatePixMap(pull_xpm), LEVEL0_50, IC_STICKY|IC_LCLICK|IC_POPUP2|IC_RCLICK, ACCL_CONNECT, NULL );
}
diff --git a/app/bin/cruler.c b/app/bin/cruler.c
index 6566e93..b1addc6 100644
--- a/app/bin/cruler.c
+++ b/app/bin/cruler.c
@@ -20,8 +20,13 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-#include "track.h"
+#include "cundo.h"
+#include "fileio.h"
#include "i18n.h"
+#include "param.h"
+#include "track.h"
+#include "utility.h"
+
/*****************************************************************************
*
diff --git a/app/bin/cselect.c b/app/bin/cselect.c
index 1bafd45..861f03f 100644
--- a/app/bin/cselect.c
+++ b/app/bin/cselect.c
@@ -1,8 +1,5 @@
/** \file cselect.c
* Handle selecting / unselecting track and basic operations on the selection
- *
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/cselect.c,v 1.11 2008-09-05 08:08:15 m_fischer Exp $
- *
*/
/* XTrkCad - Model Railroad CAD
@@ -23,19 +20,30 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-#include "track.h"
-/*#include "trackx.h"*/
+#include <math.h>
+#include <string.h>
+
#include "ccurve.h"
+#include "tcornu.h"
+#include "tbezier.h"
#define PRIVATE_EXTRADATA
#include "compound.h"
+#include "cselect.h"
+#include "cundo.h"
+#include "custom.h"
+#include "fileio.h"
+#include "i18n.h"
+#include "layout.h"
+#include "messages.h"
+#include "param.h"
+#include "track.h"
+#include "utility.h"
#include "bitmaps/bmendpt.xbm"
#include "bitmaps/bma0.xbm"
#include "bitmaps/bma45.xbm"
#include "bitmaps/bma90.xbm"
#include "bitmaps/bma135.xbm"
-#include "i18n.h"
-
#define SETMOVEMODE "MOVEMODE"
@@ -55,8 +63,10 @@ static wDrawBitMap_p angle_bm[4];
long quickMove = 0;
BOOL_T importMove = 0;
int incrementalDrawLimit = 20;
+ static int microCount = 0;
static dynArr_t tlist_da;
+
#define Tlist(N) DYNARR_N( track_p, tlist_da, N )
#define TlistAppend( T ) \
{ DYNARR_APPEND( track_p, tlist_da, 10 );\
@@ -143,6 +153,7 @@ EXPORT void SetAllTrackSelect( BOOL_T select )
SelectedTrackCountChange();
if (doRedraw) {
MainRedraw();
+ MapRedraw();
} else {
wDrawDelayUpdate( mainD.d, FALSE );
}
@@ -174,6 +185,7 @@ EXPORT void InvertTrackSelect( void *ptr )
SelectedTrackCountChange();
MainRedraw();
+ MapRedraw();
}
/* Select orphaned (ie single) track pieces.
@@ -207,6 +219,7 @@ EXPORT void OrphanedTrackSelect( void *ptr )
}
SelectedTrackCountChange();
MainRedraw();
+ MapRedraw();
}
@@ -389,7 +402,7 @@ EXPORT void SelectTunnel( void )
}
-EXPORT void SelectRecount( void )
+void SelectRecount( void )
{
track_p trk;
selectedTrackCount = 0;
@@ -518,6 +531,7 @@ EXPORT void DoRefreshCompound( void )
RefreshCompound( NULL, FALSE );
UndoEnd();
MainRedraw();
+ MapRedraw();
} else {
ErrorMessage( MSG_NO_SELECTED_TRK );
}
@@ -766,9 +780,9 @@ EXPORT void DoRescale( void )
if ( rescalePG.win == NULL ) {
ParamCreateDialog( &rescalePG, MakeWindowTitle(_("Rescale")), _("Ok"), RescaleDlgOk, wHide, TRUE, NULL, F_BLOCK, RescaleDlgUpdate );
LoadScaleList( (wList_p)rescalePLs[I_RESCALE_TO_SCALE].control );
- LoadGaugeList( (wList_p)rescalePLs[I_RESCALE_TO_GAUGE].control, curScaleDescInx ); /* set correct gauge list here */
- rescaleFromScaleInx = curScaleInx;
- rescaleToScaleInx = curScaleInx;
+ LoadGaugeList( (wList_p)rescalePLs[I_RESCALE_TO_GAUGE].control, GetLayoutCurScaleDesc() ); /* set correct gauge list here */
+ rescaleFromScaleInx = GetLayoutCurScale();
+ rescaleToScaleInx = rescaleFromScaleInx;
rescalePercent = 100.0;
}
@@ -1032,6 +1046,10 @@ static void MoveTracks(
track_p trk, trk1;
EPINX_T ep, ep1;
int inx;
+ trackParams_t trackParms;
+ ANGLE_T endAngle;
+ DIST_T endRadius;
+ coOrd endCenter;
wSetCursor( wCursorWait );
/*UndoStart( "Move/Rotate Tracks", "move/rotate" );*/
@@ -1050,12 +1068,57 @@ static void MoveTracks(
RotateTrack( trk, orig, angle );
for (ep=0; ep<GetTrkEndPtCnt(trk); ep++) {
if ((trk1 = GetTrkEndTrk(trk,ep)) != NULL &&
- !GetTrkSelected(trk1)) {
+ !GetTrkSelected(trk1)) {
ep1 = GetEndPtConnectedToMe( trk1, trk );
DisconnectTracks( trk, ep, trk1, ep1 );
+ if (QueryTrack(trk1,Q_IS_CORNU)) { //Cornu at end stays connected
+ GetTrackParams(PARAMS_CORNU,trk,GetTrkEndPos(trk,ep),&trackParms);
+ if (trackParms.type == curveTypeStraight) {
+ endRadius = 0;
+ endCenter = zero;
+ } else {
+ endRadius = trackParms.arcR;
+ endCenter = trackParms.arcP;
+ }
+ DrawTrack(trk1,&mainD,wDrawColorWhite);
+ DrawTrack(trk1,&mapD,wDrawColorWhite);
+ endAngle = NormalizeAngle(GetTrkEndAngle(trk,ep)+180);
+ if (SetCornuEndPt(trk1,ep1,GetTrkEndPos(trk,ep),endCenter,endAngle,endRadius)) {
+ ConnectTracks(trk,ep,trk1,ep1);
+ DrawTrack(trk1,&mainD,wDrawColorBlack);
+ DrawTrack(trk1,&mapD,wDrawColorBlack);
+ } else {
+ DeleteTrack(trk1,TRUE);
+ ErrorMessage(_("Cornu too tight - it was deleted"));
+ }
+ } else {
+ if (QueryTrack(trk,Q_IS_CORNU)) { //I am a Cornu myself!
+ GetTrackParams(PARAMS_CORNU,trk1,GetTrkEndPos(trk1,ep1),&trackParms);
+ if (trackParms.type == curveTypeStraight) {
+ endRadius = 0;
+ endCenter = zero;
+ } else {
+ endRadius = trackParms.arcR;
+ endCenter = trackParms.arcP;
+ }
+ DrawTrack(trk,&mainD,wDrawColorWhite);
+ DrawTrack(trk1,&mapD,wDrawColorWhite);
+ endAngle = NormalizeAngle(GetTrkEndAngle(trk1,ep1)+180);
+ if (SetCornuEndPt(trk,ep,GetTrkEndPos(trk1,ep1),endCenter,endAngle,endRadius)) {
+ ConnectTracks(trk,ep,trk1,ep1);
+ DrawTrack(trk,&mainD,wDrawColorBlack);
+ DrawTrack(trk,&mapD,wDrawColorBlack);
+ } else {
+ ErrorMessage(_("Cornu selected too tight after move - it was left alone"));
+ DrawTrack(trk,&mainD,wDrawColorBlack);
+ DrawTrack(trk,&mapD,wDrawColorBlack);
+ }
+ }
+ }
DrawEndPt( &mainD, trk1, ep1, wDrawColorBlack );
}
}
+
InfoCount( inx );
#ifdef LATER
if (tlist_da.cnt <= incrementalDrawLimit)
@@ -1103,6 +1166,16 @@ void MoveToJoin(
DrawNewTrack( trk0 );
DrawNewTrack( trk1 );
}
+
+void FreeTempStrings() {
+ for (int i = 0; i<tempSegs_da.cnt; i++) {
+ if (tempSegs(i).type == SEG_TEXT) {
+ if (tempSegs(i).u.t.string)
+ MyFree(tempSegs(i).u.t.string);
+ tempSegs(i).u.t.string = NULL;
+ }
+ }
+}
static STATUS_T CmdMove(
wAction_t action,
@@ -1112,7 +1185,7 @@ static STATUS_T CmdMove(
static coOrd orig;
static int state;
- switch( action ) {
+ switch( action&0xFF) {
case C_START:
if (selectedTrackCount == 0) {
@@ -1122,7 +1195,7 @@ static STATUS_T CmdMove(
if (SelectedTracksAreFrozen()) {
return C_TERMINATE;
}
- InfoMessage( _("Drag to move selected tracks") );
+ InfoMessage( _("Drag to move selected tracks - Shift+Ctrl+Arrow micro-steps the move") );
state = 0;
break;
case C_DOWN:
@@ -1134,19 +1207,20 @@ static STATUS_T CmdMove(
orig = pos;
GetMovedTracks(quickMove != MOVE_QUICK);
SetMoveD( TRUE, base, 0.0 );
- DrawMovedTracks();
+ //DrawMovedTracks();
drawCount = 0;
state = 1;
MainRedraw();
+ MapRedraw();
return C_CONTINUE;
case C_MOVE:
drawEnable = enableMoveDraw;
- DrawMovedTracks();
+ //DrawMovedTracks();
base.x = pos.x - orig.x;
base.y = pos.y - orig.y;
SnapPos( &base );
SetMoveD( TRUE, base, 0.0 );
- DrawMovedTracks();
+ //DrawMovedTracks();
#ifdef DRAWCOUNT
InfoMessage( " [%s %s] #%ld", FormatDistance(base.x), FormatDistance(base.y), drawCount );
#else
@@ -1154,10 +1228,12 @@ static STATUS_T CmdMove(
#endif
drawEnable = TRUE;
MainRedraw();
+ MapRedraw();
return C_CONTINUE;
case C_UP:
state = 0;
- DrawMovedTracks();
+ //DrawMovedTracks();
+ FreeTempStrings();
MoveTracks( quickMove==MOVE_QUICK, TRUE, FALSE, base, zero, 0.0 );
return C_TERMINATE;
@@ -1173,6 +1249,47 @@ static STATUS_T CmdMove(
DrawMovedTracks();
break;
+ case wActionExtKey:
+ if (state) return C_CONTINUE;
+ if (SelectedTracksAreFrozen()) return C_TERMINATE;
+ if ((MyGetKeyState() &
+ (WKEY_SHIFT | WKEY_CTRL)) == (WKEY_SHIFT | WKEY_CTRL)) {
+ base = zero;
+ DIST_T w = tempD.scale/tempD.dpi;
+ switch((wAccelKey_e) action>>8) {
+ case wAccelKey_Up:
+ base.y = w;
+ break;
+ case wAccelKey_Down:
+ base.y = -w;
+ break;
+ case wAccelKey_Left:
+ base.x = -w;
+ break;
+ case wAccelKey_Right:
+ base.x = w;
+ break;
+ default:
+ return C_CONTINUE;
+ break;
+ }
+
+ drawEnable = enableMoveDraw;
+ GetMovedTracks(quickMove!=MOVE_QUICK);
+ UndoStart( _("Move Tracks"), "move" );
+ SetMoveD( TRUE, base, 0.0 );
+ DrawSelectedTracksD( &mainD, wDrawColorWhite );
+ MoveTracks( quickMove==MOVE_QUICK, TRUE, FALSE, base, zero, 0.0 );
+ ++microCount;
+ if (microCount>5) {
+ microCount = 0;
+ MainRedraw();
+ MapRedraw();
+ }
+ return C_CONTINUE;
+ }
+ break;
+
default:
break;
}
@@ -1214,7 +1331,7 @@ static STATUS_T CmdRotate(
if (SelectedTracksAreFrozen()) {
return C_TERMINATE;
}
- InfoMessage( _("Drag to rotate selected tracks") );
+ InfoMessage( _("Drag to rotate selected tracks, Shift+RightClick for QuickRotate Menu") );
wMenuPushEnable( rotateAlignMI, TRUE );
rotateAlignState = 0;
break;
@@ -1226,9 +1343,22 @@ static STATUS_T CmdRotate(
UndoStart( _("Rotate Tracks"), "rotate" );
if ( rotateAlignState == 0 ) {
drawnAngle = FALSE;
- angle = 0;
+ angle = 0.0;
base = orig = pos;
+ trk = OnTrack(&pos, FALSE, FALSE); //Note pollutes pos if turntable
+ if ((trk) &&
+ QueryTrack(trk,Q_CAN_ADD_ENDPOINTS)) { //Turntable snap to center if within 1/4 radius
+ trackParams_t trackParams;
+ if (GetTrackParams(PARAMS_CORNU, trk, pos, &trackParams)) {
+ DIST_T dist = FindDistance(base, trackParams.ttcenter);
+ if (dist < trackParams.ttradius/4) {
+ base = orig = trackParams.ttcenter;
+ InfoMessage( _("Center of Rotation snapped to Turntable center") );
+ }
+ }
+ }
GetMovedTracks(FALSE);
+ SetMoveD( FALSE, base, angle );
/*DrawLine( &mainD, base, orig, 0, wDrawColorBlack );
DrawMovedTracks(FALSE, orig, angle);*/
} else {
@@ -1256,20 +1386,21 @@ static STATUS_T CmdRotate(
angle = 0;
} else {
angle = NormalizeAngle(angle1-baseAngle);
- if ( angle > 90 && angle < 270 )
- angle = NormalizeAngle( angle + 180.0 );
- if ( NormalizeAngle( FindAngle( pos, pos1 ) - angle1 ) < 180.0 )
- angle = NormalizeAngle( angle + 180.0 );
+ //if ( angle > 90 && angle < 270 )
+ // angle = NormalizeAngle( angle + 180.0 );
+ //if ( NormalizeAngle( FindAngle( base, pos1 ) - angle1 ) < 180.0 )
+ // angle = NormalizeAngle( angle + 180.0 );
/*printf( "angle 1 = %0.3f\n", angle );*/
if ( angle1 > 180.0 ) angle1 -= 180.0;
InfoMessage( _("Angle %0.3f"), angle1 );
}
GetMovedTracks(TRUE);
SetMoveD( FALSE, orig, angle );
- DrawMovedTracks();
+ //DrawMovedTracks();
}
}
MainRedraw();
+ MapRedraw();
return C_CONTINUE;
case C_MOVE:
if ( rotateAlignState == 1 )
@@ -1285,7 +1416,7 @@ static STATUS_T CmdRotate(
ErrorMessage( MSG_2ND_TRACK_MUST_BE_UNSELECTED );
return C_CONTINUE;
}
- DrawMovedTracks();
+ //DrawMovedTracks();
angle1 = NormalizeAngle( GetAngleAtPoint( trk, pos, NULL, NULL ) );
angle = NormalizeAngle(angle1-baseAngle);
if ( angle > 90 && angle < 270 )
@@ -1296,8 +1427,9 @@ static STATUS_T CmdRotate(
InfoMessage( _("Angle %0.3f"), angle1 );
SetMoveD( FALSE, orig, angle );
/*printf( "angle 2 = %0.3f\n", angle );*/
- DrawMovedTracks();
+ //DrawMovedTracks();
MainRedraw();
+ MapRedraw();
return C_CONTINUE;
}
if ( FindDistance( orig, pos ) > (6.0/75.0)*mainD.scale ) {
@@ -1321,7 +1453,7 @@ static STATUS_T CmdRotate(
}
DrawLine( &tempD, base, orig, 0, wDrawColorBlack );
SetMoveD( FALSE, orig, angle );
- DrawMovedTracks();
+ //DrawMovedTracks();
#ifdef DRAWCOUNT
InfoMessage( _(" Angle %0.3f #%ld"), angle, drawCount );
#else
@@ -1331,6 +1463,7 @@ static STATUS_T CmdRotate(
drawEnable = TRUE;
}
MainRedraw();
+ MapRedraw();
return C_CONTINUE;
case C_UP:
state = 0;
@@ -1341,19 +1474,33 @@ static STATUS_T CmdRotate(
}
return C_CONTINUE;
}
+ FreeTempStrings();
if ( rotateAlignState == 2 ) {
- DrawMovedTracks();
+ //DrawMovedTracks();
MoveTracks( quickMove==MOVE_QUICK, FALSE, TRUE, zero, orig, angle );
rotateAlignState = 0;
} else if (drawnAngle) {
DrawLine( &tempD, base, orig, 0, wDrawColorBlack );
- DrawMovedTracks();
+ //DrawMovedTracks();
MoveTracks( quickMove==MOVE_QUICK, FALSE, TRUE, zero, orig, angle );
}
MainRedraw();
+ MapRedraw();
return C_TERMINATE;
case C_CMDMENU:
+ base = pos;
+ trk = OnTrack(&pos, FALSE, FALSE); //Note pollutes pos if turntable
+ if ((trk) &&
+ QueryTrack(trk,Q_CAN_ADD_ENDPOINTS)) { //Turntable snap to center if within 1/4 radius
+ trackParams_t trackParams;
+ if (GetTrackParams(PARAMS_CORNU, trk, pos, &trackParams)) {
+ DIST_T dist = FindDistance(base, trackParams.ttcenter);
+ if (dist < trackParams.ttradius/4) {
+ cmdMenuPos = trackParams.ttcenter;
+ }
+ }
+ }
wMenuPopupShow( selectPopup2M );
return C_CONTINUE;
@@ -1371,6 +1518,20 @@ static STATUS_T CmdRotate(
return C_CONTINUE;
}
+static void QuickMove( void* pos) {
+ coOrd move_pos = *(coOrd*)pos;
+ if ( SelectedTracksAreFrozen() )
+ return;
+ wDrawDelayUpdate( mainD.d, TRUE );
+ GetMovedTracks(FALSE);
+ DrawSelectedTracksD( &mainD, wDrawColorWhite );
+ UndoStart( _("Move Tracks"), "Move Tracks" );
+ MoveTracks( quickMove==MOVE_QUICK, TRUE, FALSE, move_pos, zero, 0.0 );
+ wDrawDelayUpdate( mainD.d, FALSE );
+ MainRedraw();
+ MapRedraw();
+}
+
static void QuickRotate( void* pangle )
{
ANGLE_T angle = (ANGLE_T)(long)pangle;
@@ -1382,6 +1543,8 @@ static void QuickRotate( void* pangle )
UndoStart( _("Rotate Tracks"), "Rotate Tracks" );
MoveTracks( quickMove==MOVE_QUICK, FALSE, TRUE, zero, cmdMenuPos, angle );
wDrawDelayUpdate( mainD.d, FALSE );
+ MainRedraw();
+ MapRedraw();
}
@@ -1460,11 +1623,26 @@ STATUS_T CmdMoveDescription(
ep = -1;
mode = 2;
}
+ d = CornuDescriptionDistance( pos, trk1 );
+ if ( d < dd ) {
+ dd = d;
+ trk = trk1;
+ ep = -1;
+ mode = 3;
+ }
+ d = BezierDescriptionDistance( pos, trk1 );
+ if ( d < dd ) {
+ dd = d;
+ trk = trk1;
+ ep = -1;
+ mode = 4;
+ }
}
if (trk != NULL) {
UndoStart( _("Move Label"), "Modedesc( T%d )", GetTrkIndex(trk) );
UndoModify( trk );
}
+ /* no break */
case C_MOVE:
case C_UP:
case C_REDRAW:
@@ -1478,9 +1656,13 @@ STATUS_T CmdMoveDescription(
return CompoundDescriptionMove( trk, action, pos );
case 2:
return CurveDescriptionMove( trk, action, pos );
+ case 3:
+ return CornuDescriptionMove( trk, action, pos );
+ case 4:
+ return BezierDescriptionMove( trk, action, pos );
}
}
-
+ break;
case C_CMDMENU:
moveDescTrk = OnTrack( &pos, TRUE, FALSE );
if ( moveDescTrk == NULL ) break;
@@ -1579,6 +1761,7 @@ static STATUS_T CmdFlip(
pos0 = pos1 = pos;
DrawLine( &tempD, pos0, pos1, 0, wDrawColorBlack );
MainRedraw();
+ MapRedraw();
return C_CONTINUE;
case C_MOVE:
DrawLine( &tempD, pos0, pos1, 0, wDrawColorBlack );
@@ -1586,6 +1769,7 @@ static STATUS_T CmdFlip(
DrawLine( &tempD, pos0, pos1, 0, wDrawColorBlack );
InfoMessage( _("Angle %0.2f"), FindAngle( pos0, pos1 ) );
MainRedraw();
+ MapRedraw();
return C_CONTINUE;
case C_UP:
DrawLine( &tempD, pos0, pos1, 0, wDrawColorBlack );
@@ -1593,6 +1777,7 @@ static STATUS_T CmdFlip(
FlipTracks( pos0, FindAngle( pos0, pos1 ) );
state = 0;
MainRedraw();
+ MapRedraw();
return C_TERMINATE;
#ifdef LATER
@@ -1821,6 +2006,18 @@ static STATUS_T CmdSelect(
if (selectedTrackCount <= 0) {
wMenuPopupShow( selectPopup1M );
} else {
+ coOrd base = pos;
+ track_p trk = OnTrack(&pos, FALSE, FALSE); //Note pollutes pos if turntable
+ if ((trk) &&
+ QueryTrack(trk,Q_CAN_ADD_ENDPOINTS)) { //Turntable snap to center if within 1/4 radius
+ trackParams_t trackParams;
+ if (GetTrackParams(PARAMS_CORNU, trk, pos, &trackParams)) {
+ DIST_T dist = FindDistance(base, trackParams.ttcenter);
+ if (dist < trackParams.ttradius/4) {
+ cmdMenuPos = trackParams.ttcenter;
+ }
+ }
+ }
wMenuPopupShow( selectPopup2M );
}
return C_CONTINUE;
@@ -1871,6 +2068,8 @@ EXPORT void InitCmdSelect( wMenu_p menu )
quickMove2M[1] = wMenuToggleCreate( selectPopup2M, "", _("Simple"), 0, quickMove==1, ChangeQuickMove, (void *) 1 );
quickMove2M[2] = wMenuToggleCreate( selectPopup2M, "", _("End Points"), 0, quickMove==2, ChangeQuickMove, (void *) 2 );
wMenuSeparatorCreate( selectPopup2M );
+ AddMoveMenu( selectPopup2M, QuickMove);
+ wMenuSeparatorCreate( selectPopup2M );
AddRotateMenu( selectPopup2M, QuickRotate );
rotateAlignMI = wMenuPushCreate( selectPopup2M, "", _("Align"), 0, (wMenuCallBack_p)RotateAlign, NULL );
ParamRegister( &rescalePG );
diff --git a/app/bin/cselect.h b/app/bin/cselect.h
index 890e53b..c02cc1c 100644
--- a/app/bin/cselect.h
+++ b/app/bin/cselect.h
@@ -1,6 +1,6 @@
-#ifndef CSELECT_H
-#define CSELECT_H
-
+/** \file cselect.h
+ * Definitions and function prototypes for operations on selected elements
+ */
/* XTrkCad - Model Railroad CAD
* Copyright (C) 2005 Dave Bullis
*
@@ -19,6 +19,12 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
+#ifndef CSELECT_H
+#define CSELECT_H
+
+#include "common.h"
+#include "track.h"
+
wIndex_t selectCmdInx;
wIndex_t moveCmdInx;
wIndex_t rotateCmdInx;
diff --git a/app/bin/csensor.c b/app/bin/csensor.c
index e962089..db34b95 100644
--- a/app/bin/csensor.c
+++ b/app/bin/csensor.c
@@ -47,10 +47,18 @@
static const char rcsid[] = "@(#) : $Id$";
#include <ctype.h>
-#include "track.h"
-#include "trackx.h"
+#include <string.h>
+
#include "compound.h"
+#include "cundo.h"
+#include "custom.h"
+#include "fileio.h"
#include "i18n.h"
+#include "layout.h"
+#include "param.h"
+#include "track.h"
+#include "trackx.h"
+#include "utility.h"
EXPORT TRKTYP_T T_SENSOR = -1;
@@ -433,28 +441,23 @@ static void CreateNewSensor (coOrd orig)
static STATUS_T CmdSensor ( wAction_t action, coOrd pos )
{
-
-
switch (action) {
case C_START:
InfoMessage(_("Place sensor"));
return C_CONTINUE;
case C_DOWN:
- SnapPos(&pos);
- DDrawSensor( &tempD, pos, GetScaleRatio(curScaleInx), wDrawColorBlack );
- return C_CONTINUE;
- case C_MOVE:
- SnapPos(&pos);
- DDrawSensor( &tempD, pos, GetScaleRatio(curScaleInx), wDrawColorBlack );
+ case C_MOVE:
+ SnapPos(&pos);
+ DDrawSensor( &tempD, pos, GetScaleRatio(GetLayoutCurScale()), wDrawColorBlack );
return C_CONTINUE;
case C_UP:
SnapPos(&pos);
- DDrawSensor( &tempD, pos, GetScaleRatio(curScaleInx), wDrawColorBlack );
+ DDrawSensor( &tempD, pos, GetScaleRatio(GetLayoutCurScale()), wDrawColorBlack );
CreateNewSensor(pos);
return C_TERMINATE;
case C_REDRAW:
case C_CANCEL:
- DDrawSensor( &tempD, pos, GetScaleRatio(curScaleInx), wDrawColorBlack );
+ DDrawSensor( &tempD, pos, GetScaleRatio(GetLayoutCurScale()), wDrawColorBlack );
return C_CONTINUE;
default:
return C_CONTINUE;
diff --git a/app/bin/csignal.c b/app/bin/csignal.c
index 06adb19..2f02e58 100644
--- a/app/bin/csignal.c
+++ b/app/bin/csignal.c
@@ -12,7 +12,7 @@
* Author : $Author$
* Created By : Robert Heller
* Created : Sun Feb 19 13:11:45 2017
- * Last Modified : <170314.1311>
+ * Last Modified : <170417.1113>
*
* Description
*
@@ -48,10 +48,18 @@ static const char rcsid[] = "@(#) : $Id$";
#include <ctype.h>
-#include "track.h"
-#include "trackx.h"
+#include <string.h>
+
#include "compound.h"
+#include "cundo.h"
+#include "custom.h"
+#include "fileio.h"
#include "i18n.h"
+#include "layout.h"
+#include "param.h"
+#include "track.h"
+#include "trackx.h"
+#include "utility.h"
EXPORT TRKTYP_T T_SIGNAL = -1;
@@ -515,6 +523,7 @@ static void SignalEditOk ( void * junk )
track_p trk;
signalData_p xx;
wIndex_t ia;
+ CSIZE_T newsize;
if (signalCreate_P) {
UndoStart( _("Create Signal"), "Create Signal");
@@ -526,16 +535,15 @@ static void SignalEditOk ( void * junk )
xx = GetsignalData(trk);
if (xx->numAspects != signalAspect_da.cnt) {
/* We need to reallocate the extra data. */
- /* We will delete the Signal and re-create it. */
- BOOL_T visible = GetTrkVisible(trk);
- SCALEINX_T scale = GetTrkScale(trk);
- LAYER_T layer = GetTrkLayer(trk);
- wIndex_t tindx = GetTrkIndex(trk);
- FreeTrack(trk);
- trk = NewTrack(tindx, T_SIGNAL, 0, sizeof(signalData_t)+(sizeof(signalAspect_t)*(signalAspect_da.cnt-1))+1);
- SetTrkVisible(trk,visible);
- SetTrkScale(trk,scale);
- SetTrkLayer(trk,layer);
+ for (ia = 0; ia < xx->numAspects; ia++) {
+ MyFree((&(xx->aspectList))[ia].aspectName);
+ MyFree((&(xx->aspectList))[ia].aspectScript);
+ (&(xx->aspectList))[ia].aspectName = NULL;
+ (&(xx->aspectList))[ia].aspectScript = NULL;
+ }
+ newsize = sizeof(signalData_t)+(sizeof(signalAspect_t)*(signalAspect_da.cnt-1))+1;
+ trk->extraData = MyRealloc(trk->extraData,newsize);
+ trk->extraSize = newsize;
xx = GetsignalData(trk);
}
}
@@ -769,7 +777,7 @@ static STATUS_T CmdSignal ( wAction_t action, coOrd pos )
case C_MOVE:
SnapPos(&pos);
orient = FindAngle(pos0,pos);
- DDrawSignal( &tempD, pos0, orient, 1, GetScaleRatio(curScaleInx), wDrawColorBlack );
+ DDrawSignal( &tempD, pos0, orient, 1, GetScaleRatio(GetLayoutCurScale()), wDrawColorBlack );
return C_CONTINUE;
case C_UP:
SnapPos(&pos);
@@ -778,7 +786,7 @@ static STATUS_T CmdSignal ( wAction_t action, coOrd pos )
return C_TERMINATE;
case C_REDRAW:
case C_CANCEL:
- DDrawSignal( &tempD, pos0, orient, 1, GetScaleRatio(curScaleInx), wDrawColorBlack );
+ DDrawSignal( &tempD, pos0, orient, 1, GetScaleRatio(GetLayoutCurScale()), wDrawColorBlack );
return C_CONTINUE;
default:
return C_CONTINUE;
diff --git a/app/bin/csnap.c b/app/bin/csnap.c
index 1d16136..0f0f353 100644
--- a/app/bin/csnap.c
+++ b/app/bin/csnap.c
@@ -1,5 +1,5 @@
-/*
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/csnap.c,v 1.7 2008-06-03 15:43:58 m_fischer Exp $
+/** \file csnap.c
+ * Draw Snap Grid
*/
/* XTrkCad - Model Railroad CAD
@@ -20,13 +20,19 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-#include "track.h"
-#include "i18n.h"
+#include <math.h>
+#include "custom.h"
+#include "fileio.h"
+#include "i18n.h"
+#include "messages.h"
+#include "param.h"
+#include "track.h"
+#include "utility.h"
/*****************************************************************************
*
- * Draw Snap Grid
+
*
*/
diff --git a/app/bin/csplit.c b/app/bin/csplit.c
index 69642fb..6cfdcc8 100644
--- a/app/bin/csplit.c
+++ b/app/bin/csplit.c
@@ -1,5 +1,5 @@
-/*
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/csplit.c,v 1.4 2008-03-06 19:35:06 m_fischer Exp $
+/** \file csplit.c
+ * SPLIT
*/
/* XTrkCad - Model Railroad CAD
@@ -20,15 +20,11 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-#include "track.h"
+#include "cundo.h"
#include "i18n.h"
-
-/*****************************************************************************
- *
- * SPLIT
- *
- */
-
+#include "messages.h"
+#include "track.h"
+#include "utility.h"
static wMenu_p splitPopupM[2];
static wMenuToggle_p splitPopupMI[2][4];
@@ -70,6 +66,7 @@ static STATUS_T CmdSplitTrack( wAction_t action, coOrd pos )
switch (action) {
case C_START:
InfoMessage( _("Select track to split") );
+ /* no break */
case C_DOWN:
case C_MOVE:
return C_CONTINUE;
@@ -82,6 +79,11 @@ static STATUS_T CmdSplitTrack( wAction_t action, coOrd pos )
onTrackInSplit = FALSE;
return C_TERMINATE;
}
+ if (!QueryTrack(trk0,Q_MODIFY_CAN_SPLIT)) {
+ onTrackInSplit = FALSE;
+ InfoMessage(_("Can't Split that Track"));
+ return C_CONTINUE;
+ }
ep0 = PickEndPoint( pos, trk0 );
onTrackInSplit = FALSE;
if (ep0 < 0) {
diff --git a/app/bin/cstraigh.c b/app/bin/cstraigh.c
index 6038c9a..7be25ee 100644
--- a/app/bin/cstraigh.c
+++ b/app/bin/cstraigh.c
@@ -1,5 +1,5 @@
-/*
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/cstraigh.c,v 1.4 2008-03-06 19:35:06 m_fischer Exp $
+/** \file cstraigh.c
+ * STRAIGHT
*/
/* XTrkCad - Model Railroad CAD
@@ -19,22 +19,25 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
+#include <math.h>
-#include "track.h"
#include "cstraigh.h"
+#include "cundo.h"
+#include "fileio.h"
#include "i18n.h"
-
-/*******************************************************************************
- *
- * STRAIGHT
- *
- */
+#include "messages.h"
+#include "param.h"
+#include "track.h"
+#include "utility.h"
/*
* STATE INFO
*/
static struct {
coOrd pos0, pos1;
+ track_p trk;
+ EPINX_T ep;
+ BOOL_T down;
} Dl;
@@ -42,15 +45,46 @@ static STATUS_T CmdStraight( wAction_t action, coOrd pos )
{
track_p t;
DIST_T dist;
+ coOrd p;
switch (action) {
case C_START:
- InfoMessage( _("Place 1st end point of Straight track") );
+ Dl.pos0=pos;
+ Dl.pos1=pos;
+ Dl.trk = NULL;
+ Dl.ep=-1;
+ Dl.down = FALSE;
+ InfoMessage( _("Place 1st end point of straight track + Shift -> snap to unconnected endpoint") );
return C_CONTINUE;
case C_DOWN:
- SnapPos( &pos );
+ p = pos;
+ BOOL_T found = FALSE;
+ Dl.trk = NULL;
+ if ((MyGetKeyState() & WKEY_SHIFT) != 0) {
+ if ((t = OnTrack(&p, FALSE, TRUE)) != NULL) {
+ EPINX_T ep = PickUnconnectedEndPointSilent(p, t);
+ if (ep != -1) {
+ Dl.trk = t;
+ Dl.ep = ep;
+ pos = GetTrkEndPos(t, ep);
+ found = TRUE;
+ } else {
+ InfoMessage(_("No unconnected end-point on track - Try again or release Shift and click"));
+ Dl.pos0=pos;
+ Dl.pos1=pos;
+ return C_CONTINUE;
+ }
+ } else {
+ InfoMessage(_("Not on a track - Try again or release Shift and click"));
+ Dl.pos0=pos;
+ Dl.pos1=pos;
+ return C_CONTINUE;
+ }
+ }
+ Dl.down = TRUE;
+ if (!found) SnapPos( &pos );
Dl.pos0 = pos;
InfoMessage( _("Drag to place 2nd end point") );
DYNARR_SET( trkSeg_t, tempSegs_da, 1 );
@@ -62,8 +96,17 @@ static STATUS_T CmdStraight( wAction_t action, coOrd pos )
return C_CONTINUE;
case C_MOVE:
+ if (!Dl.down) return C_CONTINUE;
DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack );
- SnapPos( &pos );
+ ANGLE_T angle, angle2;
+ if (Dl.trk) {
+ angle = NormalizeAngle(GetTrkEndAngle( Dl.trk, Dl.ep));
+ angle2 = NormalizeAngle(FindAngle(pos, Dl.pos0)-angle);
+ if (angle2 > 90.0 && angle2 < 270.0)
+ Translate( &pos, Dl.pos0, angle, FindDistance( Dl.pos0, pos ) );
+ else pos = Dl.pos0;
+ } else SnapPos( &pos );
+
InfoMessage( _("Straight Track Length=%s Angle=%0.3f"),
FormatDistance(FindDistance( Dl.pos0, pos )),
PutAngle(FindAngle( Dl.pos0, pos )) );
@@ -73,15 +116,25 @@ static STATUS_T CmdStraight( wAction_t action, coOrd pos )
return C_CONTINUE;
case C_UP:
+ if (!Dl.down) return C_CONTINUE;
DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack );
tempSegs_da.cnt = 0;
- SnapPos( &pos );
+ if (Dl.trk) {
+ angle = NormalizeAngle(GetTrkEndAngle( Dl.trk, Dl.ep));
+ angle2 = NormalizeAngle(FindAngle(pos, Dl.pos0)-angle);
+ if (angle2 > 90.0 && angle2 < 270.0)
+ Translate( &pos, Dl.pos0, angle, FindDistance( Dl.pos0, pos ));
+ else pos = Dl.pos0;
+ } else SnapPos( &pos );
if ((dist=FindDistance( Dl.pos0, pos )) <= minLength) {
ErrorMessage( MSG_TRK_TOO_SHORT, "Straight ", PutDim(fabs(minLength-dist)) );
return C_TERMINATE;
}
UndoStart( _("Create Straight Track"), "newStraight" );
t = NewStraightTrack( Dl.pos0, pos );
+ if (Dl.trk) {
+ ConnectTracks(Dl.trk, Dl.ep, t, 0);
+ }
UndoEnd();
DrawNewTrack(t);
return C_TERMINATE;
@@ -89,6 +142,7 @@ static STATUS_T CmdStraight( wAction_t action, coOrd pos )
case C_REDRAW:
case C_CANCEL:
DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack );
+ Dl.down = FALSE;
return C_CONTINUE;
default:
diff --git a/app/bin/cstraigh.h b/app/bin/cstraigh.h
index eca7e99..30d1539 100644
--- a/app/bin/cstraigh.h
+++ b/app/bin/cstraigh.h
@@ -1,5 +1,5 @@
-/*
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/cstraigh.h,v 1.1 2005-12-07 15:46:54 rc-flyer Exp $
+/** \file cstraigh.h
+ * Prototypes for straight track functions
*/
/* XTrkCad - Model Railroad CAD
@@ -20,6 +20,14 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
+#ifndef HAVE_CSTRAIGH_H
+#define HAVE_CSTRAIGH_H
+
+#include "common.h"
+#include "track.h"
+
void AdjustStraightEndPt( track_p t, EPINX_T ep, coOrd pos );
track_p NewStraightTrack( coOrd p0, coOrd p1 );
BOOL_T ExtendStraightToJoin( track_p, EPINX_T, track_p, EPINX_T );
+
+#endif // !HAVE_CSTRAIGH_H \ No newline at end of file
diff --git a/app/bin/cstruct.c b/app/bin/cstruct.c
index 1f86217..41c47e5 100644
--- a/app/bin/cstruct.c
+++ b/app/bin/cstruct.c
@@ -1,8 +1,5 @@
-/*
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/cstruct.c,v 1.4 2008-03-06 19:35:06 m_fischer Exp $
- *
+/** \file cstruct.c
* T_STRUCTURE
- *
*/
/* XTrkCad - Model Railroad CAD
@@ -24,11 +21,20 @@
*/
#include <ctype.h>
-#include "track.h"
+#include <math.h>
+#include <stdint.h>
+#include <string.h>
+
#include "compound.h"
+#include "cundo.h"
+#include "custom.h"
+#include "fileio.h"
#include "i18n.h"
-
-#include <stdint.h>
+#include "layout.h"
+#include "messages.h"
+#include "param.h"
+#include "track.h"
+#include "utility.h"
EXPORT TRKTYP_T T_STRUCTURE = -1;
@@ -272,7 +278,7 @@ static ANGLE_T GetAngleStruct(
pos.x -= xx->orig.x;
pos.y -= xx->orig.y;
Rotate( &pos, zero, -xx->angle );
- angle = GetAngleSegs( xx->segCnt, xx->segs, pos, NULL );
+ angle = GetAngleSegs( xx->segCnt, xx->segs, &pos, NULL, NULL, NULL, NULL, NULL);
if ( ep0 ) *ep0 = -1;
if ( ep1 ) *ep1 = -1;
return NormalizeAngle( angle+xx->angle );
@@ -403,7 +409,7 @@ static void structureChange( long changes )
maxStructureDim.x = maxStructureDim.y = 0.0;
if (structureInfo_da.cnt <= 0)
return;
- curStructure = StructAdd( LABEL_TABBED|LABEL_MANUF|LABEL_PARTNO|LABEL_DESCR, curScaleInx, structureListL, &maxStructureDim );
+ curStructure = StructAdd( LABEL_TABBED|LABEL_MANUF|LABEL_PARTNO|LABEL_DESCR, GetLayoutCurScale(), structureListL, &maxStructureDim );
wControlShow( (wControl_p)structureListL, TRUE );
if (curStructure == NULL) {
wDrawClear( structureD.d );
@@ -653,6 +659,7 @@ EXPORT STATUS_T CmdStructureAction(
DrawSegs( &tempD, Dst.pos, Dst.angle,
curStructure->segs, curStructure->segCnt, 0.0, wDrawColorBlack );
MainRedraw();
+ MapRedraw();
InfoMessage( "[ %0.3f %0.3f ]", pos.x - origPos.x, pos.y - origPos.y );
return C_CONTINUE;
@@ -701,6 +708,7 @@ EXPORT STATUS_T CmdStructureAction(
DrawLine( &tempD, rot0, rot1, 0, wDrawColorBlack );
case C_UP:
MainRedraw();
+ MapRedraw();
return C_CONTINUE;
case C_CMDMENU:
@@ -854,7 +862,7 @@ EXPORT void AddHotBarStructures( void )
to = structureInfo(inx);
if ( !( IsParamValid(to->paramFileIndex) &&
to->segCnt > 0 &&
- CompatibleScale( FALSE, to->scaleInx, curScaleInx ) ) )
+ CompatibleScale( FALSE, to->scaleInx, GetLayoutCurScale()) ) )
/*( (strcmp( to->scale, "*" ) == 0 && strcasecmp( curScaleName, "DEMO" ) != 0 ) ||
strncasecmp( to->scale, curScaleName, strlen(to->scale) ) == 0 ) ) )*/
continue;
diff --git a/app/bin/cswitchmotor.c b/app/bin/cswitchmotor.c
index dbe006c..3d39a68 100644
--- a/app/bin/cswitchmotor.c
+++ b/app/bin/cswitchmotor.c
@@ -50,10 +50,17 @@
*/
#include <ctype.h>
-#include "track.h"
-#include "trackx.h"
+#include <string.h>
+
#include "compound.h"
+#include "cundo.h"
+#include "custom.h"
+#include "fileio.h"
#include "i18n.h"
+#include "param.h"
+#include "track.h"
+#include "trackx.h"
+#include "utility.h"
EXPORT TRKTYP_T T_SWITCHMOTOR = -1;
@@ -271,7 +278,7 @@ static DIST_T DistanceSwitchMotor (track_p t, coOrd * p )
{
switchmotorData_p xx = GetswitchmotorData(t);
if (xx->turnout == NULL) return 0;
- return GetTrkDistance(xx->turnout,*p);
+ return GetTrkDistance(xx->turnout,p);
}
static void DescribeSwitchMotor (track_p trk, char * str, CSIZE_T len )
@@ -769,11 +776,11 @@ EXPORT void CheckDeleteSwitchmotor(track_p t)
track_p sm;
switchmotorData_p xx;
- sm = FindSwitchMotor( t );
- if (sm == NULL) return;
- xx = GetswitchmotorData (sm);
- NoticeMessage(_("Deleting Switch Motor %s"),_("Ok"),NULL,xx->name);
- DeleteTrack (sm, FALSE);
+ while ((sm = FindSwitchMotor( t ))) { //Cope with multiple motors for one Turnout!
+ xx = GetswitchmotorData (sm);
+ InfoMessage(_("Deleting Switch Motor %s"),xx->name);
+ DeleteTrack (sm, FALSE);
+ };
}
diff --git a/app/bin/ctext.c b/app/bin/ctext.c
index 525b55a..ca0c7c7 100644
--- a/app/bin/ctext.c
+++ b/app/bin/ctext.c
@@ -1,6 +1,5 @@
/** \file ctext.c
* Text command
- *
*/
/* XTrkCad - Model Railroad CAD
@@ -21,8 +20,15 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-#include "track.h"
+#include "cundo.h"
+#include "fileio.h"
#include "i18n.h"
+#include "messages.h"
+#include "param.h"
+#include "track.h"
+#include "wlib.h"
+#include "draw.h"
+#include "misc.h"
track_p NewText( wIndex_t index, coOrd p, ANGLE_T angle, char * text, CSIZE_T textSize, wDrawColor color );
@@ -43,11 +49,13 @@ static struct {
coOrd cursPos0, cursPos1;
POS_T cursHeight;
POS_T textLen;
+ POS_T lastLineLen;
+ POS_T lastLineOffset;
coOrd pos;
ANGLE_T angle;
long size;
wIndex_t fontSizeInx;
- char text[STR_SIZE];
+ char text[STR_LONG_SIZE];
wDrawColor color;
} Dt;
@@ -70,31 +78,35 @@ static void TextDlgUpdate(
int inx,
void * context )
{
- coOrd size;
+ coOrd size, lastline;
switch (inx) {
case 0:
+ case 1:
if ( Dt.state == SHOW_TEXT) {
- DrawString( &tempD, Dt.pos, 0.0, Dt.text, NULL, (FONTSIZE_T)Dt.size, Dt.color );
+ DrawMultiString( &tempD, Dt.pos, Dt.text, NULL, (FONTSIZE_T)Dt.size, Dt.color, 0, NULL, NULL );
DrawLine( &tempD, Dt.cursPos0, Dt.cursPos1, 0, Dt.color );
}
UpdateFontSizeList( &Dt.size, (wList_p)textPLs[0].control, Dt.fontSizeInx );
/*wWinSetBusy( mainW, TRUE );*/
if ( Dt.state == SHOW_TEXT) {
- DrawTextSize( &mainD, Dt.text, NULL, Dt.size, TRUE, &size );
+ DrawMultiLineTextSize( &mainD, Dt.text, NULL, Dt.size, TRUE, &size, &lastline);
Dt.textLen = size.x;
+ Dt.lastLineLen = lastline.x;
+ Dt.lastLineOffset = lastline.y;
}
- DrawTextSize( &mainD, "X", NULL, Dt.size, TRUE, &size );
+ DrawTextSize( &mainD, "Aquilp", NULL, Dt.size, TRUE, &size );
Dt.cursHeight = size.y;
/*wWinSetBusy( mainW, FALSE );*/
if ( Dt.state == SHOW_TEXT) {
- Dt.cursPos0.x = Dt.cursPos1.x = Dt.pos.x+Dt.textLen;
- Dt.cursPos1.y = Dt.pos.y+Dt.cursHeight;
+ Dt.cursPos0.x = Dt.cursPos1.x = Dt.pos.x+Dt.lastLineLen;
+ Dt.cursPos1.y = Dt.pos.y+Dt.cursHeight+Dt.lastLineOffset;
DrawLine( &tempD, Dt.cursPos0, Dt.cursPos1, 0, Dt.color );
- DrawString( &tempD, Dt.pos, 0.0, Dt.text, NULL, (FONTSIZE_T)Dt.size, Dt.color );
+ DrawMultiString( &tempD, Dt.pos, Dt.text, NULL, (FONTSIZE_T)Dt.size, Dt.color, 0, NULL, NULL );
}
MainRedraw();
- break;
+ MapRedraw();
+ break;
}
}
@@ -105,7 +117,7 @@ static STATUS_T CmdText( wAction_t action, coOrd pos )
unsigned char c;
wControl_p controls[3];
char * labels[2];
- coOrd size;
+ coOrd size, lastline;
switch (action & 0xFF) {
case C_START:
@@ -114,6 +126,8 @@ static STATUS_T CmdText( wAction_t action, coOrd pos )
Dt.len = 0;
Dt.textLen = 0;
Dt.text[0] = '\0';
+ Dt.lastLineLen = 0;
+ Dt.lastLineOffset = 0;
if (textPD.control == NULL)
{
@@ -122,14 +136,14 @@ static STATUS_T CmdText( wAction_t action, coOrd pos )
ParamRegister(&textPG);
Dt.size = GetFontSize(Dt.fontSizeInx);
}
- Dt.size = wSelectedFontSize();
+ Dt.size = (long)wSelectedFontSize();
Dt.fontSizeInx = GetFontSizeIndex(Dt.size);
ParamLoadControls(&textPG);
ParamGroupRecord( &textPG );
if (!inPlayback)
wWinSetBusy(mainW, TRUE);
- DrawTextSize(&mainD, "X", NULL, Dt.size, TRUE, &size);
+ DrawTextSize(&mainD, "Aquilp", NULL, Dt.size, TRUE, &size);
Dt.cursHeight = size.y;
if (!inPlayback)
wWinSetBusy(mainW, FALSE);
@@ -144,28 +158,28 @@ static STATUS_T CmdText( wAction_t action, coOrd pos )
break;
case C_DOWN:
if (Dt.state != 0) {
- //DrawLine( &tempD, Dt.cursPos0, Dt.cursPos1, 0, Dt.color );
- //DrawString( &tempD, Dt.pos, 0.0, Dt.text, NULL, (FONTSIZE_T)Dt.size, Dt.color );
}
Dt.pos = pos;
- Dt.cursPos0.y = Dt.cursPos1.y = pos.y;
- Dt.cursPos0.x = Dt.cursPos1.x = pos.x + Dt.textLen;
+ Dt.cursPos0.y = Dt.cursPos1.y = pos.y + Dt.lastLineOffset;
+ Dt.cursPos0.x = Dt.cursPos1.x = pos.x + Dt.lastLineLen;
+ DrawTextSize(&mainD, "Aquilp", NULL, Dt.size, TRUE, &size); //In case fontsize change
+ Dt.cursHeight = size.y;
Dt.cursPos1.y += Dt.cursHeight;
DrawLine( &tempD, Dt.cursPos0, Dt.cursPos1, 0, Dt.color );
- DrawString( &tempD, Dt.pos, 0.0, Dt.text, NULL, (FONTSIZE_T)Dt.size, Dt.color );
+ DrawMultiString(&tempD, Dt.pos, Dt.text, NULL, (FONTSIZE_T)Dt.size, Dt.color, 0.0, NULL, NULL );
Dt.state = SHOW_TEXT;
MainRedraw();
+ MapRedraw();
return C_CONTINUE;
case C_MOVE:
- //DrawLine( &tempD, Dt.cursPos0, Dt.cursPos1, 0, Dt.color );
- //DrawString( &tempD, Dt.pos, 0.0, Dt.text, NULL, (FONTSIZE_T)Dt.size, Dt.color );
Dt.pos = pos;
- Dt.cursPos0.y = Dt.cursPos1.y = pos.y;
- Dt.cursPos0.x = Dt.cursPos1.x = pos.x + Dt.textLen;
+ Dt.cursPos0.y = Dt.cursPos1.y = pos.y + Dt.lastLineOffset;
+ Dt.cursPos0.x = Dt.cursPos1.x = pos.x + Dt.lastLineLen;
Dt.cursPos1.y += Dt.cursHeight;
DrawLine( &tempD, Dt.cursPos0, Dt.cursPos1, 0, wDrawColorBlack );
- DrawString( &tempD, Dt.pos, 0.0, Dt.text, NULL, (FONTSIZE_T)Dt.size, Dt.color );
+ DrawMultiString(&tempD, Dt.pos, Dt.text, NULL, (FONTSIZE_T)Dt.size, Dt.color, 0.0, NULL, NULL );
MainRedraw();
+ MapRedraw();
return C_CONTINUE;
case C_UP:
return C_CONTINUE;
@@ -175,7 +189,7 @@ static STATUS_T CmdText( wAction_t action, coOrd pos )
return C_CONTINUE;
}
DrawLine( &tempD, Dt.cursPos0, Dt.cursPos1, 0, Dt.color );
- DrawString( &tempD, Dt.pos, 0.0, Dt.text, NULL, (FONTSIZE_T)Dt.size, Dt.color );
+ DrawMultiString(&tempD, Dt.pos, Dt.text, NULL, (FONTSIZE_T)Dt.size, Dt.color, 0.0, NULL, NULL );
c = (unsigned char)(action >> 8);
switch (c) {
case '\b':
@@ -187,11 +201,17 @@ static STATUS_T CmdText( wAction_t action, coOrd pos )
wBeep();
}
break;
+ case '\n': // Line Feed
+ if (Dt.len < sizeof Dt.text - 1 ) {
+ Dt.text[Dt.len++] = (char)c;
+ Dt.text[Dt.len] = '\000';
+ }
+ break;
case '\015':
UndoStart( _("Create Text"), "newText - CR" );
t = NewText( 0, Dt.pos, Dt.angle, Dt.text, (CSIZE_T)Dt.size, Dt.color );
UndoEnd();
- DrawNewTrack(t);
+ DrawNewTrack(t);
Dt.state = POSITION_TEXT;
InfoSubstituteControls( NULL, NULL );
return C_TERMINATE;
@@ -201,26 +221,33 @@ static STATUS_T CmdText( wAction_t action, coOrd pos )
Dt.text[Dt.len] = '\000';
}
}
- DrawTextSize( &mainD, Dt.text, NULL, Dt.size, TRUE, &size );
+ DrawMultiLineTextSize( &mainD, Dt.text, NULL, Dt.size, TRUE, &size, &lastline);
Dt.textLen = size.x;
- Dt.cursPos0.x = Dt.cursPos1.x = Dt.pos.x + Dt.textLen;
- DrawLine( &tempD, Dt.cursPos0, Dt.cursPos1, 0, Dt.color );
- DrawString( &tempD, Dt.pos, 0.0, Dt.text, NULL, (FONTSIZE_T)Dt.size, Dt.color );
+ Dt.lastLineLen = lastline.x;
+ Dt.lastLineOffset = lastline.y;
+ Dt.cursPos0.x = Dt.cursPos1.x = Dt.pos.x + Dt.lastLineLen;
+ Dt.cursPos0.y = Dt.cursPos1.y = Dt.pos.y + Dt.lastLineOffset;
+ DrawTextSize(&mainD, "Aquilp", NULL, Dt.size, TRUE, &size); //In case fontsize change
+ Dt.cursHeight = size.y;
+ Dt.cursPos1.y +=Dt.cursHeight;
+ MainRedraw();
+ MapRedraw();
+ //DrawLine( &tempD, Dt.cursPos0, Dt.cursPos1, 0, Dt.color );
+ //DrawMultiString(&tempD, Dt.pos, Dt.text, NULL, (FONTSIZE_T)Dt.size, Dt.color, 0.0, NULL, NULL );
return C_CONTINUE;
case C_REDRAW:
if (Dt.state == 1) {
DrawLine( &tempD, Dt.cursPos0, Dt.cursPos1, 0, Dt.color );
- DrawString( &tempD, Dt.pos, 0.0, Dt.text, NULL, (FONTSIZE_T)Dt.size, Dt.color );
+ DrawMultiString(&tempD, Dt.pos, Dt.text, NULL, (FONTSIZE_T)Dt.size, Dt.color, 0.0, NULL, NULL );
}
return C_CONTINUE;
case C_CANCEL:
if (Dt.state != POSITION_TEXT) {
- //DrawString( &tempD, Dt.pos, 0.0, Dt.text, NULL, (FONTSIZE_T)Dt.size, Dt.color );
- //DrawLine( &tempD, Dt.cursPos0, Dt.cursPos1, 0, Dt.color );
Dt.state = POSITION_TEXT;
}
InfoSubstituteControls( NULL, NULL );
MainRedraw();
+ MapRedraw();
return C_TERMINATE;
case C_OK:
if (Dt.state != POSITION_TEXT) {
@@ -235,6 +262,7 @@ static STATUS_T CmdText( wAction_t action, coOrd pos )
}
InfoSubstituteControls( NULL, NULL );
MainRedraw();
+ MapRedraw();
return C_TERMINATE;
case C_FINISH:
diff --git a/app/bin/ctodesgn.c b/app/bin/ctodesgn.c
index b5ba768..392b405 100644
--- a/app/bin/ctodesgn.c
+++ b/app/bin/ctodesgn.c
@@ -26,14 +26,22 @@
#endif
#include <stdint.h>
-
#include <ctype.h>
-#include "track.h"
+#include <math.h>
+#include <string.h>
+#include <messages.h>
+
#include "ccurve.h"
-#include "cstraigh.h"
#include "compound.h"
+#include "cstraigh.h"
+#include "custom.h"
+#include "fileio.h"
#include "i18n.h"
+#include "param.h"
+#include "track.h"
+#include "utility.h"
+
#define TURNOUTDESIGNER "CTURNOUT DESIGNER"
@@ -1414,7 +1422,7 @@ static void NewTurnPrint(
wDrawString( newTurnout_d.d, POSX(3.0),
POSY(6.25), 0.0, message, fp, 30,
wDrawColorBlack, 0 );
- sprintf( message, "%s %d x %d (of %d x %d)", _("Page"), i+1, j+1, ii, jj );
+ sprintf( message, _("%s %d x %d (of %d x %d)"), _("Page"), i+1, j+1, ii, jj );
wDrawString( newTurnout_d.d, POSX(3.0),
POSY(5.75), 0.0, message, fp, 20,
wDrawColorBlack, 0 );
@@ -2176,14 +2184,14 @@ EXPORT BOOL_T WriteSegs(
case SEG_CRVLIN:
rc &= fprintf( f, "\t%c %ld %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f\n",
segs[i].type, (segs[i].type==SEG_CRVTRK?0:roadbedColorRGB), segs[i].width,
- segs[i].u.c.radius,
+ fabs(segs[i].u.c.radius),
segs[i].u.c.center.x, segs[i].u.c.center.y,
segs[i].u.c.a0, segs[i].u.c.a1 )>0;
break;
case SEG_FILCRCL:
rc &= fprintf( f, "\t%c %ld %0.6f %0.6f %0.6f %0.6f\n",
segs[i].type, roadbedColorRGB, segs[i].width,
- segs[i].u.c.radius,
+ fabs(segs[i].u.c.radius),
segs[i].u.c.center.x, segs[i].u.c.center.y )>0;
break;
case SEG_POLY:
diff --git a/app/bin/ctrain.c b/app/bin/ctrain.c
index d3eb00a..d480982 100644
--- a/app/bin/ctrain.c
+++ b/app/bin/ctrain.c
@@ -1,6 +1,5 @@
/** \file ctrain.c
* Functions related to running trains
- *
*/
/* XTrkCad - Model Railroad CAD
@@ -25,37 +24,47 @@
#include <errno.h>
#endif
#include <ctype.h>
+#include <math.h>
+#include <string.h>
#define PRIVATE_EXTRADATA
-#include "track.h"
-#include "trackx.h"
-#include "ctrain.h"
+
#include "compound.h"
+#include "ctrain.h"
+#include "cundo.h"
+#include "custom.h"
+#include "fileio.h"
#include "i18n.h"
+#include "layout.h"
+#include "messages.h"
+#include "param.h"
+#include "track.h"
+#include "trackx.h"
+#include "utility.h"
-EXPORT long programMode;
-EXPORT long maxCouplingSpeed = 100;
-EXPORT long hideTrainsInTunnels;
+long programMode;
+long maxCouplingSpeed = 100;
+long hideTrainsInTunnels;
extern int doDrawTurnoutPosition;
-extern void NextTurnoutPosition( track_p );
+extern void NextTurnoutPosition(track_p);
static TRKTYP_T T_CAR = -1;
typedef enum { ST_NotOnTrack, ST_StopManual, ST_EndOfTrack, ST_OpenTurnout, ST_NoRoom, ST_Crashed } trainStatus_e;
struct extraData {
- traverseTrack_t trvTrk;
- long state;
- carItem_p item;
- double speed;
- BOOL_T direction;
- BOOL_T autoReverse;
- trainStatus_e status;
- DIST_T distance;
- coOrd couplerPos[2];
- LAYER_T trkLayer;
- };
+ traverseTrack_t trvTrk;
+ long state;
+ carItem_p item;
+ double speed;
+ BOOL_T direction;
+ BOOL_T autoReverse;
+ trainStatus_e status;
+ DIST_T distance;
+ coOrd couplerPos[2];
+ unsigned int trkLayer;
+};
#define NOTALAYER (127)
#define CAR_STATE_IGNORED (1L<<17)
@@ -63,16 +72,14 @@ struct extraData {
#define CAR_STATE_LOCOISMASTER (1L<<19)
#define CAR_STATE_ONHIDENTRACK (1L<<20)
+#define COUPLERCONNECTIONANGLE 45.0
+#define CRASHSPEEDDECAY 5
#define IsOnTrack( XX ) ((XX)->trvTrk.trk!=NULL)
#define IsIgnored( XX ) (((XX)->state&CAR_STATE_IGNORED)!=0)
#define SetIgnored( XX ) (XX)->state |= CAR_STATE_IGNORED
#define ClrIgnored( XX ) (XX)->state &= ~CAR_STATE_IGNORED
-#ifdef LATER
-#define IsLocoMaster( XX ) (((XX)->state&CAR_STATE_LOCOISMASTER)!=0)
-#define SetLocoMaster( XX ) (XX)->state |= CAR_STATE_LOCOISMASTER
-#define ClrLocoMaster( XX ) (XX)->state &= ~CAR_STATE_LOCOISMASTER
-#endif
+
#define IsLocoMaster( XX ) CarItemIsLocoMaster((XX)->item)
#define SetLocoMaster( XX ) CarItemSetLocoMaster((XX)->item,TRUE)
#define ClrLocoMaster( XX ) CarItemSetLocoMaster((XX)->item,FALSE)
@@ -82,8 +89,8 @@ struct extraData {
static wButton_p newcarB;
-static void ControllerDialogSyncAll( void );
-static STATUS_T CmdTrain( wAction_t, coOrd );
+static void ControllerDialogSyncAll(void);
+static STATUS_T CmdTrain(wAction_t, coOrd);
static wMenu_p trainPopupM;
static wMenuPush_p trainPopupMI[8];
static track_p followTrain;
@@ -91,16 +98,16 @@ static coOrd followCenter;
static BOOL_T trainsTimeoutPending;
static enum { TRAINS_STOP, TRAINS_RUN, TRAINS_IDLE, TRAINS_PAUSE } trainsState;
static wIcon_p stopI, goI;
-static void RestartTrains( void );
-static void DrawAllCars( void );
-static void UncoupleCars( track_p, track_p );
-static void TrainTimeEndPause( void );
-static void TrainTimeStartPause( void );
+static void RestartTrains(void);
+static void DrawAllCars(void);
+static void UncoupleCars(track_p, track_p);
+static void TrainTimeEndPause(void);
+static void TrainTimeStartPause(void);
static int log_trainMove;
static int log_trainPlayback;
-static void PlaceCar( track_p );
+static void PlaceCar(track_p);
#define WALK_CARS_START( CAR, XX, DIR ) \
@@ -123,151 +130,175 @@ static void PlaceCar( track_p );
* Generic Commands
*/
-EXPORT void CarGetPos(
- track_p car,
- coOrd * posR,
- ANGLE_T * angleR )
+void CarGetPos(
+ track_p car,
+ coOrd * posR,
+ ANGLE_T * angleR)
{
- struct extraData * xx = GetTrkExtraData( car );
- if ( GetTrkType(car) != T_CAR )
- AbortProg( "getCarPos" );
- *posR = xx->trvTrk.pos;
- *angleR = xx->trvTrk.angle;
+ struct extraData * xx = GetTrkExtraData(car);
+
+ if (GetTrkType(car) != T_CAR) {
+ AbortProg("getCarPos");
+ }
+
+ *posR = xx->trvTrk.pos;
+ *angleR = xx->trvTrk.angle;
}
-EXPORT void CarSetVisible(
- track_p car )
+void CarSetVisible(
+ track_p car)
{
- struct extraData * xx;
- int dir;
- dir = 0;
- WALK_CARS_START( car, xx, dir )
- if ( GetTrkType(car) != T_CAR )
- AbortProg( "carSetVisible" );
- WALK_CARS_END( car, xx, dir )
- dir = 1-dir;
- WALK_CARS_START( car, xx, dir ) {
- xx->state &= ~(CAR_STATE_ONHIDENTRACK);
- xx->trkLayer = NOTALAYER;
- }
- WALK_CARS_END( car, xx, dir )
+ struct extraData * xx;
+ int dir;
+ dir = 0;
+ WALK_CARS_START(car, xx, dir)
+
+ if (GetTrkType(car) != T_CAR) {
+ AbortProg("carSetVisible");
+ }
+
+ WALK_CARS_END(car, xx, dir)
+ dir = 1-dir;
+ WALK_CARS_START(car, xx, dir) {
+ xx->state &= ~(CAR_STATE_ONHIDENTRACK);
+ xx->trkLayer = NOTALAYER;
+ }
+ WALK_CARS_END(car, xx, dir)
}
static struct {
- long index;
- coOrd pos;
- ANGLE_T angle;
- DIST_T length;
- DIST_T width;
- char desc[STR_SIZE];
- char number[STR_SIZE];
- } carData;
+ long index;
+ coOrd pos;
+ ANGLE_T angle;
+ DIST_T length;
+ DIST_T width;
+ char desc[STR_SIZE];
+ char number[STR_SIZE];
+} carData;
typedef enum { IT, PN, AN, LN, WD, DE, NM } carDesc_e;
static descData_t carDesc[] = {
-/*IT*/ { DESC_LONG, N_("Index"), &carData.index },
-/*PN*/ { DESC_POS, N_("Position"), &carData.pos },
-/*AN*/ { DESC_ANGLE, N_("Angle"), &carData.angle },
-/*LN*/ { DESC_DIM, N_("Length"), &carData.length },
-/*WD*/ { DESC_DIM, N_("Width"), &carData.width },
-/*DE*/ { DESC_STRING, N_("Description"), &carData.desc },
-/*NM*/ { DESC_STRING, N_("Rep Marks"), &carData.number },
- { DESC_NULL } };
+ /*IT*/ { DESC_LONG, N_("Index"), &carData.index },
+ /*PN*/ { DESC_POS, N_("Position"), &carData.pos },
+ /*AN*/ { DESC_ANGLE, N_("Angle"), &carData.angle },
+ /*LN*/ { DESC_DIM, N_("Length"), &carData.length },
+ /*WD*/ { DESC_DIM, N_("Width"), &carData.width },
+ /*DE*/ { DESC_STRING, N_("Description"), &carData.desc },
+ /*NM*/ { DESC_STRING, N_("Rep Marks"), &carData.number },
+ { DESC_NULL }
+};
static void UpdateCar(
- track_p trk,
- int inx,
- descData_p descUpd,
- BOOL_T needUndoStart )
+ track_p trk,
+ int inx,
+ descData_p descUpd,
+ BOOL_T needUndoStart)
{
- BOOL_T titleChanged;
- const char * cp;
- if ( inx == -1 ) {
- titleChanged = FALSE;
- cp = wStringGetValue( (wString_p)carDesc[NM].control0 );
- if ( cp && strcmp( carData.number, cp ) != 0 ) {
- titleChanged = TRUE;
- strcpy( carData.number, cp );
- }
- if ( !titleChanged )
- return;
- if ( needUndoStart )
- UndoStart( _("Change Track"), "Change Track" );
- UndoModify( trk );
- UndrawNewTrack( trk );
- DrawNewTrack( trk );
- return;
- }
- UndrawNewTrack( trk );
- switch (inx) {
- case NM:
- break;
- default:
- break;
- }
- DrawNewTrack( trk );
+ if (inx == -1) {
+ BOOL_T titleChanged;
+ const char * cp;
+ titleChanged = FALSE;
+ cp = wStringGetValue((wString_p)carDesc[NM].control0);
+
+ if (cp && strcmp(carData.number, cp) != 0) {
+ titleChanged = TRUE;
+ strcpy(carData.number, cp);
+ }
+
+ if (!titleChanged) {
+ return;
+ }
+
+ if (needUndoStart) {
+ UndoStart(_("Change Track"), "Change Track");
+ }
+
+ UndoModify(trk);
+ UndrawNewTrack(trk);
+ DrawNewTrack(trk);
+ return;
+ }
+
+ UndrawNewTrack(trk);
+
+ switch (inx) {
+ case NM:
+ break;
+
+ default:
+ break;
+ }
+
+ DrawNewTrack(trk);
}
static void DescribeCar(
- track_p trk,
- char * str,
- CSIZE_T len )
+ track_p trk,
+ char * str,
+ CSIZE_T len)
+{
+ struct extraData *xx = GetTrkExtraData(trk);
+ char * cp;
+ coOrd size;
+ CarItemSize(xx->item, &size);
+ carData.length = size.x;
+ carData.width = size.y;
+ cp = CarItemDescribe(xx->item, 0, &carData.index);
+ strcpy(carData.number, CarItemNumber(xx->item));
+ strncpy(str, cp, len);
+ carData.pos = xx->trvTrk.pos;
+ carData.angle = xx->trvTrk.angle;
+ cp = CarItemDescribe(xx->item, -1, NULL);
+ strncpy(carData.desc, cp, sizeof carData.desc);
+ carDesc[IT].mode =
+ carDesc[PN].mode =
+ carDesc[AN].mode =
+ carDesc[LN].mode =
+ carDesc[WD].mode = DESC_RO;
+ carDesc[DE].mode =
+ carDesc[NM].mode = DESC_RO;
+ DoDescribe(_("Car"), trk, carDesc, UpdateCar);
+}
+
+
+void FlipTraverseTrack(
+ traverseTrack_p trvTrk)
{
- struct extraData *xx = GetTrkExtraData(trk);
- char * cp;
- coOrd size;
-
- CarItemSize( xx->item, &size );
- carData.length = size.x;
- carData.width = size.y;
- cp = CarItemDescribe( xx->item, 0, &carData.index );
- strcpy( carData.number, CarItemNumber(xx->item) );
- strncpy( str, cp, len );
- carData.pos = xx->trvTrk.pos;
- carData.angle = xx->trvTrk.angle;
- cp = CarItemDescribe( xx->item, -1, NULL );
- strncpy( carData.desc, cp, sizeof carData.desc );
- carDesc[IT].mode =
- carDesc[PN].mode =
- carDesc[AN].mode =
- carDesc[LN].mode =
- carDesc[WD].mode = DESC_RO;
- carDesc[DE].mode =
- carDesc[NM].mode = DESC_RO;
- DoDescribe( _("Car"), trk, carDesc, UpdateCar );
+ trvTrk->angle = NormalizeAngle(trvTrk->angle + 180.0);
+
+ if (trvTrk->length > 0) {
+ trvTrk->dist = trvTrk->length - trvTrk->dist;
+ }
}
-EXPORT void FlipTraverseTrack(
- traverseTrack_p trvTrk )
+BOOL_T TraverseTrack2(
+ traverseTrack_p trvTrk0,
+ DIST_T dist0)
{
- trvTrk->angle = NormalizeAngle( trvTrk->angle + 180.0 );
- if ( trvTrk->length > 0 )
- trvTrk->dist = trvTrk->length - trvTrk->dist;
-}
+ traverseTrack_t trvTrk = *trvTrk0;
+ DIST_T dist = dist0;
+ if (dist0 < 0) {
+ dist = -dist;
+ FlipTraverseTrack(&trvTrk);
+ }
-EXPORT BOOL_T TraverseTrack2(
- traverseTrack_p trvTrk0,
- DIST_T dist0 )
-{
- traverseTrack_t trvTrk = *trvTrk0;
- DIST_T dist = dist0;
- if ( dist0 < 0 ) {
- dist = -dist;
- FlipTraverseTrack( &trvTrk );
- }
- if ( trvTrk.trk==NULL ||
- (!TraverseTrack(&trvTrk,&dist)) ||
- trvTrk.trk==NULL ||
- dist!=0.0 ) {
- Translate( &trvTrk.pos, trvTrk.pos, trvTrk.angle, dist );
- }
- if ( dist0 < 0 )
- FlipTraverseTrack( &trvTrk );
- *trvTrk0 = trvTrk;
- return TRUE;
+ if (trvTrk.trk==NULL ||
+ (!TraverseTrack(&trvTrk,&dist)) ||
+ trvTrk.trk==NULL ||
+ dist!=0.0) {
+ Translate(&trvTrk.pos, trvTrk.pos, trvTrk.angle, dist);
+
+ }
+
+ if (dist0 < 0) {
+ FlipTraverseTrack(&trvTrk);
+ }
+
+ *trvTrk0 = trvTrk;
+ return TRUE;
}
@@ -276,220 +307,238 @@ static BOOL_T drawCarEnable = TRUE;
static BOOL_T noCarDraw = FALSE;
static void DrawCar(
- track_p car,
- drawCmd_p d,
- wDrawColor color )
+ track_p car,
+ drawCmd_p d,
+ wDrawColor color)
{
- struct extraData * xx = GetTrkExtraData(car);
- int dir;
- vector_t coupler[2];
- track_p car1;
- struct extraData * xx1;
- int dir1;
-
- if ( drawCarEnable == FALSE )
- return;
- /*d = &tempD;*/
-/*
- if ( !IsVisible(xx) )
- return;
-*/
- if ( d == &mapD )
- return;
- if ( noCarDraw )
- return;
- if ( hideTrainsInTunnels &&
- ( (((xx->state&CAR_STATE_ONHIDENTRACK)!=0) && drawTunnel==0) ||
- (xx->trkLayer!=NOTALAYER && !GetLayerVisible(xx->trkLayer)) ) )
- return;
-
- for ( dir=0; dir<2; dir++ ) {
- coupler[dir].pos = xx->couplerPos[dir];
- if ( (car1 = GetTrkEndTrk(car,dir)) ) {
- xx1 = GetTrkExtraData(car1);
- dir1 = (GetTrkEndTrk(car1,0)==car)?0:1;
- coupler[dir].angle = FindAngle( xx->couplerPos[dir], xx1->couplerPos[dir1] );
- } else {
- coupler[dir].angle = NormalizeAngle(xx->trvTrk.angle+(dir==0?0.0:180.0)-15.0);
- }
- }
- CarItemDraw( d, xx->item, color, xx->direction, IsLocoMaster(xx), coupler );
+ struct extraData * xx = GetTrkExtraData(car);
+ int dir;
+ vector_t coupler[2];
+ struct extraData * xx1;
+ int dir1;
+
+ if (drawCarEnable == FALSE) {
+ return;
+ }
+
+ if (d == &mapD) {
+ return;
+ }
+
+ if (noCarDraw) {
+ return;
+ }
+
+ if (hideTrainsInTunnels &&
+ ((((xx->state&CAR_STATE_ONHIDENTRACK)!=0) && drawTunnel==0) ||
+ (xx->trkLayer!=NOTALAYER && !GetLayerVisible(xx->trkLayer)))) {
+ return;
+ }
+
+ for (dir=0; dir<2; dir++) {
+ track_p car1;
+ coupler[dir].pos = xx->couplerPos[dir];
+
+ if ((car1 = GetTrkEndTrk(car,dir))) {
+ xx1 = GetTrkExtraData(car1);
+ dir1 = (GetTrkEndTrk(car1,0)==car)?0:1;
+ coupler[dir].angle = FindAngle(xx->couplerPos[dir], xx1->couplerPos[dir1]);
+ } else {
+ coupler[dir].angle = NormalizeAngle(xx->trvTrk.angle+(dir==0?0.0:180.0)-15.0);
+ }
+ }
+
+ CarItemDraw(d, xx->item, color, xx->direction, IsLocoMaster(xx), coupler);
}
static DIST_T DistanceCar(
- track_p trk,
- coOrd * pos )
+ track_p trk,
+ coOrd * pos)
{
- struct extraData * xx = GetTrkExtraData(trk);
- DIST_T dist;
- coOrd pos1;
- coOrd size;
-
- xx = GetTrkExtraData(trk);
- if ( IsIgnored(xx) )
- return 10000.0;
-
- CarItemSize( xx->item, &size ); /* TODO assumes xx->trvTrk.pos is the car center */
- dist = FindDistance( *pos, xx->trvTrk.pos );
- if ( dist < size.x/2.0 ) {
- pos1 = *pos;
- Rotate( &pos1, xx->trvTrk.pos, -xx->trvTrk.angle );
- pos1.x += -xx->trvTrk.pos.x + size.y/2.0; /* TODO: why not size.x? */
- pos1.y += -xx->trvTrk.pos.y + size.x/2.0;
- if ( pos1.x >= 0 && pos1.x <= size.y &&
- pos1.y >= 0 && pos1.y <= size.x )
- dist = 0;
- }
- *pos = xx->trvTrk.pos;
- return dist;
-}
+ struct extraData * xx = GetTrkExtraData(trk);
+ DIST_T dist;
+ coOrd pos1;
+ coOrd size;
+ if (IsIgnored(xx)) {
+ return 10000.0;
+ }
-static void SetCarBoundingBox(
- track_p car )
-{
- struct extraData * xx = GetTrkExtraData(car);
- coOrd lo, hi, p[4];
- int inx;
- coOrd size;
-
-/* TODO: should be bounding box of all pieces aligned on track */
- CarItemSize( xx->item, &size ); /* TODO assumes xx->trvTrk.pos is the car center */
- Translate( &p[0], xx->trvTrk.pos, xx->trvTrk.angle, size.x/2.0 );
- Translate( &p[1], p[0], xx->trvTrk.angle+90, size.y/2.0 );
- Translate( &p[0], p[0], xx->trvTrk.angle-90, size.y/2.0 );
- Translate( &p[2], xx->trvTrk.pos, xx->trvTrk.angle+180, size.x/2.0 );
- Translate( &p[3], p[2], xx->trvTrk.angle+90, size.y/2.0 );
- Translate( &p[2], p[2], xx->trvTrk.angle-90, size.y/2.0 );
- lo = hi = p[0];
- for ( inx = 1; inx < 4; inx++ ) {
- if ( p[inx].x < lo.x )
- lo.x = p[inx].x;
- if ( p[inx].y < lo.y )
- lo.y = p[inx].y;
- if ( p[inx].x > hi.x )
- hi.x = p[inx].x;
- if ( p[inx].y > hi.y )
- hi.y = p[inx].y;
- }
- SetBoundingBox( car, hi, lo );
+ CarItemSize(xx->item,
+ &size); /* TODO assumes xx->trvTrk.pos is the car center */
+ dist = FindDistance(*pos, xx->trvTrk.pos);
+
+ if (dist < size.x/2.0) {
+ pos1 = *pos;
+ Rotate(&pos1, xx->trvTrk.pos, -xx->trvTrk.angle);
+ pos1.x += -xx->trvTrk.pos.x + size.y/2.0; /* TODO: why not size.x? */
+ pos1.y += -xx->trvTrk.pos.y + size.x/2.0;
+ if (pos1.x >= 0 && pos1.x <= size.y &&
+ pos1.y >= 0 && pos1.y <= size.x) {
+ dist = 0;
+ }
+ }
+
+ *pos = xx->trvTrk.pos;
+ return dist;
}
-EXPORT track_p NewCar(
- wIndex_t index,
- carItem_p item,
- coOrd pos,
- ANGLE_T angle )
+static void SetCarBoundingBox(
+ track_p car)
{
- track_p trk;
- struct extraData * xx;
-
- trk = NewTrack( index, T_CAR, 2, sizeof (*xx) );
- /*SetEndPts( trk, 0 );*/
- xx = GetTrkExtraData(trk);
- /*SetTrkVisible( trk, IsVisible(xx) );*/
- xx->item = item;
- xx->trvTrk.pos = pos;
- xx->trvTrk.angle = angle;
- xx->state = 0;
- SetCarBoundingBox( trk );
- CarItemSetTrack( item, trk );
- PlaceCar( trk );
- return trk;
+ struct extraData * xx = GetTrkExtraData(car);
+ coOrd lo, hi, p[4];
+ int inx;
+ coOrd size;
+ /* TODO: should be bounding box of all pieces aligned on track */
+ CarItemSize(xx->item,
+ &size); /* TODO assumes xx->trvTrk.pos is the car center */
+ Translate(&p[0], xx->trvTrk.pos, xx->trvTrk.angle, size.x/2.0);
+ Translate(&p[1], p[0], xx->trvTrk.angle+90, size.y/2.0);
+ Translate(&p[0], p[0], xx->trvTrk.angle-90, size.y/2.0);
+ Translate(&p[2], xx->trvTrk.pos, xx->trvTrk.angle+180, size.x/2.0);
+ Translate(&p[3], p[2], xx->trvTrk.angle+90, size.y/2.0);
+ Translate(&p[2], p[2], xx->trvTrk.angle-90, size.y/2.0);
+ lo = hi = p[0];
+
+ for (inx = 1; inx < 4; inx++) {
+ if (p[inx].x < lo.x) {
+ lo.x = p[inx].x;
+ }
+
+ if (p[inx].y < lo.y) {
+ lo.y = p[inx].y;
+ }
+
+ if (p[inx].x > hi.x) {
+ hi.x = p[inx].x;
+ }
+
+ if (p[inx].y > hi.y) {
+ hi.y = p[inx].y;
+ }
+ }
+
+ SetBoundingBox(car, hi, lo);
+}
+
+
+track_p NewCar(
+ wIndex_t index,
+ carItem_p item,
+ coOrd pos,
+ ANGLE_T angle)
+{
+ track_p trk;
+ struct extraData * xx;
+ trk = NewTrack(index, T_CAR, 2, sizeof(*xx));
+ /*SetEndPts( trk, 0 );*/
+ xx = GetTrkExtraData(trk);
+ /*SetTrkVisible( trk, IsVisible(xx) );*/
+ xx->item = item;
+ xx->trvTrk.pos = pos;
+ xx->trvTrk.angle = angle;
+ xx->state = 0;
+ SetCarBoundingBox(trk);
+ CarItemSetTrack(item, trk);
+ PlaceCar(trk);
+ return trk;
}
static void DeleteCar(
- track_p trk )
+ track_p trk)
{
- struct extraData * xx = GetTrkExtraData(trk);
- CarItemSetTrack( xx->item, NULL );
+ struct extraData * xx = GetTrkExtraData(trk);
+ CarItemSetTrack(xx->item, NULL);
}
static void ReadCar(
- char * line )
+ char * line)
{
- CarItemRead( line );
+ CarItemRead(line);
}
static BOOL_T WriteCar(
- track_p trk,
- FILE * f )
+ track_p trk,
+ FILE * f)
{
- BOOL_T rc = TRUE;
- return rc;
+ BOOL_T rc = TRUE;
+ return rc;
}
static void MoveCar(
- track_p car,
- coOrd pos )
+ track_p car,
+ coOrd pos)
{
- struct extraData *xx = GetTrkExtraData(car);
- xx->trvTrk.pos.x += pos.x;
- xx->trvTrk.pos.y += pos.y;
- xx->trvTrk.trk = NULL;
- PlaceCar( car );
- SetCarBoundingBox(car);
+ struct extraData *xx = GetTrkExtraData(car);
+ xx->trvTrk.pos.x += pos.x;
+ xx->trvTrk.pos.y += pos.y;
+ xx->trvTrk.trk = NULL;
+ PlaceCar(car);
+ SetCarBoundingBox(car);
}
static void RotateCar(
- track_p car,
- coOrd pos,
- ANGLE_T angle )
+ track_p car,
+ coOrd pos,
+ ANGLE_T angle)
{
- struct extraData *xx = GetTrkExtraData(car);
- Rotate( &xx->trvTrk.pos, pos, angle );
- xx->trvTrk.angle = NormalizeAngle( xx->trvTrk.angle + angle );
- xx->trvTrk.trk = NULL;
- PlaceCar( car );
- SetCarBoundingBox( car );
+ struct extraData *xx = GetTrkExtraData(car);
+ Rotate(&xx->trvTrk.pos, pos, angle);
+ xx->trvTrk.angle = NormalizeAngle(xx->trvTrk.angle + angle);
+ xx->trvTrk.trk = NULL;
+ PlaceCar(car);
+ SetCarBoundingBox(car);
}
-static BOOL_T QueryCar( track_p trk, int query )
+static BOOL_T QueryCar(track_p trk, int query)
{
- switch ( query ) {
- case Q_NODRAWENDPT:
- return TRUE;
- default:
- return FALSE;
- }
+ switch (query) {
+ case Q_NODRAWENDPT:
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
}
static trackCmd_t carCmds = {
- "CAR ",
- DrawCar, /* draw */
- DistanceCar, /* distance */
- DescribeCar, /* describe */
- DeleteCar, /* delete */
- WriteCar, /* write */
- ReadCar, /* read */
- MoveCar, /* move */
- RotateCar, /* rotate */
- NULL, /* rescale */
- NULL, /* audit */
- NULL, /* getAngle */
- NULL, /* split */
- NULL, /* traverse */
- NULL, /* enumerate */
- NULL, /* redraw*/
- NULL, /* trim*/
- NULL, /* merge*/
- NULL, /* modify */
- NULL, /* getLength */
- NULL, /* getParams */
- NULL, /* moveEndPt */
- QueryCar, /* query */
- NULL, /* ungroup */
- NULL, /* flip */ };
+ "CAR ",
+ DrawCar, /* draw */
+ DistanceCar, /* distance */
+ DescribeCar, /* describe */
+ DeleteCar, /* delete */
+ WriteCar, /* write */
+ ReadCar, /* read */
+ MoveCar, /* move */
+ RotateCar, /* rotate */
+ NULL, /* rescale */
+ NULL, /* audit */
+ NULL, /* getAngle */
+ NULL, /* split */
+ NULL, /* traverse */
+ NULL, /* enumerate */
+ NULL, /* redraw*/
+ NULL, /* trim*/
+ NULL, /* merge*/
+ NULL, /* modify */
+ NULL, /* getLength */
+ NULL, /* getParams */
+ NULL, /* moveEndPt */
+ QueryCar, /* query */
+ NULL, /* ungroup */
+ NULL, /* flip */
+};
/*
*
@@ -505,36 +554,37 @@ static int numTrainDlg;
#define MAX_SPEED (100.0)
typedef struct {
- wWin_p win;
- wIndex_t inx;
- track_p train;
- long direction;
- long followMe;
- long autoReverse;
- coOrd pos;
- char posS[STR_SHORT_SIZE];
- DIST_T speed;
- char speedS[10];
- paramGroup_p trainPGp;
- } trainControlDlg_t, * trainControlDlg_p;
+ wWin_p win;
+ wIndex_t inx;
+ track_p train;
+ long direction;
+ long followMe;
+ long autoReverse;
+ coOrd pos;
+ char posS[STR_SHORT_SIZE];
+ DIST_T speed;
+ char speedS[10];
+ paramGroup_p trainPGp;
+} trainControlDlg_t, * trainControlDlg_p;
static trainControlDlg_t * curTrainDlg;
-static void SpeedRedraw( wDraw_p, void *, wPos_t, wPos_t );
-static void SpeedAction( wAction_t, coOrd );
-static void LocoListChangeEntry( track_p, track_p );
-static void CmdTrainExit( void * );
+static void SpeedRedraw(wDraw_p, void *, wPos_t, wPos_t);
+static void SpeedAction(wAction_t, coOrd);
+static void LocoListChangeEntry(track_p, track_p);
+static void CmdTrainExit(void *);
drawCmd_t speedD = {
- NULL,
- &screenDrawFuncs,
- 0,
- 1.0,
- 0.0,
- { 0.0, 0.0 },
- { 0.0, 0.0 },
- Pix2CoOrd,
- CoOrd2Pix };
+ NULL,
+ &screenDrawFuncs,
+ 0,
+ 1.0,
+ 0.0,
+ { 0.0, 0.0 },
+ { 0.0, 0.0 },
+ Pix2CoOrd,
+ CoOrd2Pix
+};
static paramDrawData_t speedParamData = { SLIDER_WIDTH, SLIDER_HEIGHT, SpeedRedraw, SpeedAction, &speedD };
#ifndef WINDOWS
static paramListData_t listData = { 3, 120 };
@@ -544,52 +594,56 @@ static char * trainAutoReverseLabels[] = { N_("Auto Reverse"), NULL };
static paramData_t trainPLs[] = {
#define I_LIST (0)
#ifdef WINDOWS
-/*0*/ { PD_DROPLIST, NULL, "list", PDO_NOPREF|PDO_NOPSHUPD, (void*)120, NULL, 0 },
+ /*0*/ { PD_DROPLIST, NULL, "list", PDO_NOPREF|PDO_NOPSHUPD, (void*)120, NULL, 0 },
#else
-/*0*/ { PD_LIST, NULL, "list", PDO_NOPREF|PDO_NOPSHUPD, &listData, NULL, 0 },
+ /*0*/ { PD_LIST, NULL, "list", PDO_NOPREF|PDO_NOPSHUPD, &listData, NULL, 0 },
#endif
#define I_STATUS (1)
- { PD_MESSAGE, NULL, NULL, 0, (void*)120 },
+ { PD_MESSAGE, NULL, NULL, 0, (void*)120 },
#define I_POS (2)
- { PD_MESSAGE, NULL, NULL, 0, (void*)120 },
+ { PD_MESSAGE, NULL, NULL, 0, (void*)120 },
#define I_SLIDER (3)
- { PD_DRAW, NULL, "speed", PDO_NOPSHUPD|PDO_DLGSETY, &speedParamData },
+ { PD_DRAW, NULL, "speed", PDO_NOPSHUPD|PDO_DLGSETY, &speedParamData },
#define I_DIST (4)
- { PD_STRING, NULL, "distance", PDO_DLGNEWCOLUMN, (void*)(100-SLIDER_WIDTH), NULL, BO_READONLY },
+ { PD_STRING, NULL, "distance", PDO_DLGNEWCOLUMN, (void*)(100-SLIDER_WIDTH), NULL, BO_READONLY },
#define I_ZERO (5)
- { PD_BUTTON, NULL, "zeroDistance", PDO_NOPSHUPD|PDO_NOPREF|PDO_DLGHORZ, NULL, NULL, BO_ICON },
+ { PD_BUTTON, NULL, "zeroDistance", PDO_NOPSHUPD|PDO_NOPREF|PDO_DLGHORZ, NULL, NULL, BO_ICON },
#define I_GOTO (6)
- { PD_BUTTON, NULL, "goto", PDO_NOPSHUPD|PDO_NOPREF|PDO_DLGWIDE, NULL, N_("Find") },
+ { PD_BUTTON, NULL, "goto", PDO_NOPSHUPD|PDO_NOPREF|PDO_DLGWIDE, NULL, N_("Find") },
#define I_FOLLOW (7)
- { PD_TOGGLE, NULL, "follow", PDO_NOPREF|PDO_DLGWIDE, trainFollowMeLabels, NULL, BC_HORZ|BC_NOBORDER },
+ { PD_TOGGLE, NULL, "follow", PDO_NOPREF|PDO_DLGWIDE, trainFollowMeLabels, NULL, BC_HORZ|BC_NOBORDER },
#define I_AUTORVRS (8)
- { PD_TOGGLE, NULL, "autoreverse", PDO_NOPREF, trainAutoReverseLabels, NULL, BC_HORZ|BC_NOBORDER },
+ { PD_TOGGLE, NULL, "autoreverse", PDO_NOPREF, trainAutoReverseLabels, NULL, BC_HORZ|BC_NOBORDER },
#define I_DIR (9)
- { PD_BUTTON, NULL, "direction", PDO_NOPREF|PDO_DLGWIDE, NULL, N_("Forward"), 0 },
+ { PD_BUTTON, NULL, "direction", PDO_NOPREF|PDO_DLGWIDE, NULL, N_("Forward"), 0 },
#define I_STOP (10)
- { PD_BUTTON, NULL, "stop", PDO_DLGWIDE, NULL, N_("Stop") },
+ { PD_BUTTON, NULL, "stop", PDO_DLGWIDE, NULL, N_("Stop") },
#define I_SPEED (11)
- { PD_MESSAGE, NULL, NULL, PDO_DLGIGNOREX, (void *)120 } };
+ { PD_MESSAGE, NULL, NULL, PDO_DLGIGNOREX, (void *)120 }
+};
static paramGroup_t trainPG = { "train", 0, trainPLs, sizeof trainPLs/sizeof trainPLs[0] };
typedef struct {
- track_p loco;
- BOOL_T running;
- } locoList_t;
+ track_p loco;
+ BOOL_T running;
+} locoList_t;
dynArr_t locoList_da;
#define locoList(N) DYNARR_N( locoList_t, locoList_da, N )
static wIndex_t FindLoco(
- track_p loco )
+ track_p loco)
{
- wIndex_t inx;
- for ( inx = 0; inx<locoList_da.cnt; inx++ ) {
- if ( locoList(inx).loco == loco )
- return inx;
- }
- return -1;
+ wIndex_t inx;
+
+ for (inx = 0; inx<locoList_da.cnt; inx++) {
+ if (locoList(inx).loco == loco) {
+ return inx;
+ }
+ }
+
+ return -1;
}
/**
@@ -603,488 +657,591 @@ static wIndex_t FindLoco(
*/
static void SpeedRedraw(
- wDraw_p d,
- void * context,
- wPos_t w,
- wPos_t h )
+ wDraw_p d,
+ void * context,
+ wPos_t w,
+ wPos_t h)
{
- wPos_t y, pts[4][2];
- trainControlDlg_p dlg = (trainControlDlg_p)context;
- struct extraData * xx;
- wDrawColor drawColor;
-
- wDrawClear( d );
- if ( dlg == NULL || dlg->train == NULL ) return;
- xx = GetTrkExtraData( dlg->train );
- if ( xx->speed > MAX_SPEED )
- xx->speed = MAX_SPEED;
- if ( xx->speed < 0 )
- xx->speed = 0;
- y = (wPos_t)(xx->speed/MAX_SPEED*((SLIDER_HEIGHT-SLIDER_THICKNESS))+SLIDER_THICKNESS/2);
-
- drawColor = wDrawFindColor( wRGB( 160, 160, 160) );
- pts[0][1] = pts[1][1] = y-SLIDER_THICKNESS/2;
- pts[2][1] = pts[3][1] = y+SLIDER_THICKNESS/2;
- pts[0][0] = pts[3][0] = 0;
- pts[1][0] = pts[2][0] = SLIDER_WIDTH;
- wDrawFilledPolygon( d, pts, 4, drawColor, 0 );
-
- drawColor = wDrawFindColor( wRGB( 220, 220, 220) );
- pts[0][1] = pts[1][1] = y+SLIDER_THICKNESS/2;
- pts[2][1] = pts[3][1] = y;
- pts[0][0] = pts[3][0] = 0;
- pts[1][0] = pts[2][0] = SLIDER_WIDTH;
- wDrawFilledPolygon( d, pts, 4, drawColor, 0 );
-
- wDrawLine( d, 0, y, SLIDER_WIDTH, y, 1, wDrawLineSolid, drawColorRed, 0 );
- wDrawLine( d, 0, y+SLIDER_THICKNESS/2, SLIDER_WIDTH, y+SLIDER_THICKNESS/2, 1, wDrawLineSolid, drawColorBlack, 0 );
- wDrawLine( d, 0, y-SLIDER_THICKNESS/2, SLIDER_WIDTH, y-SLIDER_THICKNESS/2, 1, wDrawLineSolid, drawColorBlack, 0 );
-
- sprintf( dlg->speedS, "%3d %s", (int)(units==UNITS_ENGLISH?xx->speed:xx->speed*1.6), (units==UNITS_ENGLISH?"mph":"km/h") );
- ParamLoadMessage( dlg->trainPGp, I_SPEED, dlg->speedS );
- LOG( log_trainPlayback, 3, ( "Speed = %d\n", (int)xx->speed ) );
+ wPos_t y, pts[4][2];
+ trainControlDlg_p dlg = (trainControlDlg_p)context;
+ struct extraData * xx;
+ wDrawColor drawColor;
+ wDrawClear(d);
+
+ if (dlg == NULL || dlg->train == NULL) {
+ return;
+ }
+
+ xx = GetTrkExtraData(dlg->train);
+
+ if (xx->speed > MAX_SPEED) {
+ xx->speed = MAX_SPEED;
+ }
+
+ if (xx->speed < 0) {
+ xx->speed = 0;
+ }
+
+ y = (wPos_t)(xx->speed/MAX_SPEED*((SLIDER_HEIGHT-SLIDER_THICKNESS))
+ +SLIDER_THICKNESS/2);
+ drawColor = wDrawFindColor(wRGB(160, 160, 160));
+ pts[0][1] = pts[1][1] = y-SLIDER_THICKNESS/2;
+ pts[2][1] = pts[3][1] = y+SLIDER_THICKNESS/2;
+ pts[0][0] = pts[3][0] = 0;
+ pts[1][0] = pts[2][0] = SLIDER_WIDTH;
+ wDrawFilledPolygon(d, pts, 4, drawColor, 0);
+ drawColor = wDrawFindColor(wRGB(220, 220, 220));
+ pts[0][1] = pts[1][1] = y+SLIDER_THICKNESS/2;
+ pts[2][1] = pts[3][1] = y;
+ pts[0][0] = pts[3][0] = 0;
+ pts[1][0] = pts[2][0] = SLIDER_WIDTH;
+ wDrawFilledPolygon(d, pts, 4, drawColor, 0);
+ wDrawLine(d, 0, y, SLIDER_WIDTH, y, 1, wDrawLineSolid, drawColorRed, 0);
+ wDrawLine(d, 0, y+SLIDER_THICKNESS/2, SLIDER_WIDTH, y+SLIDER_THICKNESS/2, 1,
+ wDrawLineSolid, drawColorBlack, 0);
+ wDrawLine(d, 0, y-SLIDER_THICKNESS/2, SLIDER_WIDTH, y-SLIDER_THICKNESS/2, 1,
+ wDrawLineSolid, drawColorBlack, 0);
+ sprintf(dlg->speedS, "%3d %s",
+ (int)(units==UNITS_ENGLISH?xx->speed:xx->speed*1.6),
+ (units==UNITS_ENGLISH?"mph":"km/h"));
+ ParamLoadMessage(dlg->trainPGp, I_SPEED, dlg->speedS);
+ LOG(log_trainPlayback, 3, ("Speed = %d\n", (int)xx->speed));
}
static void SpeedAction(
- wAction_t action,
- coOrd pos )
+ wAction_t action,
+ coOrd pos)
{
- /*trainControlDlg_p dlg = (trainControlDlg_p)wDrawGetContext(d);*/
- trainControlDlg_p dlg = curTrainDlg;
- struct extraData * xx;
- FLOAT_T speed;
- BOOL_T startStop;
- if ( dlg == NULL || dlg->train == NULL )
- return;
- xx = GetTrkExtraData( dlg->train );
- switch ( action ) {
- case C_DOWN:
- InfoMessage( "" );
- case C_MOVE:
- case C_UP:
- TrainTimeEndPause();
- if ( IsOnTrack(xx) ) {
- speed = ((FLOAT_T)((pos.y*speedD.dpi)-SLIDER_THICKNESS/2))/(SLIDER_HEIGHT-SLIDER_THICKNESS)*MAX_SPEED;
- } else {
- speed = 0;
- }
- if ( speed > MAX_SPEED )
- speed = MAX_SPEED;
- if ( speed < 0 )
- speed = 0;
- startStop = (xx->speed == 0) != (speed == 0);
- xx->speed = speed;
- SpeedRedraw( (wDraw_p)dlg->trainPGp->paramPtr[I_SLIDER].control, dlg, SLIDER_WIDTH, SLIDER_HEIGHT );
- if ( startStop ) {
- if ( xx->speed == 0 )
- xx->status = ST_StopManual;
- LocoListChangeEntry( dlg->train, dlg->train );
- }
- TrainTimeStartPause();
- if ( trainsState == TRAINS_IDLE )
- RestartTrains();
- break;
- default:
- break;
- }
-}
+ trainControlDlg_p dlg = curTrainDlg;
+ struct extraData * xx;
+ FLOAT_T speed;
+ BOOL_T startStop;
+ if (dlg == NULL || dlg->train == NULL) {
+ return;
+ }
-static void ControllerDialogSync(
- trainControlDlg_p dlg )
-{
- struct extraData * xx=NULL;
- wIndex_t inx;
- BOOL_T dir;
- BOOL_T followMe;
- BOOL_T autoReverse;
- DIST_T speed;
- coOrd pos;
- char * statusMsg;
- long format;
-
- if ( dlg == NULL ) return;
-
- inx = wListGetIndex( (wList_p)dlg->trainPGp->paramPtr[I_LIST].control );
- if ( dlg->train ) {
- if ( inx >= 0 && inx < locoList_da.cnt && dlg->train && dlg->train != locoList(inx).loco ) {
- inx = FindLoco( dlg->train );
- if ( inx >= 0 ) {
- wListSetIndex( (wList_p)dlg->trainPGp->paramPtr[I_LIST].control, inx );
- }
- }
- } else {
- wListSetIndex( (wList_p)dlg->trainPGp->paramPtr[I_LIST].control, -1 );
- }
+ xx = GetTrkExtraData(dlg->train);
- if ( dlg->train ) {
- xx = GetTrkExtraData(dlg->train);
- dir = xx->direction==0?0:1;
- speed = xx->speed;
- pos = xx->trvTrk.pos;
- followMe = followTrain == dlg->train;
- autoReverse = xx->autoReverse;
- if ( xx->trvTrk.trk == NULL ) {
- if ( xx->status == ST_Crashed )
- statusMsg = _("Crashed");
- else
- statusMsg = _("Not on Track");
- } else if ( xx->speed > 0 ) {
- if ( trainsState == TRAINS_STOP )
- statusMsg = _("Trains Paused");
- else
- statusMsg = _("Running");
- } else {
- switch (xx->status ) {
- case ST_EndOfTrack:
- statusMsg = _("End of Track");
- break;
- case ST_OpenTurnout:
- statusMsg = _("Open Turnout");
- break;
- case ST_StopManual:
- statusMsg = _("Manual Stop");
- break;
- case ST_NoRoom:
- statusMsg = _("No Room");
- break;
- case ST_Crashed:
- statusMsg = _("Crashed");
- break;
- default:
- statusMsg = _("Unknown Status");
- break;
- }
- }
- ParamLoadMessage( dlg->trainPGp, I_STATUS, statusMsg );
- } else {
- dir = 0;
- followMe = FALSE;
- autoReverse = FALSE;
- ParamLoadMessage( dlg->trainPGp, I_STATUS, _("No trains") );
- }
- if ( dlg->followMe != followMe ) {
- dlg->followMe = followMe;
- ParamLoadControl( dlg->trainPGp, I_FOLLOW );
- }
- if ( dlg->autoReverse != autoReverse ) {
- dlg->autoReverse = autoReverse;
- ParamLoadControl( dlg->trainPGp, I_AUTORVRS );
- }
- if ( dlg->direction != dir ) {
- dlg->direction = dir;
- wButtonSetLabel( (wButton_p)dlg->trainPGp->paramPtr[I_DIR].control, (dlg->direction?_("Reverse"):_("Forward")) );
- }
- if ( dlg->train ) {
- if ( dlg->posS[0] == '\0' ||
- dlg->pos.x != xx->trvTrk.pos.x ||
- dlg->pos.y != xx->trvTrk.pos.y ) {
- dlg->pos = xx->trvTrk.pos;
- format = GetDistanceFormat();
- format &= ~DISTFMT_DECS;
- sprintf( dlg->posS, "X:%s Y:%s",
- FormatDistanceEx( xx->trvTrk.pos.x, format ),
- FormatDistanceEx( xx->trvTrk.pos.y, format ) );
- ParamLoadMessage( dlg->trainPGp, I_POS, dlg->posS );
- }
- if ( dlg->speed != xx->speed ) {
- dlg->speed = xx->speed;
- sprintf( dlg->speedS, "%3d", (int)(units==UNITS_ENGLISH?xx->speed:xx->speed*1.6) );
- ParamLoadMessage( dlg->trainPGp, I_SPEED, dlg->speedS );
- SpeedRedraw( (wDraw_p)dlg->trainPGp->paramPtr[I_SLIDER].control, dlg, SLIDER_WIDTH, SLIDER_HEIGHT );
- }
- ParamLoadMessage( dlg->trainPGp, I_DIST, FormatDistance(xx->distance) );
- } else {
- if ( dlg->posS[0] != '\0' ) {
- dlg->posS[0] = '\0';
- ParamLoadMessage( dlg->trainPGp, I_POS, dlg->posS );
- }
- if ( dlg->speed >= 0 ) {
- dlg->speed = -1;
- dlg->speedS[0] = '\0';
- ParamLoadMessage( dlg->trainPGp, I_SPEED, dlg->speedS );
- wDrawClear( (wDraw_p)dlg->trainPGp->paramPtr[I_SLIDER].control );
- }
- ParamLoadMessage( dlg->trainPGp, I_DIST, "" );
- }
+ switch (action) {
+ case C_DOWN:
+ InfoMessage("");
+
+ case C_MOVE:
+ case C_UP:
+ TrainTimeEndPause();
+
+ if (IsOnTrack(xx)) {
+ speed = ((FLOAT_T)((pos.y*speedD.dpi)-SLIDER_THICKNESS/2))/
+ (SLIDER_HEIGHT-SLIDER_THICKNESS)*MAX_SPEED;
+ } else {
+ speed = 0;
+ }
+
+ if (speed > MAX_SPEED) {
+ speed = MAX_SPEED;
+ }
+
+ if (speed < 0) {
+ speed = 0;
+ }
+
+ startStop = (xx->speed == 0) != (speed == 0);
+ xx->speed = speed;
+ SpeedRedraw((wDraw_p)dlg->trainPGp->paramPtr[I_SLIDER].control, dlg,
+ SLIDER_WIDTH, SLIDER_HEIGHT);
+
+ if (startStop) {
+ if (xx->speed == 0) {
+ xx->status = ST_StopManual;
+ }
+
+ LocoListChangeEntry(dlg->train, dlg->train);
+ }
+
+ TrainTimeStartPause();
+
+ if (trainsState == TRAINS_IDLE) {
+ RestartTrains();
+ }
+
+ break;
+
+ default:
+ break;
+ }
}
-static void ControllerDialogSyncAll( void )
+static void ControllerDialogSync(
+ trainControlDlg_p dlg)
+{
+ struct extraData * xx=NULL;
+ wIndex_t inx;
+ BOOL_T dir;
+ BOOL_T followMe;
+ BOOL_T autoReverse;
+ coOrd pos;
+
+ if (dlg == NULL) {
+ return;
+ }
+
+ inx = wListGetIndex((wList_p)dlg->trainPGp->paramPtr[I_LIST].control);
+
+ if (dlg->train) {
+ if (inx >= 0 && inx < locoList_da.cnt && dlg->train &&
+ dlg->train != locoList(inx).loco) {
+ inx = FindLoco(dlg->train);
+
+ if (inx >= 0) {
+ wListSetIndex((wList_p)dlg->trainPGp->paramPtr[I_LIST].control, inx);
+ }
+ }
+ } else {
+ wListSetIndex((wList_p)dlg->trainPGp->paramPtr[I_LIST].control, -1);
+ }
+
+ if (dlg->train) {
+ char * statusMsg;
+ DIST_T speed;
+ xx = GetTrkExtraData(dlg->train);
+ dir = xx->direction==0?0:1;
+ speed = xx->speed;
+ pos = xx->trvTrk.pos;
+ followMe = followTrain == dlg->train;
+ autoReverse = xx->autoReverse;
+
+ if (xx->trvTrk.trk == NULL) {
+ if (xx->status == ST_Crashed) {
+ statusMsg = _("Crashed");
+ } else {
+ statusMsg = _("Not on Track");
+ }
+ } else if (xx->speed > 0) {
+ if (trainsState == TRAINS_STOP) {
+ statusMsg = _("Trains Paused");
+ } else {
+ statusMsg = _("Running");
+ }
+ } else {
+ switch (xx->status) {
+ case ST_EndOfTrack:
+ statusMsg = _("End of Track");
+ break;
+
+ case ST_OpenTurnout:
+ statusMsg = _("Open Turnout");
+ break;
+
+ case ST_StopManual:
+ statusMsg = _("Manual Stop");
+ break;
+
+ case ST_NoRoom:
+ statusMsg = _("No Room");
+ break;
+
+ case ST_Crashed:
+ statusMsg = _("Crashed");
+ break;
+
+ default:
+ statusMsg = _("Unknown Status");
+ break;
+ }
+ }
+
+ ParamLoadMessage(dlg->trainPGp, I_STATUS, statusMsg);
+ } else {
+ dir = 0;
+ followMe = FALSE;
+ autoReverse = FALSE;
+ ParamLoadMessage(dlg->trainPGp, I_STATUS, _("No trains"));
+ }
+
+ if (dlg->followMe != followMe) {
+ dlg->followMe = followMe;
+ ParamLoadControl(dlg->trainPGp, I_FOLLOW);
+ }
+
+ if (dlg->autoReverse != autoReverse) {
+ dlg->autoReverse = autoReverse;
+ ParamLoadControl(dlg->trainPGp, I_AUTORVRS);
+ }
+
+ if (dlg->direction != dir) {
+ dlg->direction = dir;
+ wButtonSetLabel((wButton_p)dlg->trainPGp->paramPtr[I_DIR].control,
+ (dlg->direction?_("Reverse"):_("Forward")));
+ }
+
+ if (dlg->train) {
+ if (dlg->posS[0] == '\0' ||
+ dlg->pos.x != xx->trvTrk.pos.x ||
+ dlg->pos.y != xx->trvTrk.pos.y) {
+ long format;
+ dlg->pos = xx->trvTrk.pos;
+ format = GetDistanceFormat();
+ format &= ~DISTFMT_DECS;
+ sprintf(dlg->posS, "X:%s Y:%s",
+ FormatDistanceEx(xx->trvTrk.pos.x, format),
+ FormatDistanceEx(xx->trvTrk.pos.y, format));
+ ParamLoadMessage(dlg->trainPGp, I_POS, dlg->posS);
+ }
+
+ if (dlg->speed != xx->speed) {
+ dlg->speed = xx->speed;
+ sprintf(dlg->speedS, "%3d",
+ (int)(units==UNITS_ENGLISH?xx->speed:xx->speed*1.6));
+ ParamLoadMessage(dlg->trainPGp, I_SPEED, dlg->speedS);
+ SpeedRedraw((wDraw_p)dlg->trainPGp->paramPtr[I_SLIDER].control, dlg,
+ SLIDER_WIDTH, SLIDER_HEIGHT);
+ }
+
+ ParamLoadMessage(dlg->trainPGp, I_DIST, FormatDistance(xx->distance));
+ } else {
+ if (dlg->posS[0] != '\0') {
+ dlg->posS[0] = '\0';
+ ParamLoadMessage(dlg->trainPGp, I_POS, dlg->posS);
+ }
+
+ if (dlg->speed >= 0) {
+ dlg->speed = -1;
+ dlg->speedS[0] = '\0';
+ ParamLoadMessage(dlg->trainPGp, I_SPEED, dlg->speedS);
+ wDrawClear((wDraw_p)dlg->trainPGp->paramPtr[I_SLIDER].control);
+ }
+
+ ParamLoadMessage(dlg->trainPGp, I_DIST, "");
+ }
+}
+
+
+static void ControllerDialogSyncAll(void)
{
- if ( curTrainDlg )
- ControllerDialogSync( curTrainDlg );
+ if (curTrainDlg) {
+ ControllerDialogSync(curTrainDlg);
+ }
}
static void LocoListChangeEntry(
- track_p oldLoco,
- track_p newLoco )
+ track_p oldLoco,
+ track_p newLoco)
{
- wIndex_t inx = -1;
- struct extraData * xx;
-
- if ( curTrainDlg == NULL )
- return;
- if ( oldLoco && (inx=FindLoco(oldLoco))>=0 ) {
- if ( newLoco ) {
- xx = GetTrkExtraData(newLoco);
- locoList(inx).loco = newLoco;
- xx = GetTrkExtraData(newLoco);
- locoList(inx).running = IsOnTrack(xx) && xx->speed > 0;
- wListSetValues( (wList_p)curTrainDlg->trainPGp->paramPtr[I_LIST].control, inx, CarItemNumber(xx->item), locoList(inx).running?goI:stopI, newLoco );
- } else {
- wListDelete( (wList_p)curTrainDlg->trainPGp->paramPtr[I_LIST].control, inx );
- for ( ; inx<locoList_da.cnt-1; inx++ )
- locoList(inx) = locoList(inx+1);
- locoList_da.cnt -= 1;
- if ( inx >= locoList_da.cnt )
- inx--;
- }
- } else if ( newLoco ){
- inx = locoList_da.cnt;
- DYNARR_APPEND( locoList_t, locoList_da, 10 );
- locoList(inx).loco = newLoco;
- xx = GetTrkExtraData(newLoco);
- locoList(inx).running = IsOnTrack(xx) && xx->speed > 0;
- wListAddValue( (wList_p)curTrainDlg->trainPGp->paramPtr[I_LIST].control, CarItemNumber(xx->item), locoList(inx).running?goI:stopI, newLoco );
- }
- if ( curTrainDlg->train == oldLoco ) {
- if ( newLoco || locoList_da.cnt <= 0 ) {
- curTrainDlg->train = newLoco;
- } else {
- curTrainDlg->train = wListGetItemContext( (wList_p)curTrainDlg->trainPGp->paramPtr[I_LIST].control, inx );
- }
- }
- ControllerDialogSync( curTrainDlg );
-}
+ wIndex_t inx = -1;
+ struct extraData * xx;
+
+ if (curTrainDlg == NULL) {
+ return;
+ }
+
+ if (oldLoco && (inx=FindLoco(oldLoco))>=0) {
+ if (newLoco) {
+ locoList(inx).loco = newLoco;
+ xx = GetTrkExtraData(newLoco);
+ locoList(inx).running = IsOnTrack(xx) && xx->speed > 0;
+ wListSetValues((wList_p)curTrainDlg->trainPGp->paramPtr[I_LIST].control, inx,
+ CarItemNumber(xx->item), locoList(inx).running?goI:stopI, newLoco);
+ } else {
+ wListDelete((wList_p)curTrainDlg->trainPGp->paramPtr[I_LIST].control, inx);
+
+ for (; inx<locoList_da.cnt-1; inx++) {
+ locoList(inx) = locoList(inx+1);
+ }
+
+ locoList_da.cnt -= 1;
+
+ if (inx >= locoList_da.cnt) {
+ inx--;
+ }
+ }
+ } else if (newLoco) {
+ inx = locoList_da.cnt;
+ DYNARR_APPEND(locoList_t, locoList_da, 10);
+ locoList(inx).loco = newLoco;
+ xx = GetTrkExtraData(newLoco);
+ locoList(inx).running = IsOnTrack(xx) && xx->speed > 0;
+ wListAddValue((wList_p)curTrainDlg->trainPGp->paramPtr[I_LIST].control,
+ CarItemNumber(xx->item), locoList(inx).running?goI:stopI, newLoco);
+ }
+
+ if (curTrainDlg->train == oldLoco) {
+ if (newLoco || locoList_da.cnt <= 0) {
+ curTrainDlg->train = newLoco;
+ } else {
+ curTrainDlg->train = wListGetItemContext((wList_p)
+ curTrainDlg->trainPGp->paramPtr[I_LIST].control, inx);
+ }
+ }
+
+ ControllerDialogSync(curTrainDlg);
+}
+
+
+static void LocoListInit(void)
+{
+ track_p train;
+ struct extraData * xx;
+ locoList_da.cnt = 0;
+ for (train=NULL; TrackIterate(&train);) {
+ if (GetTrkType(train) != T_CAR) {
+ continue;
+ }
-static void LocoListInit( void )
-{
- track_p train;
- struct extraData * xx;
-
- locoList_da.cnt = 0;
- for ( train=NULL; TrackIterate( &train ); ) {
- if ( GetTrkType(train) != T_CAR ) continue;
- xx = GetTrkExtraData(train);
- if ( !CarItemIsLoco(xx->item) ) continue;
- if ( !IsLocoMaster(xx) ) continue;
- LocoListChangeEntry( NULL, train );
- }
-}
+ xx = GetTrkExtraData(train);
+ if (!CarItemIsLoco(xx->item)) {
+ continue;
+ }
-#ifdef LATER
-static void LoadTrainDlgIndex(
- trainControlDlg_p dlg )
-{
- track_p car;
- struct extraData * xx;
-
- wListClear( (wList_p)dlg->trainPGp->paramPtr[I_LIST].control );
- for ( car=NULL; TrackIterate( &car ); ) {
- if ( GetTrkType(car) != T_CAR ) continue;
- xx = GetTrkExtraData(car);
- if ( !CarItemIsLoco(xx->item) ) continue;
- if ( !IsLocoMaster(xx) ) continue;
- wListAddValue( (wList_p)dlg->trainPGp->paramPtr[I_LIST].control, CarItemNumber(xx->item), xx->speed>0?goI:stopI, car );
- }
- TrainDialogSetIndex( dlg );
- ControllerDialogSync( curTrainDlg );
+ if (!IsLocoMaster(xx)) {
+ continue;
+ }
+
+ LocoListChangeEntry(NULL, train);
+ }
}
-#endif
static void SetCurTrain(
- track_p train )
+ track_p train)
{
- curTrainDlg->train = train;
- ControllerDialogSync( curTrainDlg );
+ curTrainDlg->train = train;
+ ControllerDialogSync(curTrainDlg);
}
static void StopTrain(
- track_p train,
- trainStatus_e status )
+ track_p train,
+ trainStatus_e status)
{
- struct extraData * xx;
-
- if ( train == NULL )
- return;
- xx = GetTrkExtraData(train);
- xx->speed = 0;
- xx->status = status;
- LocoListChangeEntry( train, train );
+ struct extraData * xx;
+
+ if (train == NULL) {
+ return;
+ }
+
+ xx = GetTrkExtraData(train);
+ xx->speed = 0;
+ xx->status = status;
+ LocoListChangeEntry(train, train);
}
static void MoveMainWindow(
- coOrd pos,
- ANGLE_T angle )
+ coOrd pos,
+ ANGLE_T angle)
{
- DIST_T dist;
- static DIST_T factor = 0.5;
- ANGLE_T angle1 = angle, angle2;
- if ( angle1 > 180.0)
- angle1 = 360.0 - angle1;
- if ( angle1 > 90.0)
- angle1 = 180.0 - angle1;
- angle2 = R2D(atan2(mainD.size.x,mainD.size.y));
- if ( angle1 < angle2 )
- dist = mainD.size.y/2.0/cos(D2R(angle1));
- else
- dist = mainD.size.x/2.0/cos(D2R(90.0-angle1));
- dist *= factor;
- Translate( &pos, pos, angle, dist );
- DrawMapBoundingBox( FALSE );
- mainCenter = pos;
- mainD.orig.x = pos.x-mainD.size.x/2;;
- mainD.orig.y = pos.y-mainD.size.y/2;;
- MainRedraw();
- DrawMapBoundingBox( TRUE );
+ DIST_T dist;
+ static DIST_T factor = 0.5;
+ ANGLE_T angle1 = angle, angle2;
+
+ if (angle1 > 180.0) {
+ angle1 = 360.0 - angle1;
+ }
+
+ if (angle1 > 90.0) {
+ angle1 = 180.0 - angle1;
+ }
+
+ angle2 = R2D(atan2(mainD.size.x,mainD.size.y));
+
+ if (angle1 < angle2) {
+ dist = mainD.size.y/2.0/cos(D2R(angle1));
+ } else {
+ dist = mainD.size.x/2.0/cos(D2R(90.0-angle1));
+ }
+
+ dist *= factor;
+ Translate(&pos, pos, angle, dist);
+ //DrawMapBoundingBox(FALSE);
+ mainCenter = pos;
+ mainD.orig.x = pos.x-mainD.size.x/2;;
+ mainD.orig.y = pos.y-mainD.size.y/2;;
+ MainRedraw();
+ MapRedraw();
+ //DrawMapBoundingBox(TRUE);
}
static void SetTrainDirection(
- track_p train )
+ track_p train)
{
- struct extraData *xx, *xx0=GetTrkExtraData(train);
- int dir, dir0;
- track_p car;
-
- car = train;
- for ( dir0 = 0; dir0 < 2; dir0++ ) {
- dir = dir0;
- WALK_CARS_START( car, xx, dir )
- if ( car != train ) {
- if ( CarItemIsLoco(xx->item) ) {
- xx->direction = (dir==dir0?xx0->direction:!xx0->direction);
- }
- }
- WALK_CARS_END( car, xx, dir )
- }
-}
+ struct extraData *xx, *xx0=GetTrkExtraData(train);
+ int dir0;
+ track_p car;
+ car = train;
+ for (dir0 = 0; dir0 < 2; dir0++) {
+ int dir;
+ dir = dir0;
+ WALK_CARS_START(car, xx, dir)
-static void ControllerDialogUpdate(
- paramGroup_p pg,
- int inx,
- void * valueP )
-{
- trainControlDlg_p dlg = curTrainDlg;
- track_p train;
- struct extraData * xx;
-
- if ( dlg == NULL )
- return;
-
- TrainTimeEndPause();
- switch (inx) {
- case I_LIST:
- train = (track_p)wListGetItemContext( (wList_p)pg->paramPtr[inx].control, (wIndex_t)*(long*)valueP );
- if ( train == NULL ) return;
- dlg->train = train;
- ControllerDialogSync( dlg );
- break;
- case I_ZERO:
- if ( dlg->train == NULL ) return;
- TrainTimeEndPause();
- xx = GetTrkExtraData( dlg->train );
- xx->distance = 0.0;
- ParamLoadMessage( dlg->trainPGp, I_DIST, FormatDistance(xx->distance) );
- ParamLoadControl( curTrainDlg->trainPGp, I_DIST );
- TrainTimeStartPause();
- break;
- case I_GOTO:
- if ( dlg->train == NULL ) return;
- TrainTimeEndPause();
- xx = GetTrkExtraData( dlg->train );
- followTrain = NULL;
- dlg->followMe = FALSE;
- ParamLoadControl( curTrainDlg->trainPGp, I_FOLLOW );
- CarSetVisible( dlg->train );
- MoveMainWindow( xx->trvTrk.pos, xx->trvTrk.angle );
- TrainTimeStartPause();
- break;
- case I_FOLLOW:
- if ( dlg->train == NULL ) return;
- if ( *(long*)valueP ) {
- followTrain = dlg->train;
- xx = GetTrkExtraData(dlg->train);
- if ( OFF_MAIND( xx->trvTrk.pos, xx->trvTrk.pos ) ) {
- MoveMainWindow( xx->trvTrk.pos, xx->trvTrk.angle );
- }
- followCenter = mainCenter;
- } else {
- followTrain = NULL;
- }
- break;
- case I_AUTORVRS:
- if ( dlg->train == NULL ) return;
- xx = GetTrkExtraData(dlg->train);
- xx->autoReverse = *(long*)valueP!=0;
- break;
- case I_DIR:
- if ( dlg->train == NULL ) return;
- xx = GetTrkExtraData(dlg->train);
- dlg->direction = xx->direction = !xx->direction;
- wButtonSetLabel( (wButton_p)pg->paramPtr[I_DIR].control, (dlg->direction?_("Reverse"):_("Forward")) );
- SetTrainDirection( dlg->train );
- DrawAllCars();
- break;
- case I_STOP:
- if ( dlg->train == NULL ) return;
- TrainTimeEndPause();
- StopTrain( dlg->train, ST_StopManual );
- TrainTimeStartPause();
- break;
- case -1:
- /* Close window */
- CmdTrainExit( NULL );
- break;
- }
- /*ControllerDialogSync( dlg );*/
- TrainTimeStartPause();
+ if (car != train) {
+ if (CarItemIsLoco(xx->item)) {
+ xx->direction = (dir==dir0?xx0->direction:!xx0->direction);
+ }
+ }
+
+ WALK_CARS_END(car, xx, dir)
+ }
}
-static trainControlDlg_p CreateTrainControlDlg( void )
+static void ControllerDialogUpdate(
+ paramGroup_p pg,
+ int inx,
+ void * valueP)
{
- trainControlDlg_p dlg;
- char * title;
- paramData_p PLp;
- dlg = (trainControlDlg_p)MyMalloc( sizeof *dlg );
-#ifdef LATER
- PLp = (paramData_p)MyMalloc( sizeof trainPLs );
- memcpy( PLp, trainPLs, sizeof trainPLs );
-#endif
- PLp = trainPLs;
- dlg->posS[0] = '\0';
- dlg->speedS[0] = '\0';
- PLp[I_LIST].valueP = &dlg->inx;
- PLp[I_LIST].context = dlg;
- PLp[I_POS].valueP = &dlg->posS;
- PLp[I_POS].context = dlg;
- /*PLp[I_GOTO].valueP = NULL;*/
- PLp[I_GOTO].context = dlg;
- PLp[I_SLIDER].context = dlg;
- PLp[I_SPEED].valueP = &dlg->speedS;
- PLp[I_SPEED].context = dlg;
- PLp[I_DIR].context = dlg;
- /*PLp[I_STOP].valueP = NULL;*/
- PLp[I_STOP].context = dlg;
- PLp[I_FOLLOW].valueP = &dlg->followMe;
- PLp[I_FOLLOW].context = dlg;
- PLp[I_AUTORVRS].valueP = &dlg->autoReverse;
- PLp[I_AUTORVRS].context = dlg;
- title = MyStrdup( _("Train Control XXX") );
- sprintf( title, _("Train Control %d"), ++numTrainDlg );
- dlg->trainPGp = &trainPG;
- dlg->win = ParamCreateDialog( dlg->trainPGp, _("Train Control"), NULL, NULL, NULL, FALSE, NULL, 0, ControllerDialogUpdate );
- return dlg;
+ trainControlDlg_p dlg = curTrainDlg;
+ track_p train;
+ struct extraData * xx;
+
+ if (dlg == NULL) {
+ return;
+ }
+
+ TrainTimeEndPause();
+
+ switch (inx) {
+ case I_LIST:
+ train = (track_p)wListGetItemContext((wList_p)pg->paramPtr[inx].control,
+ (wIndex_t)*(long*)valueP);
+
+ if (train == NULL) {
+ return;
+ }
+
+ dlg->train = train;
+ ControllerDialogSync(dlg);
+ break;
+
+ case I_ZERO:
+ if (dlg->train == NULL) {
+ return;
+ }
+
+ TrainTimeEndPause();
+ xx = GetTrkExtraData(dlg->train);
+ xx->distance = 0.0;
+ ParamLoadMessage(dlg->trainPGp, I_DIST, FormatDistance(xx->distance));
+ ParamLoadControl(curTrainDlg->trainPGp, I_DIST);
+ TrainTimeStartPause();
+ break;
+
+ case I_GOTO:
+ if (dlg->train == NULL) {
+ return;
+ }
+
+ TrainTimeEndPause();
+ xx = GetTrkExtraData(dlg->train);
+ followTrain = NULL;
+ dlg->followMe = FALSE;
+ ParamLoadControl(curTrainDlg->trainPGp, I_FOLLOW);
+ CarSetVisible(dlg->train);
+ MoveMainWindow(xx->trvTrk.pos, xx->trvTrk.angle);
+ TrainTimeStartPause();
+ break;
+
+ case I_FOLLOW:
+ if (dlg->train == NULL) {
+ return;
+ }
+
+ if (*(long*)valueP) {
+ followTrain = dlg->train;
+ xx = GetTrkExtraData(dlg->train);
+
+ if (OFF_MAIND(xx->trvTrk.pos, xx->trvTrk.pos)) {
+ MoveMainWindow(xx->trvTrk.pos, xx->trvTrk.angle);
+ }
+
+ followCenter = mainCenter;
+ } else {
+ followTrain = NULL;
+ }
+
+ break;
+
+ case I_AUTORVRS:
+ if (dlg->train == NULL) {
+ return;
+ }
+
+ xx = GetTrkExtraData(dlg->train);
+ xx->autoReverse = *(long*)valueP!=0;
+ break;
+
+ case I_DIR:
+ if (dlg->train == NULL) {
+ return;
+ }
+
+ xx = GetTrkExtraData(dlg->train);
+ dlg->direction = xx->direction = !xx->direction;
+ wButtonSetLabel((wButton_p)pg->paramPtr[I_DIR].control,
+ (dlg->direction?_("Reverse"):_("Forward")));
+ SetTrainDirection(dlg->train);
+ DrawAllCars();
+ break;
+
+ case I_STOP:
+ if (dlg->train == NULL) {
+ return;
+ }
+
+ TrainTimeEndPause();
+ StopTrain(dlg->train, ST_StopManual);
+ TrainTimeStartPause();
+ break;
+
+ case -1:
+ /* Close window */
+ CmdTrainExit(NULL);
+ break;
+ }
+
+ /*ControllerDialogSync( dlg );*/
+ TrainTimeStartPause();
+}
+
+
+static trainControlDlg_p CreateTrainControlDlg(void)
+{
+ trainControlDlg_p dlg;
+ char * title;
+ paramData_p PLp;
+ dlg = (trainControlDlg_p)MyMalloc(sizeof *dlg);
+ PLp = trainPLs;
+ dlg->posS[0] = '\0';
+ dlg->speedS[0] = '\0';
+ PLp[I_LIST].valueP = &dlg->inx;
+ PLp[I_LIST].context = dlg;
+ PLp[I_POS].valueP = &dlg->posS;
+ PLp[I_POS].context = dlg;
+ /*PLp[I_GOTO].valueP = NULL;*/
+ PLp[I_GOTO].context = dlg;
+ PLp[I_SLIDER].context = dlg;
+ PLp[I_SPEED].valueP = &dlg->speedS;
+ PLp[I_SPEED].context = dlg;
+ PLp[I_DIR].context = dlg;
+ /*PLp[I_STOP].valueP = NULL;*/
+ PLp[I_STOP].context = dlg;
+ PLp[I_FOLLOW].valueP = &dlg->followMe;
+ PLp[I_FOLLOW].context = dlg;
+ PLp[I_AUTORVRS].valueP = &dlg->autoReverse;
+ PLp[I_AUTORVRS].context = dlg;
+ title = MyStrdup(_("Train Control XXX"));
+ sprintf(title, _("Train Control %d"), ++numTrainDlg);
+ dlg->trainPGp = &trainPG;
+ dlg->win = ParamCreateDialog(dlg->trainPGp, _("Train Control"), NULL, NULL,
+ NULL, FALSE, NULL, 0, ControllerDialogUpdate);
+ return dlg;
}
@@ -1094,920 +1251,1141 @@ static trainControlDlg_p CreateTrainControlDlg( void )
*/
static struct {
- STATE_T state;
- coOrd pos0;
- } Dtrain;
+ STATE_T state;
+ coOrd pos0;
+} Dtrain;
-EXPORT long trainPause = 200;
+long trainPause = 200;
static track_p followTrain = NULL;
-/*static int suppressTrainRedraw = 0;*/
-static long setTimeD;
-
-
-
-#ifdef MEMCHECK
-static BOOL_T drawAllCarsDisable;
-static void * top1, * top2;
-static long drawCounter;
-#endif
-static void DrawAllCars( void )
+static void DrawAllCars(void)
{
- track_p car;
- struct extraData * xx;
- coOrd size, lo, hi;
- BOOL_T drawCarEnable1 = drawCarEnable;
-#ifdef MEMCHECK
-drawCounter++;
-top1 = Sbrk( 0 );
-if ( top1 != top2 ) {
- fprintf( stderr, "incr by %ld at %ld\n", (char*)top1-(char*)top2, drawCounter );
- top2 = top1;
-}
-#endif
- drawCarEnable = TRUE;
- wDrawDelayUpdate( mainD.d, TRUE );
- wDrawRestoreImage( mainD.d );
- DrawMarkers();
- DrawPositionIndicators();
- for ( car=NULL; TrackIterate(&car); ) {
- if ( GetTrkType(car) == T_CAR ) {
- xx = GetTrkExtraData(car);
- CarItemSize( xx->item, &size ); /* TODO assumes xx->trvTrk.pos is the car center */
- lo.x = xx->trvTrk.pos.x - size.x/2.0;
- lo.y = xx->trvTrk.pos.y - size.x/2.0;
- hi.x = lo.x + size.x;
- hi.y = lo.y + size.x;
- if ( !OFF_MAIND( lo, hi ) )
- DrawCar( car, &mainD, wDrawColorBlack );
- }
- }
- wDrawDelayUpdate( mainD.d, FALSE );
- drawCarEnable = drawCarEnable1;
+ track_p car;
+ struct extraData * xx;
+ coOrd size, lo, hi;
+ BOOL_T drawCarEnable1 = drawCarEnable;
+ drawCarEnable = TRUE;
+ wDrawDelayUpdate(mainD.d, TRUE);
+ wDrawRestoreImage(mainD.d);
+ DrawMarkers();
+ DrawPositionIndicators();
+
+ for (car=NULL; TrackIterate(&car);) {
+ if (GetTrkType(car) == T_CAR) {
+ xx = GetTrkExtraData(car);
+ CarItemSize(xx->item,
+ &size); /* TODO assumes xx->trvTrk.pos is the car center */
+ lo.x = xx->trvTrk.pos.x - size.x/2.0;
+ lo.y = xx->trvTrk.pos.y - size.x/2.0;
+ hi.x = lo.x + size.x;
+ hi.y = lo.y + size.x;
+
+ if (!OFF_MAIND(lo, hi)) {
+ DrawCar(car, &mainD, wDrawColorBlack);
+ }
+ }
+ }
+
+ wDrawDelayUpdate(mainD.d, FALSE);
+ drawCarEnable = drawCarEnable1;
}
static DIST_T GetTrainLength2(
- track_p * car0,
- BOOL_T * dir )
+ track_p * car0,
+ BOOL_T * dir)
{
- DIST_T length = 0, carLength;
- struct extraData * xx;
-
- WALK_CARS_START ( *car0, xx, *dir )
- carLength = CarItemCoupledLength( xx->item );
- if ( length == 0 )
- length = carLength/2.0; /* TODO assumes xx->trvTrk.pos is the car center */
- else
- length += carLength;
- WALK_CARS_END ( *car0, xx, *dir )
- return length;
+ DIST_T length = 0, carLength;
+ struct extraData * xx;
+ WALK_CARS_START(*car0, xx, *dir)
+ carLength = CarItemCoupledLength(xx->item);
+
+ if (length == 0) {
+ length = carLength/2.0; /* TODO assumes xx->trvTrk.pos is the car center */
+ } else {
+ length += carLength;
+ }
+
+ WALK_CARS_END(*car0, xx, *dir)
+ return length;
}
static DIST_T GetTrainLength(
- track_p car0,
- BOOL_T dir )
+ track_p car0,
+ BOOL_T dir)
{
- return GetTrainLength2( &car0, &dir );
+ return GetTrainLength2(&car0, &dir);
}
static void PlaceCar(
- track_p car )
+ track_p car)
{
- struct extraData *xx = GetTrkExtraData(car);
- DIST_T dists[2];
- int dir;
-
- CarItemPlace( xx->item, &xx->trvTrk, dists );
-
- for ( dir=0; dir<2; dir++ )
- xx->couplerPos[dir] = CarItemFindCouplerMountPoint( xx->item, xx->trvTrk, dir );
-
- car->endPt[0].angle = xx->trvTrk.angle;
- Translate( &car->endPt[0].pos, xx->trvTrk.pos, car->endPt[0].angle, dists[0] );
- car->endPt[1].angle = NormalizeAngle( xx->trvTrk.angle + 180.0 );
- Translate( &car->endPt[1].pos, xx->trvTrk.pos, car->endPt[1].angle, dists[1] );
-LOG( log_trainMove, 4, ( "%s @ [%0.3f,%0.3f] A%0.3f\n", CarItemNumber(xx->item), xx->trvTrk.pos.x, xx->trvTrk.pos.y, xx->trvTrk.angle ) )
- SetCarBoundingBox( car );
- xx->state &= ~(CAR_STATE_ONHIDENTRACK);
- xx->trkLayer = NOTALAYER;
- if ( xx->trvTrk.trk ) {
- if ( !GetTrkVisible(xx->trvTrk.trk) )
- xx->state |= CAR_STATE_ONHIDENTRACK;
- xx->trkLayer = GetTrkLayer(xx->trvTrk.trk);
- }
+ struct extraData *xx = GetTrkExtraData(car);
+ DIST_T dists[2];
+ int dir;
+ CarItemPlace(xx->item, &xx->trvTrk, dists);
+
+ for (dir=0; dir<2; dir++) {
+ xx->couplerPos[dir] = CarItemFindCouplerMountPoint(xx->item, xx->trvTrk, dir);
+ }
+
+ car->endPt[0].angle = xx->trvTrk.angle;
+ Translate(&car->endPt[0].pos, xx->trvTrk.pos, car->endPt[0].angle, dists[0]);
+ car->endPt[1].angle = NormalizeAngle(xx->trvTrk.angle + 180.0);
+ Translate(&car->endPt[1].pos, xx->trvTrk.pos, car->endPt[1].angle, dists[1]);
+ LOG(log_trainMove, 4, ("%s @ [%0.3f,%0.3f] A%0.3f\n", CarItemNumber(xx->item),
+ xx->trvTrk.pos.x, xx->trvTrk.pos.y, xx->trvTrk.angle))
+ SetCarBoundingBox(car);
+ xx->state &= ~(CAR_STATE_ONHIDENTRACK);
+ xx->trkLayer = NOTALAYER;
+
+ if (xx->trvTrk.trk) {
+ if (!GetTrkVisible(xx->trvTrk.trk)) {
+ xx->state |= CAR_STATE_ONHIDENTRACK;
+ }
+
+ xx->trkLayer = GetTrkLayer(xx->trvTrk.trk);
+ }
}
static track_p FindCar(
- coOrd * pos )
+ coOrd * pos)
{
- coOrd pos0, pos1;
- track_p trk, trk1;
- DIST_T dist1 = 100000, dist;
- struct extraData * xx;
-
- trk1 = NULL;
- for ( trk=NULL; TrackIterate(&trk); ) {
- if ( GetTrkType(trk) == T_CAR ) {
- xx = GetTrkExtraData(trk);
- if ( IsIgnored(xx) )
- continue;
- pos0 = *pos;
- dist = DistanceCar( trk, &pos0 );
- if ( dist < dist1 ) {
- dist1 = dist;
- trk1 = trk;
- pos1 = pos0;
- }
- }
- }
- if ( dist1 < 10 ) {
- *pos = pos1;
- return trk1;
- } else {
- return NULL;
- }
+ coOrd pos0, pos1;
+ track_p trk, trk1;
+ DIST_T dist1 = 100000, dist;
+ struct extraData * xx;
+ trk1 = NULL;
+
+ for (trk=NULL; TrackIterate(&trk);) {
+ if (GetTrkType(trk) == T_CAR) {
+ xx = GetTrkExtraData(trk);
+
+ if (IsIgnored(xx)) {
+ continue;
+ }
+
+ pos0 = *pos;
+ dist = DistanceCar(trk, &pos0);
+
+ if (dist < dist1) {
+ dist1 = dist;
+ trk1 = trk;
+ pos1 = pos0;
+ }
+ }
+ }
+
+ if (dist1 < 10) {
+ *pos = pos1;
+ return trk1;
+ } else {
+ return NULL;
+ }
}
static track_p FindMasterLoco(
- track_p train,
- int * dirR )
+ track_p train,
+ int * dirR)
{
- track_p car0;
- struct extraData *xx0;
- int dir, dir0;
-
- for ( dir = 0; dir<2; dir++ ) {
- car0 = train;
- dir0 = dir;
- WALK_CARS_START( car0, xx0, dir0 )
- if ( CarItemIsLoco(xx0->item) && IsLocoMaster(xx0) ) {
- if ( dirR ) *dirR = 1-dir0;
- return car0;
- }
- WALK_CARS_END( car0, xx0, dir0 )
- }
- return NULL;
+ struct extraData *xx0;
+ int dir;
+
+ for (dir = 0; dir<2; dir++) {
+ track_p car0;
+ int dir0;
+ car0 = train;
+ dir0 = dir;
+ WALK_CARS_START(car0, xx0, dir0)
+
+ if (CarItemIsLoco(xx0->item) && IsLocoMaster(xx0)) {
+ if (dirR) {
+ *dirR = 1-dir0;
+ }
+
+ return car0;
+ }
+
+ WALK_CARS_END(car0, xx0, dir0)
+ }
+
+ return NULL;
}
static track_p PickMasterLoco(
- track_p car,
- int dir )
+ track_p car,
+ int dir)
{
- track_p loco=NULL;
- struct extraData *xx;
-
- WALK_CARS_START( car, xx, dir )
- if ( CarItemIsLoco(xx->item) ) {
- if ( IsLocoMaster(xx) )
- return car;
- if ( loco == NULL ) loco = car;
- }
- WALK_CARS_END( car, xx, dir )
- if ( loco == NULL )
- return NULL;
- xx = GetTrkExtraData(loco);
- SetLocoMaster(xx);
- xx->speed = 0;
- LOG( log_trainMove, 1, ( "%s becomes master\n", CarItemNumber(xx->item) ) )
- return loco;
+ track_p loco=NULL;
+ struct extraData *xx;
+ WALK_CARS_START(car, xx, dir)
+
+ if (CarItemIsLoco(xx->item)) {
+ if (IsLocoMaster(xx)) {
+ return car;
+ }
+
+ if (loco == NULL) {
+ loco = car;
+ }
+ }
+
+ WALK_CARS_END(car, xx, dir)
+
+ if (loco == NULL) {
+ return NULL;
+ }
+
+ xx = GetTrkExtraData(loco);
+ SetLocoMaster(xx);
+ xx->speed = 0;
+ LOG(log_trainMove, 1, ("%s becomes master\n", CarItemNumber(xx->item)))
+ return loco;
}
static void UncoupleCars(
- track_p car1,
- track_p car2 )
+ track_p car1,
+ track_p car2)
{
- struct extraData * xx1, * xx2;
- track_p loco, loco1, loco2;
- int dir1, dir2;
-
- xx1 = GetTrkExtraData(car1);
- xx2 = GetTrkExtraData(car2);
- if ( GetTrkEndTrk(car1,0) == car2 ) {
- dir1 = 0;
- } else if ( GetTrkEndTrk(car1,1) == car2 ) {
- dir1 = 1;
- } else {
- ErrorMessage( "uncoupleCars - not coupled" );
- return;
- }
- if ( GetTrkEndTrk(car2,0) == car1 ) {
- dir2 = 0;
- } else if ( GetTrkEndTrk(car2,1) == car1 ) {
- dir2 = 1;
- } else {
- ErrorMessage( "uncoupleCars - not coupled" );
- return;
- }
- loco = FindMasterLoco( car1, NULL );
- car1->endPt[dir1].track = NULL;
- car2->endPt[dir2].track = NULL;
- /*DisconnectTracks( car1, dir1, car2, dir2 );*/
- if ( loco ) {
- loco1 = PickMasterLoco( car1, 1-dir1 );
- if ( loco1 != loco )
- LocoListChangeEntry( NULL, loco1 );
- loco2 = PickMasterLoco( car2, 1-dir2 );
- if ( loco2 != loco )
- LocoListChangeEntry( NULL, loco2 );
- }
+ track_p loco;
+ int dir1, dir2;
+
+ if (GetTrkEndTrk(car1,0) == car2) {
+ dir1 = 0;
+ } else if (GetTrkEndTrk(car1,1) == car2) {
+ dir1 = 1;
+ } else {
+ ErrorMessage("uncoupleCars - not coupled");
+ return;
+ }
+
+ if (GetTrkEndTrk(car2,0) == car1) {
+ dir2 = 0;
+ } else if (GetTrkEndTrk(car2,1) == car1) {
+ dir2 = 1;
+ } else {
+ ErrorMessage("uncoupleCars - not coupled");
+ return;
+ }
+
+ loco = FindMasterLoco(car1, NULL);
+ car1->endPt[dir1].track = NULL;
+ car2->endPt[dir2].track = NULL;
+
+ if (loco) {
+ track_p loco1, loco2;
+ loco1 = PickMasterLoco(car1, 1-dir1);
+
+ if (loco1 != loco) {
+ LocoListChangeEntry(NULL, loco1);
+ }
+
+ loco2 = PickMasterLoco(car2, 1-dir2);
+
+ if (loco2 != loco) {
+ LocoListChangeEntry(NULL, loco2);
+ }
+ }
}
static void CoupleCars(
- track_p car1,
- int dir1,
- track_p car2,
- int dir2 )
+ track_p car1,
+ int dir1,
+ track_p car2,
+ int dir2)
{
- struct extraData * xx1, * xx2;
- track_p loco1, loco2;
- track_p car;
- int dir;
-
- xx1 = GetTrkExtraData(car1);
- xx2 = GetTrkExtraData(car2);
- if ( GetTrkEndTrk(car1,dir1) != NULL || GetTrkEndTrk(car2,dir2) != NULL ) {
- LOG( log_trainMove, 1, ( "coupleCars - already coupled\n" ) )
- return;
- }
- car = car1;
- dir = 1-dir1;
- WALK_CARS_START( car, xx1, dir )
- if ( car == car2 ) {
- LOG( log_trainMove, 1, ( "coupleCars - already coupled\n" ) )
- ErrorMessage( "Car coupling loop" );
- return;
- }
- WALK_CARS_END( car, xx1, dir )
- car = car2;
- dir = 1-dir2;
- WALK_CARS_START( car, xx2, dir )
- if ( car == car1 ) {
- LOG( log_trainMove, 1, ( "coupleCars - already coupled\n" ) )
- ErrorMessage( "Car coupling loop" );
- return;
- }
- WALK_CARS_END( car, xx1, dir )
- loco1 = FindMasterLoco( car1, NULL );
- loco2 = FindMasterLoco( car2, NULL );
- car1->endPt[dir1].track = car2;
- car2->endPt[dir2].track = car1;
- /*ConnectTracks( car1, dir1, car2, dir2 );*/
-if ( logTable(log_trainMove).level >= 2 ) {
-LogPrintf( "Coupling %s[%d] ", CarItemNumber(xx1->item), dir1 );
-LogPrintf( " and %s[%d]\n", CarItemNumber(xx2->item), dir2 );
-}
- if ( ( loco1 != NULL && loco2 != NULL ) ) {
- xx1 = GetTrkExtraData( loco1 );
- xx2 = GetTrkExtraData( loco2 );
- if ( xx1->speed == 0 ) {
- ClrLocoMaster(xx1);
- LOG( log_trainMove, 2, ( "%s loses master\n", CarItemNumber(xx1->item) ) )
- if ( followTrain == loco1 )
- followTrain = loco2;
- LocoListChangeEntry( loco1, NULL );
- loco1 = loco2;
- } else {
- ClrLocoMaster(xx2);
- xx1->speed = (xx1->speed + xx2->speed)/2.0;
- if ( xx1->speed < 0 )
- xx1->speed = 0;
- if ( xx1->speed > 100 )
- xx1->speed = 100;
- LOG( log_trainMove, 2, ( "%s loses master\n", CarItemNumber(xx2->item) ) )
- if ( followTrain == loco2 )
- followTrain = loco1;
- LocoListChangeEntry( loco2, NULL );
- }
- SetTrainDirection( loco1 );
- }
+ struct extraData * xx1, * xx2;
+ track_p loco1, loco2;
+ track_p car;
+ int dir;
+ xx1 = GetTrkExtraData(car1);
+ xx2 = GetTrkExtraData(car2);
+
+ if (GetTrkEndTrk(car1,dir1) != NULL || GetTrkEndTrk(car2,dir2) != NULL) {
+ LOG(log_trainMove, 1, ("coupleCars - already coupled\n"))
+ return;
+ }
+
+ car = car1;
+ dir = 1-dir1;
+ WALK_CARS_START(car, xx1, dir)
+
+ if (car == car2) {
+ LOG(log_trainMove, 1, ("coupleCars - already coupled\n"))
+ ErrorMessage("Car coupling loop");
+ return;
+ }
+
+ WALK_CARS_END(car, xx1, dir)
+ car = car2;
+ dir = 1-dir2;
+ WALK_CARS_START(car, xx2, dir)
+
+ if (car == car1) {
+ LOG(log_trainMove, 1, ("coupleCars - already coupled\n"))
+ ErrorMessage("Car coupling loop");
+ return;
+ }
+
+ WALK_CARS_END(car, xx1, dir)
+ loco1 = FindMasterLoco(car1, NULL);
+ loco2 = FindMasterLoco(car2, NULL);
+ car1->endPt[dir1].track = car2;
+ car2->endPt[dir2].track = car1;
+
+ /*ConnectTracks( car1, dir1, car2, dir2 );*/
+ if (logTable(log_trainMove).level >= 2) {
+ LogPrintf("Coupling %s[%d] ", CarItemNumber(xx1->item), dir1);
+ LogPrintf(" and %s[%d]\n", CarItemNumber(xx2->item), dir2);
+ }
+
+ if ((loco1 != NULL && loco2 != NULL)) {
+ xx1 = GetTrkExtraData(loco1);
+ xx2 = GetTrkExtraData(loco2);
+
+ if (xx1->speed == 0) {
+ ClrLocoMaster(xx1);
+ LOG(log_trainMove, 2, ("%s loses master\n", CarItemNumber(xx1->item)))
+
+ if (followTrain == loco1) {
+ followTrain = loco2;
+ }
+
+ LocoListChangeEntry(loco1, NULL);
+ loco1 = loco2;
+ } else {
+ ClrLocoMaster(xx2);
+ xx1->speed = (xx1->speed + xx2->speed)/2.0;
+
+ if (xx1->speed < 0) {
+ xx1->speed = 0;
+ }
+
+ if (xx1->speed > 100) {
+ xx1->speed = 100;
+ }
+
+ LOG(log_trainMove, 2, ("%s loses master\n", CarItemNumber(xx2->item)))
+
+ if (followTrain == loco2) {
+ followTrain = loco1;
+ }
+
+ LocoListChangeEntry(loco2, NULL);
+ }
+
+ SetTrainDirection(loco1);
+ }
}
-long crashSpeedDecay=5;
+
long crashDistFactor=60;
static void PlaceCars(
- track_p car0,
- int dir0,
- long crashSpeed,
- BOOL_T crashFlip )
+ track_p car0,
+ int dir0,
+ long crashSpeed,
+ BOOL_T crashFlip)
{
- struct extraData *xx0 = GetTrkExtraData(car0), *xx;
- int dir;
- traverseTrack_t trvTrk;
- DIST_T length, dist, length1;
- track_p car_curr;
- DIST_T flipflop = 1;
-
- if ( crashFlip )
- flipflop = -1;
- dir = dir0;
- trvTrk = xx0->trvTrk;
- if ( dir0 )
- FlipTraverseTrack( &trvTrk );
- length = CarItemCoupledLength(xx0->item)/2.0;
- car_curr = car0;
- ClrIgnored( xx0 );
- WALK_CARS_START ( car_curr, xx, dir )
- if ( car_curr != car0 ) {
- ClrIgnored( xx );
- length1 = CarItemCoupledLength(xx->item)/2.0;
- dist = length + length1;
- crashSpeed = crashSpeed*crashSpeedDecay/10;
- if ( crashSpeed > 0 )
- dist -= dist * crashSpeed/crashDistFactor;
- TraverseTrack2( &trvTrk, dist );
- xx->trvTrk = trvTrk;
- if ( crashSpeed > 0 ) {
- xx->trvTrk.angle = NormalizeAngle( xx->trvTrk.angle + flipflop*crashSpeed );
- xx->trvTrk.trk = NULL;
- }
- flipflop = -flipflop;
- if ( dir != 0 )
- FlipTraverseTrack( &xx->trvTrk );
- PlaceCar( car_curr );
- length = length1;
- }
- WALK_CARS_END ( car_curr, xx, dir )
+ struct extraData *xx0 = GetTrkExtraData(car0), *xx;
+ int dir;
+ traverseTrack_t trvTrk;
+ DIST_T length;
+ track_p car_curr;
+ DIST_T flipflop = 1;
+
+ if (crashFlip) {
+ flipflop = -1;
+ }
+
+ dir = dir0;
+ trvTrk = xx0->trvTrk;
+
+ if (dir0) {
+ FlipTraverseTrack(&trvTrk);
+ }
+
+ length = CarItemCoupledLength(xx0->item)/2.0;
+ car_curr = car0;
+ ClrIgnored(xx0);
+ WALK_CARS_START(car_curr, xx, dir)
+
+ if (car_curr != car0) {
+ DIST_T dist, length1;
+ ClrIgnored(xx);
+ length1 = CarItemCoupledLength(xx->item)/2.0;
+ dist = length + length1;
+ crashSpeed = crashSpeed*CRASHSPEEDDECAY/10;
+
+ if (crashSpeed > 0) {
+ dist -= dist * crashSpeed/crashDistFactor;
+ }
+
+ TraverseTrack2(&trvTrk, dist);
+ xx->trvTrk = trvTrk;
+
+ if (crashSpeed > 0) {
+ xx->trvTrk.angle = NormalizeAngle(xx->trvTrk.angle + flipflop*crashSpeed);
+ xx->trvTrk.trk = NULL;
+ }
+
+ flipflop = -flipflop;
+
+ if (dir != 0) {
+ FlipTraverseTrack(&xx->trvTrk);
+ }
+
+ PlaceCar(car_curr);
+ length = length1;
+ }
+
+ WALK_CARS_END(car_curr, xx, dir)
}
static void CrashTrain(
- track_p car,
- int dir,
- traverseTrack_p trvTrkP,
- long speed,
- BOOL_T flip )
+ track_p car,
+ int dir,
+ traverseTrack_p trvTrkP,
+ long speed,
+ BOOL_T flip)
{
- track_p loco;
- struct extraData *xx;
+ track_p loco;
+ struct extraData *xx;
+ loco = FindMasterLoco(car,NULL);
- loco = FindMasterLoco(car,NULL);
- if ( loco != NULL ) {
- StopTrain( loco, ST_Crashed );
- }
- xx = GetTrkExtraData(car);
- xx->trvTrk = *trvTrkP;
- if ( dir )
- FlipTraverseTrack( &xx->trvTrk );
- PlaceCars( car, 1-dir, speed, flip );
- if ( flip )
- speed = - speed;
- xx->trvTrk.angle = NormalizeAngle( xx->trvTrk.angle - speed );
- xx->trvTrk.trk = NULL;
- PlaceCar( car );
+ if (loco != NULL) {
+ StopTrain(loco, ST_Crashed);
+ }
+
+ xx = GetTrkExtraData(car);
+ xx->trvTrk = *trvTrkP;
+
+ if (dir) {
+ FlipTraverseTrack(&xx->trvTrk);
+ }
+
+ PlaceCars(car, 1-dir, speed, flip);
+
+ if (flip) {
+ speed = - speed;
+ }
+
+ xx->trvTrk.angle = NormalizeAngle(xx->trvTrk.angle - speed);
+ xx->trvTrk.trk = NULL;
+ PlaceCar(car);
}
-static FLOAT_T couplerConnAngle = 45.0;
static BOOL_T CheckCoupling(
- track_p car0,
- int dir00,
- BOOL_T doCheckCrash )
+ track_p car0,
+ int dir00,
+ BOOL_T doCheckCrash)
{
- track_p car1, loco1;
- struct extraData *xx0, *xx1;
- coOrd pos1;
- DIST_T dist0, distc, dist=100000.0;
- int dir0, dir1, dirl;
- ANGLE_T angle;
- traverseTrack_t trvTrk0, trvTrk1;
- long speed, speed0, speed1;
-
- xx0 = xx1 = GetTrkExtraData(car0);
- /* find length of train from loco to start and end */
- dir0 = dir00;
- dist0 = GetTrainLength2( &car0, &dir0 );
-
- trvTrk0 = xx0->trvTrk;
- if ( dir00 )
- FlipTraverseTrack( &trvTrk0 );
- TraverseTrack2( &trvTrk0, dist0 );
- pos1 = trvTrk0.pos;
- car1 = FindCar( &pos1 );
- if ( !car1 )
- return TRUE;
- xx1 = GetTrkExtraData(car1);
- if ( !IsOnTrack(xx1) )
- return TRUE;
- /* determine which EP of the found car to couple to */
- angle = NormalizeAngle( trvTrk0.angle-xx1->trvTrk.angle );
- if ( angle > 90 && angle < 270 ) {
- dir1 = 0;
- angle = NormalizeAngle( angle+180 );
- } else {
- dir1 = 1;
- }
- /* already coupled? */
- if ( GetTrkEndTrk(car1,dir1) != NULL )
- return TRUE;
- /* are we close to aligned? */
- if ( angle > couplerConnAngle && angle < 360.0-couplerConnAngle )
- return TRUE;
- /* find pos of found car's coupler, and dist btw couplers */
- distc = CarItemCoupledLength(xx1->item);
- Translate( &pos1, xx1->trvTrk.pos, xx1->trvTrk.angle+(dir1?180.0:0.0), distc/2.0 );
- dist = FindDistance( trvTrk0.pos, pos1 );
- if ( dist < trackGauge/10 )
- return TRUE;
- /* not real close: are we overlapped? */
- angle = FindAngle( trvTrk0.pos, pos1 );
- angle = NormalizeAngle( angle - trvTrk0.angle );
- if ( angle < 90 || angle > 270 )
- return TRUE;
- /* are we beyond the end of the found car? */
- if ( dist > distc )
- return TRUE;
- /* are we on the same track? */
- trvTrk1 = xx1->trvTrk;
- if ( dir1 )
- FlipTraverseTrack( &trvTrk1 );
- TraverseTrack2( &trvTrk1, distc/2.0-dist );
- if ( trvTrk1.trk != trvTrk0.trk )
- return TRUE;
- if ( doCheckCrash ) {
- speed0 = (long)xx0->speed;
- if ( (xx0->direction==0) != (dir00==0) )
- speed0 = - speed0;
- loco1 = FindMasterLoco( car1, &dirl );
- xx1 = NULL;
- if ( loco1 ) {
- xx1 = GetTrkExtraData(loco1);
- speed1 = (long)xx1->speed;
- if ( car1 == loco1 ) {
- dirl = IsAligned( xx1->trvTrk.angle, FindAngle( trvTrk0.pos, xx1->trvTrk.pos ) )?1:0;
- }
- if ( (xx1->direction==1) != (dirl==1) )
- speed1 = -speed1;
- } else {
- speed1 = 0;
- }
- speed = (long)labs( speed0 + speed1 );
- LOG( log_trainMove, 2, ( "coupling speed=%ld\n", speed ) )
- if ( speed > maxCouplingSpeed ) {
- CrashTrain( car0, dir0, &trvTrk0, speed, FALSE );
- CrashTrain( car1, dir1, &trvTrk1, speed, TRUE );
- return FALSE;
- }
- }
- if ( dir00 )
- dist = -dist;
- TraverseTrack2( &xx0->trvTrk, dist );
- CoupleCars( car0, dir0, car1, dir1 );
-LOG( log_trainMove, 3, ( " -> %0.3f\n", dist ) )
- return TRUE;
-}
+ track_p car1;
+ struct extraData *xx0, *xx1;
+ coOrd pos1;
+ DIST_T dist0, distc, dist=100000.0;
+ int dir0, dir1, dirl;
+ ANGLE_T angle;
+ traverseTrack_t trvTrk0, trvTrk1;
+ xx0 = xx1 = GetTrkExtraData(car0);
+ /* find length of train from loco to start and end */
+ dir0 = dir00;
+ dist0 = GetTrainLength2(&car0, &dir0);
+ trvTrk0 = xx0->trvTrk;
+
+ if (dir00) {
+ FlipTraverseTrack(&trvTrk0);
+ }
+
+ TraverseTrack2(&trvTrk0, dist0);
+ pos1 = trvTrk0.pos;
+ car1 = FindCar(&pos1);
+
+ if (!car1) {
+ return TRUE;
+ }
+
+ xx1 = GetTrkExtraData(car1);
+
+ if (!IsOnTrack(xx1)) {
+ return TRUE;
+ }
+
+ /* determine which EP of the found car to couple to */
+ angle = NormalizeAngle(trvTrk0.angle-xx1->trvTrk.angle);
+
+ if (angle > 90 && angle < 270) {
+ dir1 = 0;
+ angle = NormalizeAngle(angle+180);
+ } else {
+ dir1 = 1;
+ }
+
+ /* already coupled? */
+ if (GetTrkEndTrk(car1,dir1) != NULL) {
+ return TRUE;
+ }
+
+ /* are we close to aligned? */
+ if (angle > COUPLERCONNECTIONANGLE && angle < 360.0-COUPLERCONNECTIONANGLE) {
+ return TRUE;
+ }
+
+ /* find pos of found car's coupler, and dist btw couplers */
+ distc = CarItemCoupledLength(xx1->item);
+ Translate(&pos1, xx1->trvTrk.pos, xx1->trvTrk.angle+(dir1?180.0:0.0),
+ distc/2.0);
+ dist = FindDistance(trvTrk0.pos, pos1);
+
+ if (dist < trackGauge/10) {
+ return TRUE;
+ }
+
+ /* not real close: are we overlapped? */
+ angle = FindAngle(trvTrk0.pos, pos1);
+ angle = NormalizeAngle(angle - trvTrk0.angle);
+
+ if (angle < 90 || angle > 270) {
+ return TRUE;
+ }
+
+ /* are we beyond the end of the found car? */
+ if (dist > distc) {
+ return TRUE;
+ }
+
+ /* are we on the same track? */
+ trvTrk1 = xx1->trvTrk;
+
+ if (dir1) {
+ FlipTraverseTrack(&trvTrk1);
+ }
+
+ TraverseTrack2(&trvTrk1, distc/2.0-dist);
+
+ if (trvTrk1.trk != trvTrk0.trk) {
+ return TRUE;
+ }
+
+ if (doCheckCrash) {
+ track_p loco1;
+ long speed, speed0, speed1;
+ speed0 = (long)xx0->speed;
+
+ if ((xx0->direction==0) != (dir00==0)) {
+ speed0 = - speed0;
+ }
+
+ loco1 = FindMasterLoco(car1, &dirl);
+ xx1 = NULL;
+
+ if (loco1) {
+ xx1 = GetTrkExtraData(loco1);
+ speed1 = (long)xx1->speed;
+
+ if (car1 == loco1) {
+ dirl = IsAligned(xx1->trvTrk.angle, FindAngle(trvTrk0.pos,
+ xx1->trvTrk.pos))?1:0;
+ }
+
+ if ((xx1->direction==1) != (dirl==1)) {
+ speed1 = -speed1;
+ }
+ } else {
+ speed1 = 0;
+ }
+ speed = labs(speed0 + speed1);
+ LOG(log_trainMove, 2, ("coupling speed=%ld\n", speed))
-static void PlaceTrain(
- track_p car0,
- BOOL_T doCheckCrash,
- BOOL_T doCheckCoupling )
-{
- track_p car_curr;
- struct extraData *xx0, *xx;
- int dir0, dir;
-
- xx0 = GetTrkExtraData(car0);
-
- LOG( log_trainMove, 2, ( " placeTrain: %s [%0.3f %0.3f] A%0.3f", CarItemNumber(xx0->item), xx0->trvTrk.pos.x, xx0->trvTrk.pos.y, xx0->trvTrk.angle ) )
-
- car_curr = car0;
- for ( dir0=0; dir0<2; dir0++ ) {
- car_curr = car0;
- dir = dir0;
- xx = xx0;
- WALK_CARS_START( car_curr, xx, dir )
- SetIgnored(xx);
- WALK_CARS_END( car_curr, xx, dir );
- }
+ if (speed > maxCouplingSpeed) {
+ CrashTrain(car0, dir0, &trvTrk0, speed, FALSE);
+ CrashTrain(car1, dir1, &trvTrk1, speed, TRUE);
+ return FALSE;
+ }
+ }
- /* check for coupling to other cars */
- if ( doCheckCoupling ) {
- if ( xx0->trvTrk.trk )
- if ( !CheckCoupling( car0, 0, doCheckCrash ) )
- return;
- if ( xx0->trvTrk.trk )
- if ( !CheckCoupling( car0, 1, doCheckCrash ) )
- return;
- }
+ if (dir00) {
+ dist = -dist;
+ }
+
+ TraverseTrack2(&xx0->trvTrk, dist);
+ CoupleCars(car0, dir0, car1, dir1);
+ LOG(log_trainMove, 3, (" -> %0.3f\n", dist))
+ return TRUE;
+}
- PlaceCar( car0 );
- for ( dir0=0; dir0<2; dir0++ )
- PlaceCars( car0, dir0, 0, FALSE );
+static void PlaceTrain(
+ track_p car0,
+ BOOL_T doCheckCrash,
+ BOOL_T doCheckCoupling)
+{
+ track_p car_curr;
+ struct extraData *xx0;
+ int dir0;
+ xx0 = GetTrkExtraData(car0);
+ LOG(log_trainMove, 2, (" placeTrain: %s [%0.3f %0.3f] A%0.3f",
+ CarItemNumber(xx0->item), xx0->trvTrk.pos.x, xx0->trvTrk.pos.y,
+ xx0->trvTrk.angle))
+ car_curr = car0;
+
+ for (dir0=0; dir0<2; dir0++) {
+ int dir;
+ struct extraData *xx;
+ car_curr = car0;
+ dir = dir0;
+ xx = xx0;
+ WALK_CARS_START(car_curr, xx, dir)
+ SetIgnored(xx);
+ WALK_CARS_END(car_curr, xx, dir);
+ }
+
+ /* check for coupling to other cars */
+ if (doCheckCoupling) {
+ if (xx0->trvTrk.trk)
+ if (!CheckCoupling(car0, 0, doCheckCrash)) {
+ return;
+ }
+
+ if (xx0->trvTrk.trk)
+ if (!CheckCoupling(car0, 1, doCheckCrash)) {
+ return;
+ }
+ }
+
+ PlaceCar(car0);
+
+ for (dir0=0; dir0<2; dir0++) {
+ PlaceCars(car0, dir0, 0, FALSE);
+ }
}
static void PlaceTrainInit(
- track_p car0,
- track_p trk0,
- coOrd pos0,
- ANGLE_T angle0,
- BOOL_T doCheckCoupling )
+ track_p car0,
+ track_p trk0,
+ coOrd pos0,
+ ANGLE_T angle0,
+ BOOL_T doCheckCoupling)
{
- struct extraData * xx = GetTrkExtraData(car0);
- xx->trvTrk.trk = trk0;
- xx->trvTrk.dist = xx->trvTrk.length = -1;
- xx->trvTrk.pos = pos0;
- xx->trvTrk.angle = angle0;
- PlaceTrain( car0, FALSE, doCheckCoupling );
+ struct extraData * xx = GetTrkExtraData(car0);
+ xx->trvTrk.trk = trk0;
+ xx->trvTrk.dist = xx->trvTrk.length = -1;
+ xx->trvTrk.pos = pos0;
+ xx->trvTrk.angle = angle0;
+ PlaceTrain(car0, FALSE, doCheckCoupling);
}
static void FlipTrain(
- track_p train )
+ track_p train)
{
- DIST_T d0, d1;
- struct extraData * xx;
-
- if ( train == NULL )
- return;
- d0 = GetTrainLength( train, 0 );
- d1 = GetTrainLength( train, 1 );
- xx = GetTrkExtraData(train);
- TraverseTrack2( &xx->trvTrk, d0-d1 );
- FlipTraverseTrack( &xx->trvTrk );
- xx->trvTrk.length = -1;
- PlaceTrain( train, FALSE, TRUE );
+ DIST_T d0, d1;
+ struct extraData * xx;
+
+ if (train == NULL) {
+ return;
+ }
+
+ d0 = GetTrainLength(train, 0);
+ d1 = GetTrainLength(train, 1);
+ xx = GetTrkExtraData(train);
+ TraverseTrack2(&xx->trvTrk, d0-d1);
+ FlipTraverseTrack(&xx->trvTrk);
+ xx->trvTrk.length = -1;
+ PlaceTrain(train, FALSE, TRUE);
}
static BOOL_T MoveTrain(
- track_p train,
- long timeD )
+ track_p train,
+ long timeD)
{
- DIST_T ips, dist0, dist1;
- struct extraData *xx, *xx1;
- traverseTrack_t trvTrk;
- DIST_T length;
- track_p car1;
- int dir1;
- int measured; /* make sure the distance is only measured once per train */
-
- if ( train == NULL )
- return FALSE;
- xx = GetTrkExtraData(train);
- if ( xx->speed <= 0 )
- return FALSE;
-
- if ( setTimeD )
- timeD = setTimeD;
- ips = ((xx->speed*5280.0*12.0)/(60.0*60.0*GetScaleRatio(curScaleInx)));
- dist0 = ips * timeD/1000.0;
- length = GetTrainLength( train, xx->direction );
- dist1 = length + dist0;
- trvTrk = xx->trvTrk;
- if ( trvTrk.trk == NULL ) {
- return FALSE;
- }
- LOG( log_trainMove, 1, ( "moveTrain: %s t%ld->%0.3f S%0.3f D%d [%0.3f %0.3f] A%0.3f T%d\n",
- CarItemNumber(xx->item), timeD, dist0, xx->speed, xx->direction, xx->trvTrk.pos.x, xx->trvTrk.pos.y, xx->trvTrk.angle, xx->trvTrk.trk?GetTrkIndex(xx->trvTrk.trk):-1 ) )
- if ( xx->direction )
- FlipTraverseTrack( &trvTrk );
- TraverseTrack( &trvTrk, &dist1 );
- if ( dist1 > 0.0 ) {
- if ( dist1 > dist0 ) {
- /*ErrorMessage( "%s no room: L%0.3f D%0.3f", CarItemNumber(xx->item), length, dist1 );*/
- StopTrain( train, ST_NoRoom );
- return FALSE;
- } else {
- dist0 -= dist1;
- LOG( log_trainMove, 1, ( " %s STOP D%d [%0.3f %0.3f] A%0.3f D%0.3f\n",
- CarItemNumber(xx->item), xx->direction, xx->trvTrk.pos.x, xx->trvTrk.pos.y, xx->trvTrk.angle, dist0 ) )
- }
- /*ErrorMessage( "%s stopped at End Of Track", CarItemNumber(xx->item) );*/
- if ( xx->autoReverse ) {
- xx->direction = !xx->direction;
- SetTrainDirection( train );
- } else {
- if ( xx->speed > maxCouplingSpeed ) {
- car1 = train;
- dir1 = xx->direction;
- GetTrainLength2( &car1, &dir1 );
- CrashTrain( car1, dir1, &trvTrk, (long)xx->speed, FALSE );
- return TRUE;
- } else {
- StopTrain( train, trvTrk.trk?ST_OpenTurnout:ST_EndOfTrack );
- }
- }
- }
- trvTrk = xx->trvTrk;
- TraverseTrack2( &xx->trvTrk, xx->direction==0?dist0:-dist0 );
- car1 = train;
- dir1 = 0;
- GetTrainLength2( &car1, &dir1 );
- dir1 = 1-dir1;
-
- measured = FALSE;
- WALK_CARS_START( car1, xx1, dir1 );
- if ( CarItemIsLoco(xx1->item) && !measured ) {
- xx->distance += dist0;
- measured = TRUE;
- }
- WALK_CARS_END( car1, xx1, dir1 );
-
- if ( train == followTrain ) {
- if ( followCenter.x != mainCenter.x ||
- followCenter.y != mainCenter.y ) {
- if ( curTrainDlg->train == followTrain ) {
- curTrainDlg->followMe = FALSE;
- ParamLoadControl( curTrainDlg->trainPGp, I_FOLLOW );
- }
- followTrain = NULL;
- } else if ( OFF_MAIND( xx->trvTrk.pos, xx->trvTrk.pos ) ) {
- MoveMainWindow( xx->trvTrk.pos, NormalizeAngle(xx->trvTrk.angle+(xx->direction?180.0:0.0)) );
- followCenter = mainCenter;
- }
- }
- PlaceTrain( train, TRUE, TRUE );
- return TRUE;
-}
+ DIST_T ips, dist0, dist1;
+ struct extraData *xx, *xx1;
+ traverseTrack_t trvTrk;
+ DIST_T length;
+ track_p car1;
+ int dir1;
+ int measured; /* make sure the distance is only measured once per train */
+
+ if (train == NULL) {
+ return FALSE;
+ }
+
+ xx = GetTrkExtraData(train);
+
+ if (xx->speed <= 0) {
+ return FALSE;
+ }
+
+ ips = ((xx->speed*5280.0*12.0)/(60.0*60.0*GetScaleRatio(GetLayoutCurScale())));
+ dist0 = ips * timeD/1000.0;
+ length = GetTrainLength(train, xx->direction);
+ dist1 = length + dist0;
+ trvTrk = xx->trvTrk;
+
+ if (trvTrk.trk == NULL) {
+ return FALSE;
+ }
+
+ LOG(log_trainMove, 1,
+ ("moveTrain: %s t%ld->%0.3f S%0.3f D%d [%0.3f %0.3f] A%0.3f T%d\n",
+ CarItemNumber(xx->item), timeD, dist0, xx->speed, xx->direction,
+ xx->trvTrk.pos.x, xx->trvTrk.pos.y, xx->trvTrk.angle,
+ xx->trvTrk.trk?GetTrkIndex(xx->trvTrk.trk):-1))
+
+ if (xx->direction) {
+ FlipTraverseTrack(&trvTrk);
+ }
+
+ TraverseTrack(&trvTrk, &dist1);
+
+ if (dist1 > 0.0) {
+ if (dist1 > dist0) {
+ /*ErrorMessage( "%s no room: L%0.3f D%0.3f", CarItemNumber(xx->item), length, dist1 );*/
+ StopTrain(train, ST_NoRoom);
+ return FALSE;
+ } else {
+ dist0 -= dist1;
+ LOG(log_trainMove, 1, (" %s STOP D%d [%0.3f %0.3f] A%0.3f D%0.3f\n",
+ CarItemNumber(xx->item), xx->direction, xx->trvTrk.pos.x, xx->trvTrk.pos.y,
+ xx->trvTrk.angle, dist0))
+ }
+
+ /*ErrorMessage( "%s stopped at End Of Track", CarItemNumber(xx->item) );*/
+ if (xx->autoReverse) {
+ xx->direction = !xx->direction;
+ SetTrainDirection(train);
+ } else {
+ if (xx->speed > maxCouplingSpeed) {
+ car1 = train;
+ dir1 = xx->direction;
+ GetTrainLength2(&car1, &dir1);
+ CrashTrain(car1, dir1, &trvTrk, (long)xx->speed, FALSE);
+ return TRUE;
+ } else {
+ if (trvTrk.trk && trvTrk.trk->endCnt > 1) //Test for null track after Traverse
+ StopTrain(train, ST_OpenTurnout );
+ else
+ StopTrain(train, ST_EndOfTrack);
+ return (FALSE);
+ }
+ }
+ }
+
+ trvTrk = xx->trvTrk;
+ TraverseTrack2(&xx->trvTrk, xx->direction==0?dist0:-dist0);
+ car1 = train;
+ dir1 = 0;
+ GetTrainLength2(&car1, &dir1);
+ dir1 = 1-dir1;
+ measured = FALSE;
+ WALK_CARS_START(car1, xx1, dir1);
+
+ if (CarItemIsLoco(xx1->item) && !measured) {
+ xx->distance += dist0;
+ measured = TRUE;
+ }
+
+ WALK_CARS_END(car1, xx1, dir1);
+
+ if (train == followTrain) {
+ if (followCenter.x != mainCenter.x ||
+ followCenter.y != mainCenter.y) {
+ if (curTrainDlg->train == followTrain) {
+ curTrainDlg->followMe = FALSE;
+ ParamLoadControl(curTrainDlg->trainPGp, I_FOLLOW);
+ }
+
+ followTrain = NULL;
+ } else if (OFF_MAIND(xx->trvTrk.pos, xx->trvTrk.pos)) {
+ MoveMainWindow(xx->trvTrk.pos,
+ NormalizeAngle(xx->trvTrk.angle+(xx->direction?180.0:0.0)));
+ followCenter = mainCenter;
+ }
+ }
+
+ PlaceTrain(train, TRUE, TRUE);
+ return TRUE;
+}
+
+
+static BOOL_T MoveTrains(long timeD)
+{
+ BOOL_T trains_moved = FALSE;
+ track_p train;
+ struct extraData * xx;
+ for (train=NULL; TrackIterate(&train);) {
+ if (GetTrkType(train) != T_CAR) {
+ continue;
+ }
-static BOOL_T MoveTrains( long timeD )
-{
- BOOL_T trains_moved = FALSE;
- track_p train;
- struct extraData * xx;
-
- for ( train=NULL; TrackIterate( &train ); ) {
- if ( GetTrkType(train) != T_CAR ) continue;
- xx = GetTrkExtraData(train);
- if ( !CarItemIsLoco(xx->item) ) continue;
- if ( !IsLocoMaster(xx) ) continue;
- if ( xx->speed == 0 ) continue;
- trains_moved |= MoveTrain( train, timeD );
- }
+ xx = GetTrkExtraData(train);
+
+ if (!CarItemIsLoco(xx->item)) {
+ continue;
+ }
+
+ if (!IsLocoMaster(xx)) {
+ continue;
+ }
- ControllerDialogSyncAll();
+ if (xx->speed == 0) {
+ continue;
+ }
- DrawAllCars();
+ trains_moved |= MoveTrain(train, timeD);
+ }
- return trains_moved;
+ ControllerDialogSyncAll();
+ DrawAllCars();
+ return trains_moved;
}
-static void MoveTrainsLoop( void )
+static void MoveTrainsLoop(void)
{
- long time1, timeD;
- static long time0 = 0;
+ long time1, timeD;
+ static long time0 = 0;
+ trainsTimeoutPending = FALSE;
- trainsTimeoutPending = FALSE;
- if ( trainsState != TRAINS_RUN ) {
- time0 = 0;
- return;
- }
- if ( time0 == 0 )
- time0 = wGetTimer();
- time1 = wGetTimer();
- timeD = time1-time0;
- time0 = time1;
- if ( timeD > 1000 )
- timeD = 1000;
- if ( MoveTrains( timeD ) ) {
- wAlarm( trainPause, MoveTrainsLoop );
- trainsTimeoutPending = TRUE;
- } else {
- time0 = 0;
- trainsState = TRAINS_IDLE;
- TrainTimeEndPause();
- }
+ if (trainsState != TRAINS_RUN) {
+ time0 = 0;
+ return;
+ }
+
+ if (time0 == 0) {
+ time0 = wGetTimer();
+ }
+
+ time1 = wGetTimer();
+ timeD = time1-time0;
+ time0 = time1;
+
+ if (timeD > 1000) {
+ timeD = 1000;
+ }
+
+ if (MoveTrains(timeD)) {
+ wAlarm(trainPause, MoveTrainsLoop);
+ trainsTimeoutPending = TRUE;
+ } else {
+ time0 = 0;
+ trainsState = TRAINS_IDLE;
+ TrainTimeEndPause();
+ }
}
-static void RestartTrains( void )
+static void RestartTrains(void)
{
- if ( trainsState != TRAINS_RUN )
- TrainTimeStartPause();
- trainsState = TRAINS_RUN;
- if ( !trainsTimeoutPending )
- MoveTrainsLoop();
+ if (trainsState != TRAINS_RUN) {
+ TrainTimeStartPause();
+ }
+
+ trainsState = TRAINS_RUN;
+
+ if (!trainsTimeoutPending) {
+ MoveTrainsLoop();
+ }
}
static long trainTime0 = 0;
static long playbackTrainPause = 0;
static drawCmd_t trainMovieD = {
- NULL,
- &screenDrawFuncs,
- 0,
- 16.0,
- 0,
- {0,0}, {1,1},
- Pix2CoOrd, CoOrd2Pix };
+ NULL,
+ &screenDrawFuncs,
+ 0,
+ 16.0,
+ 0,
+ {0,0}, {1,1},
+ Pix2CoOrd, CoOrd2Pix
+};
static long trainMovieFrameDelay;
static long trainMovieFrameNext;
-static void TrainTimeEndPause( void )
+static void TrainTimeEndPause(void)
{
- if ( recordF ) {
- if (trainTime0 != 0 ) {
- long delay;
- delay = wGetTimer()-trainTime0;
- if ( delay > 0 )
- fprintf( recordF, "TRAINPAUSE %ld\n", delay );
- }
- trainTime0 = 0;
- }
+ if (recordF) {
+ if (trainTime0 != 0) {
+ long delay;
+ delay = wGetTimer()-trainTime0;
+
+ if (delay > 0) {
+ fprintf(recordF, "TRAINPAUSE %ld\n", delay);
+ }
+ }
+
+ trainTime0 = 0;
+ }
}
-static void TrainTimeStartPause( void )
+static void TrainTimeStartPause(void)
{
- if ( trainTime0 == 0 )
- trainTime0 = wGetTimer();
+ if (trainTime0 == 0) {
+ trainTime0 = wGetTimer();
+ }
}
-static BOOL_T TrainTimeDoPause( char * line )
+static BOOL_T TrainTimeDoPause(char * line)
{
- BOOL_T drawCarEnable2;
- playbackTrainPause = atol( line );
-LOG( log_trainPlayback, 1, ( "DoPause %ld\n", playbackTrainPause ) );
- trainsState = TRAINS_RUN;
- if ( trainMovieFrameDelay > 0 ) {
- drawCarEnable2 = drawCarEnable; drawCarEnable = TRUE;
- TakeSnapshot( &trainMovieD );
- drawCarEnable = drawCarEnable2;
-LOG( log_trainPlayback, 1, ( "SNAP 0\n" ) );
- trainMovieFrameNext = trainMovieFrameDelay;
- }
- /*MoveTrains();*/
- while ( playbackTrainPause > 0 ) {
- if ( playbackTrainPause > trainPause ) {
- wPause( trainPause );
- MoveTrains( trainPause );
- playbackTrainPause -= trainPause;
- if ( trainMovieFrameDelay > 0 )
- trainMovieFrameNext -= trainPause;
- } else {
- wPause( playbackTrainPause );
- MoveTrains( playbackTrainPause );
- if ( trainMovieFrameDelay > 0 )
- trainMovieFrameNext -= playbackTrainPause;
- playbackTrainPause = 0;
- }
- if ( trainMovieFrameDelay > 0 &&
- trainMovieFrameNext <= 0 ) {
- drawCarEnable2 = drawCarEnable; drawCarEnable = TRUE;
- TakeSnapshot( &trainMovieD );
- drawCarEnable = drawCarEnable2;
-LOG( log_trainPlayback, 1, ( "SNAP %ld\n", trainMovieFrameNext ) );
- trainMovieFrameNext = trainMovieFrameDelay;
- }
- }
- return TRUE;
-}
+ BOOL_T drawCarEnable2;
+ playbackTrainPause = atol(line);
+ LOG(log_trainPlayback, 1, ("DoPause %ld\n", playbackTrainPause));
+ trainsState = TRAINS_RUN;
+
+ if (trainMovieFrameDelay > 0) {
+ drawCarEnable2 = drawCarEnable;
+ drawCarEnable = TRUE;
+ TakeSnapshot(&trainMovieD);
+ drawCarEnable = drawCarEnable2;
+ LOG(log_trainPlayback, 1, ("SNAP 0\n"));
+ trainMovieFrameNext = trainMovieFrameDelay;
+ }
+
+ /*MoveTrains();*/
+ while (playbackTrainPause > 0) {
+ if (playbackTrainPause > trainPause) {
+ wPause(trainPause);
+ MoveTrains(trainPause);
+ playbackTrainPause -= trainPause;
+
+ if (trainMovieFrameDelay > 0) {
+ trainMovieFrameNext -= trainPause;
+ }
+ } else {
+ wPause(playbackTrainPause);
+ MoveTrains(playbackTrainPause);
+
+ if (trainMovieFrameDelay > 0) {
+ trainMovieFrameNext -= playbackTrainPause;
+ }
+
+ playbackTrainPause = 0;
+ }
+
+ if (trainMovieFrameDelay > 0 &&
+ trainMovieFrameNext <= 0) {
+ drawCarEnable2 = drawCarEnable;
+ drawCarEnable = TRUE;
+ TakeSnapshot(&trainMovieD);
+ drawCarEnable = drawCarEnable2;
+ LOG(log_trainPlayback, 1, ("SNAP %ld\n", trainMovieFrameNext));
+ trainMovieFrameNext = trainMovieFrameDelay;
+ }
+ }
+
+ return TRUE;
+}
+
+
+static BOOL_T TrainDoMovie(char * line)
+{
+ /* on/off, scale, orig, size */
+ long fps;
+ if (trainMovieD.dpi == 0) {
+ trainMovieD.dpi = mainD.dpi;
+ }
-static BOOL_T TrainDoMovie( char * line )
-{
- /* on/off, scale, orig, size */
- long fps;
- if ( trainMovieD.dpi == 0 )
- trainMovieD.dpi = mainD.dpi;
- if ( !GetArgs( line, "lfpp", &fps, &trainMovieD.scale, &trainMovieD.orig, &trainMovieD.size ) )
- return FALSE;
- if ( fps > 0 ) {
- trainMovieFrameDelay = 1000/fps;
- } else {
- trainMovieFrameDelay = 0;
- }
- trainMovieFrameNext = 0;
- return TRUE;
+ if (!GetArgs(line, "lfpp", &fps, &trainMovieD.scale, &trainMovieD.orig,
+ &trainMovieD.size)) {
+ return FALSE;
+ }
+
+ if (fps > 0) {
+ trainMovieFrameDelay = 1000/fps;
+ } else {
+ trainMovieFrameDelay = 0;
+ }
+
+ trainMovieFrameNext = 0;
+ return TRUE;
}
-EXPORT void AttachTrains( void )
+void AttachTrains(void)
{
- track_p car;
- track_p loco;
- struct extraData * xx;
- coOrd pos;
- track_p trk;
- ANGLE_T angle;
- EPINX_T ep0, ep1;
- int dir;
-
- for ( car=NULL; TrackIterate( &car ); ) {
- ClrTrkBits( car, TB_CARATTACHED );
- if ( GetTrkType(car) != T_CAR )
- continue;
- xx = GetTrkExtraData(car);
- ClrProcessed(xx);
- }
- for ( car=NULL; TrackIterate( &car ); ) {
- if ( GetTrkType(car) != T_CAR )
- continue;
- xx = GetTrkExtraData(car);
- if ( IsProcessed(xx) )
- continue;
- loco = FindMasterLoco( car, NULL );
- if ( loco != NULL )
- xx = GetTrkExtraData(loco);
- else
- loco = car;
- pos = xx->trvTrk.pos;
- if ( xx->status == ST_Crashed )
- continue;
- TRK_ITERATE(trk) {
- if ( trk == xx->trvTrk.trk )
- break;
- }
- if ( trk!=NULL && !QueryTrack( trk, Q_ISTRACK ) )
- trk = NULL;
- if ( trk==NULL || GetTrkDistance(trk,pos)>trackGauge*2.0 )
- trk = OnTrack2( &pos, FALSE, TRUE, FALSE );
- if ( trk!=NULL ) {
- /*if ( trk == xx->trvTrk.trk )
- continue;*/
- angle = GetAngleAtPoint( trk, pos, &ep0, &ep1 );
- if ( NormalizeAngle( xx->trvTrk.angle-angle+90 ) > 180 )
- angle = NormalizeAngle(angle+180);
- PlaceTrainInit( loco, trk, pos, angle, TRUE );
- } else {
- PlaceTrainInit( loco, NULL, xx->trvTrk.pos, xx->trvTrk.angle, FALSE );
- }
- dir = 0;
- WALK_CARS_START( loco, xx, dir )
- WALK_CARS_END( loco, xx, dir )
- dir = 1-dir;
- WALK_CARS_START( loco, xx, dir )
- SetProcessed(xx);
- if ( xx->trvTrk.trk ) {
- SetTrkBits( xx->trvTrk.trk, TB_CARATTACHED );
- xx->status = ST_StopManual;
- } else {
- xx->status = ST_NotOnTrack;
- }
- WALK_CARS_END( loco, xx, dir )
- }
- for ( car=NULL; TrackIterate( &car ); ) {
- if ( GetTrkType(car) != T_CAR )
- continue;
- xx = GetTrkExtraData(car);
- ClrProcessed(xx);
- }
-}
+ track_p car;
+ track_p loco;
+ struct extraData * xx;
+ coOrd pos;
+ track_p trk;
+ ANGLE_T angle;
+ EPINX_T ep0, ep1;
+ int dir;
+
+ for (car=NULL; TrackIterate(&car);) {
+ ClrTrkBits(car, TB_CARATTACHED);
+
+ if (GetTrkType(car) != T_CAR) {
+ continue;
+ }
+
+ xx = GetTrkExtraData(car);
+ ClrProcessed(xx);
+ }
+
+ for (car=NULL; TrackIterate(&car);) {
+ if (GetTrkType(car) != T_CAR) {
+ continue;
+ }
+
+ xx = GetTrkExtraData(car);
+
+ if (IsProcessed(xx)) {
+ continue;
+ }
+
+ loco = FindMasterLoco(car, NULL);
+
+ if (loco != NULL) {
+ xx = GetTrkExtraData(loco);
+ } else {
+ loco = car;
+ }
+
+ pos = xx->trvTrk.pos;
+
+ if (xx->status == ST_Crashed) {
+ continue;
+ }
+
+ TRK_ITERATE(trk) {
+ if (trk == xx->trvTrk.trk) {
+ break;
+ }
+ }
+
+ if (trk!=NULL && !QueryTrack(trk, Q_ISTRACK)) {
+ trk = NULL;
+ }
+ if (trk==NULL || GetTrkDistance(trk,&pos)>trackGauge*2.0) {
+ trk = OnTrack2(&pos, FALSE, TRUE, FALSE, NULL);
+ }
+
+ if (trk!=NULL) {
+ /*if ( trk == xx->trvTrk.trk )
+ continue;*/
+ angle = GetAngleAtPoint(trk, pos, &ep0, &ep1);
+
+ if (NormalizeAngle(xx->trvTrk.angle-angle+90) > 180) {
+ angle = NormalizeAngle(angle+180);
+ }
+
+ PlaceTrainInit(loco, trk, pos, angle, TRUE);
+ } else {
+ PlaceTrainInit(loco, NULL, xx->trvTrk.pos, xx->trvTrk.angle, FALSE);
+ }
+
+ dir = 0;
+ WALK_CARS_START(loco, xx, dir)
+ WALK_CARS_END(loco, xx, dir)
+ dir = 1-dir;
+ WALK_CARS_START(loco, xx, dir)
+ SetProcessed(xx);
+
+ if (xx->trvTrk.trk) {
+ SetTrkBits(xx->trvTrk.trk, TB_CARATTACHED);
+ xx->status = ST_StopManual;
+ } else {
+ xx->status = ST_NotOnTrack;
+ }
+
+ WALK_CARS_END(loco, xx, dir)
+ }
+
+ for (car=NULL; TrackIterate(&car);) {
+ if (GetTrkType(car) != T_CAR) {
+ continue;
+ }
+
+ xx = GetTrkExtraData(car);
+ ClrProcessed(xx);
+ }
+}
+
+
+static void UpdateTrainAttachment(void)
+{
+ track_p trk;
+ struct extraData * xx;
+ for (trk=NULL; TrackIterate(&trk);) {
+ ClrTrkBits(trk, TB_CARATTACHED);
+ }
-static void UpdateTrainAttachment( void )
-{
- track_p trk;
- struct extraData * xx;
- for ( trk=NULL; TrackIterate( &trk ); ) {
- ClrTrkBits( trk, TB_CARATTACHED );
- }
- for ( trk=NULL; TrackIterate( &trk ); ) {
- if ( GetTrkType(trk) == T_CAR ) {
- xx = GetTrkExtraData(trk);
- if ( xx->trvTrk.trk != NULL )
- SetTrkBits( xx->trvTrk.trk, TB_CARATTACHED );
- }
- }
+ for (trk=NULL; TrackIterate(&trk);) {
+ if (GetTrkType(trk) == T_CAR) {
+ xx = GetTrkExtraData(trk);
+
+ if (xx->trvTrk.trk != NULL) {
+ SetTrkBits(xx->trvTrk.trk, TB_CARATTACHED);
+ }
+ }
+ }
}
static BOOL_T TrainOnMovableTrack(
- track_p trk,
- track_p *trainR )
+ track_p trk,
+ track_p *trainR)
{
- track_p train;
- struct extraData * xx;
- int dir;
-
- for ( train=NULL; TrackIterate(&train); ) {
- if ( GetTrkType(train) != T_CAR )
- continue;
- xx = GetTrkExtraData(train);
- if ( IsOnTrack(xx) ) {
- if ( xx->trvTrk.trk == trk )
- break;
- }
- }
- *trainR = train;
- if ( train == NULL ) {
- return TRUE;
- }
- dir = 0;
- WALK_CARS_START( train, xx, dir )
- WALK_CARS_END( train, xx, dir )
- dir = 1-dir;
- WALK_CARS_START( train, xx, dir )
- if ( xx->trvTrk.trk != trk ) {
- ErrorMessage( MSG_CANT_MOVE_UNDER_TRAIN );
- return FALSE;
- }
- WALK_CARS_END( train, xx, dir )
- train = FindMasterLoco( train, NULL );
- if ( train != NULL )
- *trainR = train;
- return TRUE;
+ track_p train;
+ struct extraData * xx;
+ int dir;
+
+ for (train=NULL; TrackIterate(&train);) {
+ if (GetTrkType(train) != T_CAR) {
+ continue;
+ }
+
+ xx = GetTrkExtraData(train);
+
+ if (IsOnTrack(xx)) {
+ if (xx->trvTrk.trk == trk) {
+ break;
+ }
+ }
+ }
+
+ *trainR = train;
+
+ if (train == NULL) {
+ return TRUE;
+ }
+
+ dir = 0;
+ WALK_CARS_START(train, xx, dir)
+ WALK_CARS_END(train, xx, dir)
+ dir = 1-dir;
+ WALK_CARS_START(train, xx, dir)
+
+ if (xx->trvTrk.trk != trk) {
+ ErrorMessage(MSG_CANT_MOVE_UNDER_TRAIN);
+ return FALSE;
+ }
+
+ WALK_CARS_END(train, xx, dir)
+ train = FindMasterLoco(train, NULL);
+
+ if (train != NULL) {
+ *trainR = train;
+ }
+
+ return TRUE;
}
/*
@@ -2026,320 +2404,358 @@ static track_p trainFuncCar;
static coOrd trainFuncPos;
static wButton_p trainPauseB;
-#ifdef LATER
-static char * newCarLabels[3] = { N_("Road"), N_("Number"), NULL };
-#endif
-
-static STATUS_T CmdTrain( wAction_t action, coOrd pos )
+static STATUS_T CmdTrain(wAction_t action, coOrd pos)
{
- track_p trk0, trk1;
- static track_p currCar;
- coOrd pos0, pos1;
- static coOrd delta;
- ANGLE_T angle1;
- EPINX_T ep0, ep1;
- int dir;
- struct extraData * xx=NULL;
- DIST_T dist;
- wPos_t w, h;
-
- switch (action) {
-
- case C_START:
- /*UndoStart( "Trains", "Trains" );*/
- UndoSuspend();
- programMode = MODE_TRAIN;
- drawCarEnable = FALSE;
- doDrawTurnoutPosition = 1;
- DoChangeNotification( CHANGE_PARAMS|CHANGE_TOOLBAR );
- if ( CarAvailableCount() <= 0 ) {
- if ( NoticeMessage( MSG_NO_CARS, _("Yes"), _("No") ) > 0 ) {
- DoCarDlg();
- DoChangeNotification( CHANGE_PARAMS );
- }
- }
- EnableCommands();
- if ( curTrainDlg == NULL )
- curTrainDlg = CreateTrainControlDlg();
- curTrainDlg->train = NULL;
-#ifdef LATER
- if ( trainW == NULL )
- trainW = ParamCreateDialog( MakeWindowTitle(_("Train")), NULL, trainPGp );
- ParamLoadControls( trainPGp );
- wListClear( (wList_p)trainPLs[0].control );
-#endif
- wListClear( (wList_p)curTrainDlg->trainPGp->paramPtr[I_LIST].control );
- Dtrain.state = 0;
- trk0 = NULL;
- tempSegs_da.cnt = 0;
- DYNARR_SET( trkSeg_t, tempSegs_da, 8 );
- /*MainRedraw();*/
- /*wDrawSaveImage( mainD.d );*/
- /*trainEnable = FALSE;*/
- RestartTrains();
- wButtonSetLabel( trainPauseB, (char*)goI );
- trainTime0 = 0;
- AttachTrains();
- DrawAllCars();
- curTrainDlg->train = NULL;
- curTrainDlg->speed = -1;
- wDrawClear( (wDraw_p)curTrainDlg->trainPGp->paramPtr[I_SLIDER].control );
- LocoListInit();
- ControllerDialogSync( curTrainDlg );
- wShow( curTrainDlg->win );
- wControlShow( (wControl_p)newcarB, (toolbarSet&(1<<BG_HOTBAR)) == 0 );
- currCarItemPtr = NULL;
- return C_CONTINUE;
-
- case C_TEXT:
- if ( Dtrain.state == 0 )
- return C_CONTINUE;
- else
- return C_CONTINUE;
-
- case C_DOWN:
- /*trainEnable = FALSE;*/
- InfoMessage( "" );
- if ( trainsState == TRAINS_RUN ) {
- trainsState = TRAINS_PAUSE;
- TrainTimeEndPause();
- }
- pos0 = pos;
- if ( currCarItemPtr != NULL ) {
-#ifdef LATER
- ParamLoadData( &newCarPG );
-#endif
- currCar = NewCar( -1, currCarItemPtr, zero, 0.0 );
- CarItemUpdate( currCarItemPtr );
- HotBarCancel();
- if ( currCar == NULL ) {
- LOG1( log_error, ( "Train: currCar became NULL 1\n" ) )
- return C_CONTINUE;
- }
- xx = GetTrkExtraData(currCar);
- dist = CarItemCoupledLength(xx->item)/2.0;
- Translate( &pos, xx->trvTrk.pos, xx->trvTrk.angle, dist );
- SetTrkEndPoint( currCar, 0, pos, xx->trvTrk.angle );
- Translate( &pos, xx->trvTrk.pos, xx->trvTrk.angle+180.0, dist );
- SetTrkEndPoint( currCar, 1, pos, NormalizeAngle(xx->trvTrk.angle+180.0) );
- /*xx->state |= (xx->item->options&CAR_DESC_BITS);*/
- ClrLocoMaster(xx);
- if ( CarItemIsLoco(xx->item) ) {
- SetLocoMaster(xx);
- LocoListChangeEntry( NULL, currCar );
- if ( currCar == NULL ) {
- LOG1( log_error, ( "Train: currCar became NULL 2\n" ) )
- return C_CONTINUE;
- }
- }
-#ifdef LATER
- wPrefSetString( "Car Road Name", xx->ITEM->title, newCarRoad );
- number = strtol( CarItemNumber(xx->item), &cp, 10 );
- if ( cp == NULL || *cp != 0 )
- number = -1;
- wPrefSetInteger( "Car Number", xx->ITEM->title, number );
-#endif
- if( (trk0 = OnTrack( &pos0, FALSE, TRUE ) ) ) {
- xx->trvTrk.angle = GetAngleAtPoint( trk0, pos0, &ep0, &ep1 );
- if ( NormalizeAngle( FindAngle( pos, pos0 ) - xx->trvTrk.angle ) > 180.0 )
- xx->trvTrk.angle = NormalizeAngle( xx->trvTrk.angle + 180 );
- xx->status = ST_StopManual;
- } else {
- xx->trvTrk.angle = 90;
- }
- PlaceTrainInit( currCar, trk0, pos0, xx->trvTrk.angle, (MyGetKeyState()&WKEY_SHIFT) == 0 );
- /*DrawCars( &tempD, currCar, TRUE );*/
- } else {
- currCar = FindCar( &pos );
- delta.x = pos.x - pos0.x;
- delta.y = pos.y - pos0.y;
- if ( logTable(log_trainMove).level >= 1 ) {
- if ( currCar ) {
- xx = GetTrkExtraData(currCar);
- LogPrintf( "selected %s\n", CarItemNumber(xx->item) );
- for ( dir=0; dir<2; dir++ ) {
- int dir1 = dir;
- track_p car1 = currCar;
- struct extraData * xx1 = GetTrkExtraData(car1);
- LogPrintf( "dir=%d\n", dir1 );
- WALK_CARS_START( car1, xx1, dir1 )
- LogPrintf( " %s [%0.3f,%d]\n", CarItemNumber(xx1->item), xx1->trvTrk.angle, dir1 );
- WALK_CARS_END( car1, xx1, dir1 )
- }
- }
- }
- }
- if ( currCar == NULL )
- return C_CONTINUE;
- trk0 = FindMasterLoco( currCar, NULL );
- if ( trk0 )
- SetCurTrain( trk0 );
- DrawAllCars();
- return C_CONTINUE;
-
- case C_MOVE:
- if ( currCar == NULL )
- return C_CONTINUE;
- pos.x += delta.x;
- pos.y += delta.y;
- pos0 = pos;
- /*DrawCars( &tempD, currCar, FALSE );*/
- xx = GetTrkExtraData(currCar);
- trk0 = OnTrack( &pos0, FALSE, TRUE );
- if ( /*currCarItemPtr != NULL &&*/ trk0 ) {
- angle1 = GetAngleAtPoint( trk0, pos0, &ep0, &ep1 );
- if ( currCarItemPtr != NULL ) {
- if ( NormalizeAngle( FindAngle( pos, pos0 ) - angle1 ) > 180.0 )
- angle1 = NormalizeAngle( angle1 + 180 );
- } else {
- if ( NormalizeAngle( xx->trvTrk.angle - angle1 + 90.0 ) > 180.0 )
- angle1 = NormalizeAngle( angle1 + 180 );
- }
- xx->trvTrk.angle = angle1;
- }
- tempSegs_da.cnt = 1;
- PlaceTrainInit( currCar, trk0, pos0, xx->trvTrk.angle, (MyGetKeyState()&WKEY_SHIFT) == 0 );
- ControllerDialogSync( curTrainDlg );
- DrawAllCars();
- return C_CONTINUE;
-
-
- case C_UP:
- if ( currCar != NULL ) {
- trk0 = FindMasterLoco( currCar, NULL );
- if ( trk0 ) {
- xx = GetTrkExtraData( trk0 );
- if ( !IsOnTrack(xx) || xx->speed <= 0 )
- StopTrain( trk0, ST_StopManual );
- }
- Dtrain.state = 1;
- /*MainRedraw();*/
- ControllerDialogSync( curTrainDlg );
- }
- DrawAllCars();
- InfoSubstituteControls( NULL, NULL );
- currCar = trk0 = NULL;
- currCarItemPtr = NULL;
- /*trainEnable = TRUE;*/
- if ( trainsState == TRAINS_PAUSE ) {
- RestartTrains();
- }
- return C_CONTINUE;
-
- case C_LCLICK:
- if ( MyGetKeyState() & WKEY_SHIFT ) {
- pos0 = pos;
- programMode = MODE_DESIGN;
- if ( (trk0=OnTrack(&pos,FALSE,TRUE)) &&
- QueryTrack( trk0, Q_CAN_NEXT_POSITION ) &&
- TrainOnMovableTrack( trk0, &trk1) ) {
- if ( trk1 ) {
- xx = GetTrkExtraData(trk1);
- pos1 = xx->trvTrk.pos;
- angle1 = xx->trvTrk.angle;
- } else {
- pos1 = pos0;
- angle1 = 0;
- }
- AdvancePositionIndicator( trk0, pos0, &pos1, &angle1 );
- if ( trk1 ) {
- xx->trvTrk.pos = pos1;
- xx->trvTrk.angle = angle1;
- PlaceTrain( trk1, FALSE, TRUE );
- DrawAllCars();
- }
- }
- programMode = MODE_TRAIN;
- trk0 = NULL;
+ track_p trk0, trk1;
+ static track_p currCar;
+ coOrd pos0, pos1;
+ static coOrd delta;
+ ANGLE_T angle1;
+ EPINX_T ep0, ep1;
+ int dir;
+ struct extraData * xx=NULL;
+ wPos_t w, h;
+
+ switch (action) {
+ case C_START:
+ /*UndoStart( "Trains", "Trains" );*/
+ UndoSuspend();
+ programMode = MODE_TRAIN;
+ drawCarEnable = FALSE;
+ doDrawTurnoutPosition = 1;
+ DoChangeNotification(CHANGE_PARAMS|CHANGE_TOOLBAR);
+
+ if (CarAvailableCount() <= 0) {
+ if (NoticeMessage(MSG_NO_CARS, _("Yes"), _("No")) > 0) {
+ DoCarDlg();
+ DoChangeNotification(CHANGE_PARAMS);
+ }
+ }
+
+ EnableCommands();
+
+ if (curTrainDlg == NULL) {
+ curTrainDlg = CreateTrainControlDlg();
+ }
+
+ curTrainDlg->train = NULL;
+ wListClear((wList_p)curTrainDlg->trainPGp->paramPtr[I_LIST].control);
+ Dtrain.state = 0;
+ trk0 = NULL;
+ tempSegs_da.cnt = 0;
+ DYNARR_SET(trkSeg_t, tempSegs_da, 8);
+ RestartTrains();
+ wButtonSetLabel(trainPauseB, (char*)goI);
+ trainTime0 = 0;
+ AttachTrains();
+ DrawAllCars();
+ curTrainDlg->train = NULL;
+ curTrainDlg->speed = -1;
+ wDrawClear((wDraw_p)curTrainDlg->trainPGp->paramPtr[I_SLIDER].control);
+ LocoListInit();
+ ControllerDialogSync(curTrainDlg);
+ wShow(curTrainDlg->win);
+ wControlShow((wControl_p)newcarB, (toolbarSet&(1<<BG_HOTBAR)) == 0);
+ currCarItemPtr = NULL;
+ return C_CONTINUE;
+
+ case C_TEXT:
+ if (Dtrain.state == 0) {
+ return C_CONTINUE;
+ } else {
+ return C_CONTINUE;
+ }
+
+ case C_DOWN:
+ /*trainEnable = FALSE;*/
+ InfoMessage("");
+
+ if (trainsState == TRAINS_RUN) {
+ trainsState = TRAINS_PAUSE;
+ TrainTimeEndPause();
+ }
+
+ pos0 = pos;
+
+ if (currCarItemPtr != NULL) {
+ DIST_T dist;
+ currCar = NewCar(-1, currCarItemPtr, zero, 0.0);
+ CarItemUpdate(currCarItemPtr);
+ HotBarCancel();
+
+ if (currCar == NULL) {
+ LOG1(log_error, ("Train: currCar became NULL 1\n"))
+ return C_CONTINUE;
+ }
+
+ xx = GetTrkExtraData(currCar);
+ dist = CarItemCoupledLength(xx->item)/2.0;
+ Translate(&pos, xx->trvTrk.pos, xx->trvTrk.angle, dist);
+ SetTrkEndPoint(currCar, 0, pos, xx->trvTrk.angle);
+ Translate(&pos, xx->trvTrk.pos, xx->trvTrk.angle+180.0, dist);
+ SetTrkEndPoint(currCar, 1, pos, NormalizeAngle(xx->trvTrk.angle+180.0));
+ /*xx->state |= (xx->item->options&CAR_DESC_BITS);*/
+ ClrLocoMaster(xx);
+
+ if (CarItemIsLoco(xx->item)) {
+ SetLocoMaster(xx);
+ LocoListChangeEntry(NULL, currCar);
+ }
+
+ if ((trk0 = OnTrack(&pos0, FALSE, TRUE))) {
+ xx->trvTrk.angle = GetAngleAtPoint(trk0, pos0, &ep0, &ep1);
+
+ if (NormalizeAngle(FindAngle(pos, pos0) - xx->trvTrk.angle) > 180.0) {
+ xx->trvTrk.angle = NormalizeAngle(xx->trvTrk.angle + 180);
+ }
+
+ xx->status = ST_StopManual;
+ } else {
+ xx->trvTrk.angle = 90;
+ }
+
+ PlaceTrainInit(currCar, trk0, pos0, xx->trvTrk.angle,
+ (MyGetKeyState()&WKEY_SHIFT) == 0);
+ /*DrawCars( &tempD, currCar, TRUE );*/
+ } else {
+ currCar = FindCar(&pos);
+ delta.x = pos.x - pos0.x;
+ delta.y = pos.y - pos0.y;
+
+ if (logTable(log_trainMove).level >= 1) {
+ if (currCar) {
+ xx = GetTrkExtraData(currCar);
+ LogPrintf("selected %s\n", CarItemNumber(xx->item));
+
+ for (dir=0; dir<2; dir++) {
+ int dir1 = dir;
+ track_p car1 = currCar;
+ struct extraData * xx1 = GetTrkExtraData(car1);
+ LogPrintf("dir=%d\n", dir1);
+ WALK_CARS_START(car1, xx1, dir1)
+ LogPrintf(" %s [%0.3f,%d]\n", CarItemNumber(xx1->item), xx1->trvTrk.angle,
+ dir1);
+ WALK_CARS_END(car1, xx1, dir1)
+ }
+ }
+ }
+ }
+
+ if (currCar == NULL) {
+ return C_CONTINUE;
+ }
+
+ trk0 = FindMasterLoco(currCar, NULL);
+
+ if (trk0) {
+ SetCurTrain(trk0);
+ }
+
+ DrawAllCars();
+ return C_CONTINUE;
+
+ case C_MOVE:
+ if (currCar == NULL) {
+ return C_CONTINUE;
+ }
+
+ pos.x += delta.x;
+ pos.y += delta.y;
+ pos0 = pos;
+ /*DrawCars( &tempD, currCar, FALSE );*/
+ xx = GetTrkExtraData(currCar);
+ trk0 = OnTrack(&pos0, FALSE, TRUE);
+
+ if (/*currCarItemPtr != NULL &&*/ trk0) {
+ angle1 = GetAngleAtPoint(trk0, pos0, &ep0, &ep1);
+
+ if (currCarItemPtr != NULL) {
+ if (NormalizeAngle(FindAngle(pos, pos0) - angle1) > 180.0) {
+ angle1 = NormalizeAngle(angle1 + 180);
+ }
+ } else {
+ if (NormalizeAngle(xx->trvTrk.angle - angle1 + 90.0) > 180.0) {
+ angle1 = NormalizeAngle(angle1 + 180);
+ }
+ }
+
+ xx->trvTrk.angle = angle1;
+ }
+
+ tempSegs_da.cnt = 1;
+ PlaceTrainInit(currCar, trk0, pos0, xx->trvTrk.angle,
+ (MyGetKeyState()&WKEY_SHIFT) == 0);
+ ControllerDialogSync(curTrainDlg);
+ DrawAllCars();
+ return C_CONTINUE;
+
+ case C_UP:
+ if (currCar != NULL) {
+ trk0 = FindMasterLoco(currCar, NULL);
+
+ if (trk0) {
+ xx = GetTrkExtraData(trk0);
+
+ if (!IsOnTrack(xx) || xx->speed <= 0) {
+ StopTrain(trk0, ST_StopManual);
+ }
+ }
+
+ Dtrain.state = 1;
+ /*MainRedraw();*/
+ ControllerDialogSync(curTrainDlg);
+ }
+
+ DrawAllCars();
+ InfoSubstituteControls(NULL, NULL);
+ currCar = trk0 = NULL;
+ currCarItemPtr = NULL;
+
+ /*trainEnable = TRUE;*/
+ if (trainsState == TRAINS_PAUSE) {
+ RestartTrains();
+ }
+
+ return C_CONTINUE;
+
+ case C_LCLICK:
+ if (MyGetKeyState() & WKEY_SHIFT) {
+ pos0 = pos;
+ programMode = MODE_DESIGN;
+
+ if ((trk0=OnTrack(&pos,FALSE,TRUE)) &&
+ QueryTrack(trk0, Q_CAN_NEXT_POSITION) &&
+ TrainOnMovableTrack(trk0, &trk1)) {
+ if (trk1) {
+ xx = GetTrkExtraData(trk1);
+ pos1 = xx->trvTrk.pos;
+ angle1 = xx->trvTrk.angle;
+ } else {
+ pos1 = pos0;
+ angle1 = 0;
+ }
+
+ AdvancePositionIndicator(trk0, pos0, &pos1, &angle1);
+
+ if (trk1) {
+ xx->trvTrk.pos = pos1;
+ xx->trvTrk.angle = angle1;
+ PlaceTrain(trk1, FALSE, TRUE);
+ DrawAllCars();
+ }
+ }
+
+ programMode = MODE_TRAIN;
+ trk0 = NULL;
MainRedraw(); //Make sure track is redrawn after switch thrown
- } else {
- trk0 = FindCar( &pos );
- if ( trk0 == NULL )
- return C_CONTINUE;
- trk0 = FindMasterLoco( trk0, NULL );
- if ( trk0 == NULL )
- return C_CONTINUE;
- SetCurTrain( trk0 );
- }
- return C_CONTINUE;
-
- case C_RCLICK:
- trainFuncPos = pos;
- trainFuncCar = FindCar( &pos );
- if ( trainFuncCar == NULL ||
- GetTrkType(trainFuncCar) != T_CAR )
- return C_CONTINUE;
- xx = GetTrkExtraData( trainFuncCar );
- trk0 = FindMasterLoco(trainFuncCar,NULL);
- dir = IsAligned( xx->trvTrk.angle, FindAngle(xx->trvTrk.pos,trainFuncPos) ) ? 0 : 1;
- wMenuPushEnable( trainPopupMI[DO_UNCOUPLE], GetTrkEndTrk( trainFuncCar, dir )!=NULL );
- wMenuPushEnable( trainPopupMI[DO_MUMASTER], CarItemIsLoco(xx->item) && !IsLocoMaster(xx) );
- if ( trk0 ) xx = GetTrkExtraData(trk0);
- wMenuPushEnable( trainPopupMI[DO_CHANGEDIR], trk0!=NULL );
- wMenuPushEnable( trainPopupMI[DO_STOP], trk0!=NULL && xx->speed>0 );
- /*trainEnable = FALSE;*/
-#ifdef LATER
- if ( trainsState == TRAINS_RUN )
- trainsState = TRAINS_PAUSE;
-#endif
- trk0 = FindMasterLoco( trainFuncCar, NULL );
- if ( trk0 )
- SetCurTrain( trk0 );
- if ( !inPlayback )
- wMenuPopupShow( trainPopupM );
- return C_CONTINUE;
-
- case C_REDRAW:
-#ifdef LATER
- if (Dtrain.state == 1 && !suppressTrainRedraw) {
- mainD.funcs->options = wDrawOptTemp;
- mainD.funcs->options = 0;
- }
-#endif
- wDrawSaveImage(mainD.d);
- DrawAllCars();
- wWinGetSize( mainW, &w, &h );
- w -= wControlGetPosX( newCarControls[0] ) + 4;
- if ( w > 20 )
- wListSetSize( (wList_p)newCarControls[0], w, wControlGetHeight( newCarControls[0] ) );
- return C_CONTINUE;
-
- case C_CANCEL:
- /*trainEnable = FALSE;*/
- trainsState = TRAINS_STOP;
- TrainTimeEndPause();
- LOG( log_trainMove, 1, ( "Train Cancel\n" ) )
- Dtrain.state = 0;
- doDrawTurnoutPosition = 0;
- drawCarEnable = TRUE;
- programMode = MODE_DESIGN;
- UpdateTrainAttachment();
- UndoResume();
- DoChangeNotification( CHANGE_PARAMS|CHANGE_TOOLBAR );
- if ( curTrainDlg->win )
- wHide( curTrainDlg->win );
- MainRedraw();
- curTrainDlg->train = NULL;
- return C_CONTINUE;
-
-
- case C_CONFIRM:
- /*trainEnable = FALSE;*/
- if ( trainsState != TRAINS_STOP ) {
- trainsState = TRAINS_STOP;
- wButtonSetLabel( trainPauseB, (char*)stopI );
- TrainTimeEndPause();
- }
- currCar = NULL;
- currCarItemPtr = NULL;
- HotBarCancel();
- InfoSubstituteControls( NULL, NULL );
- return C_TERMINATE;
-
- }
-
- return C_CONTINUE;
-
+ MapRedraw();
+ } else {
+ trk0 = FindCar(&pos);
+
+ if (trk0 == NULL) {
+ return C_CONTINUE;
+ }
+
+ trk0 = FindMasterLoco(trk0, NULL);
+
+ if (trk0 == NULL) {
+ return C_CONTINUE;
+ }
+
+ SetCurTrain(trk0);
+ }
+
+ return C_CONTINUE;
+
+ case C_RCLICK:
+ trainFuncPos = pos;
+ trainFuncCar = FindCar(&pos);
+
+ if (trainFuncCar == NULL ||
+ GetTrkType(trainFuncCar) != T_CAR) {
+ return C_CONTINUE;
+ }
+
+ xx = GetTrkExtraData(trainFuncCar);
+ trk0 = FindMasterLoco(trainFuncCar,NULL);
+ dir = IsAligned(xx->trvTrk.angle, FindAngle(xx->trvTrk.pos,
+ trainFuncPos)) ? 0 : 1;
+ wMenuPushEnable(trainPopupMI[DO_UNCOUPLE], GetTrkEndTrk(trainFuncCar,
+ dir)!=NULL);
+ wMenuPushEnable(trainPopupMI[DO_MUMASTER], CarItemIsLoco(xx->item) &&
+ !IsLocoMaster(xx));
+
+ if (trk0) {
+ xx = GetTrkExtraData(trk0);
+ }
+
+ wMenuPushEnable(trainPopupMI[DO_CHANGEDIR], trk0!=NULL);
+ wMenuPushEnable(trainPopupMI[DO_STOP], trk0!=NULL && xx->speed>0);
+ /*trainEnable = FALSE;*/
+ trk0 = FindMasterLoco(trainFuncCar, NULL);
+
+ if (trk0) {
+ SetCurTrain(trk0);
+ }
+
+ if (!inPlayback) {
+ wMenuPopupShow(trainPopupM);
+ }
+
+ return C_CONTINUE;
+
+ case C_REDRAW:
+ wDrawSaveImage(mainD.d);
+ DrawAllCars();
+ wWinGetSize(mainW, &w, &h);
+ w -= wControlGetPosX(newCarControls[0]) + 4;
+
+ if (w > 20) {
+ wListSetSize((wList_p)newCarControls[0], w,
+ wControlGetHeight(newCarControls[0]));
+ }
+
+ return C_CONTINUE;
+
+ case C_CANCEL:
+ /*trainEnable = FALSE;*/
+ trainsState = TRAINS_STOP;
+ TrainTimeEndPause();
+ LOG(log_trainMove, 1, ("Train Cancel\n"))
+ Dtrain.state = 0;
+ doDrawTurnoutPosition = 0;
+ drawCarEnable = TRUE;
+ programMode = MODE_DESIGN;
+ UpdateTrainAttachment();
+ UndoResume();
+ DoChangeNotification(CHANGE_PARAMS|CHANGE_TOOLBAR);
+
+ if (curTrainDlg->win) {
+ wHide(curTrainDlg->win);
+ }
+
+ MainRedraw();
+ MapRedraw();
+ curTrainDlg->train = NULL;
+ return C_CONTINUE;
+
+ case C_CONFIRM:
+
+ /*trainEnable = FALSE;*/
+ if (trainsState != TRAINS_STOP) {
+ trainsState = TRAINS_STOP;
+ wButtonSetLabel(trainPauseB, (char*)stopI);
+ TrainTimeEndPause();
+ }
+
+ currCar = NULL;
+ currCarItemPtr = NULL;
+ HotBarCancel();
+ InfoSubstituteControls(NULL, NULL);
+ return C_TERMINATE;
+ }
+
+ return C_CONTINUE;
}
@@ -2347,11 +2763,11 @@ static STATUS_T CmdTrain( wAction_t action, coOrd pos )
*
*/
-EXPORT STATUS_T CmdCarDescAction(
- wAction_t action,
- coOrd pos )
+STATUS_T CmdCarDescAction(
+ wAction_t action,
+ coOrd pos)
{
- return CmdTrain( action, pos );
+ return CmdTrain(action, pos);
}
#include "bitmaps/train.xpm"
@@ -2362,225 +2778,269 @@ EXPORT STATUS_T CmdCarDescAction(
#include "bitmaps/ballred.xpm"
-static void CmdTrainStopGo( void * junk )
+static void CmdTrainStopGo(void * junk)
{
- wIcon_p icon;
- if ( trainsState == TRAINS_STOP ) {
- icon = goI;
- RestartTrains();
- } else {
- trainsState = TRAINS_STOP;
- icon = stopI;
- TrainTimeEndPause();
- }
- ControllerDialogSync( curTrainDlg );
- wButtonSetLabel( trainPauseB, (char*)icon );
- if ( recordF )
- fprintf( recordF, "TRAINSTOPGO %s\n", trainsState==TRAINS_STOP?"STOP":"GO" );
+ wIcon_p icon;
+
+ if (trainsState == TRAINS_STOP) {
+ icon = goI;
+ RestartTrains();
+ } else {
+ trainsState = TRAINS_STOP;
+ icon = stopI;
+ TrainTimeEndPause();
+ }
+
+ ControllerDialogSync(curTrainDlg);
+ wButtonSetLabel(trainPauseB, (char*)icon);
+
+ if (recordF) {
+ fprintf(recordF, "TRAINSTOPGO %s\n", trainsState==TRAINS_STOP?"STOP":"GO");
+ }
}
-static BOOL_T TrainStopGoPlayback( char * line )
+static BOOL_T TrainStopGoPlayback(char * line)
{
- while (*line && isspace((unsigned char)*line) ) line++;
- if ( (strcasecmp( line, "STOP" ) == 0) != (trainsState == TRAINS_STOP) )
- CmdTrainStopGo(NULL);
- return TRUE;
+ while (*line && isspace((unsigned char)*line)) {
+ line++;
+ }
+
+ if ((strcasecmp(line, "STOP") == 0) != (trainsState == TRAINS_STOP)) {
+ CmdTrainStopGo(NULL);
+ }
+
+ return TRUE;
}
-static void CmdTrainExit( void * junk )
+static void CmdTrainExit(void * junk)
{
- Reset();
- InfoSubstituteControls( NULL, NULL );
- MainRedraw();
+ Reset();
+ InfoSubstituteControls(NULL, NULL);
+ MainRedraw();
+ MapRedraw();
}
static void TrainFunc(
- void * action )
+ void * action)
{
- struct extraData * xx, *xx1;
- ANGLE_T angle;
- int dir;
- track_p loco;
- track_p temp0, temp1;
- coOrd pos0, pos1;
- ANGLE_T angle0, angle1;
- EPINX_T ep0=-1, ep1=-1;
-
- if ( trainFuncCar == NULL ) {
- fprintf( stderr, "trainFunc: trainFuncCar==NULL\n" );
- return;
- }
+ struct extraData * xx, *xx1;
+ ANGLE_T angle;
+ int dir;
+ track_p loco;
+ track_p temp0, temp1;
+ coOrd pos0, pos1;
+ ANGLE_T angle0, angle1;
+ EPINX_T ep0=-1, ep1=-1;
+
+ if (trainFuncCar == NULL) {
+ fprintf(stderr, "trainFunc: trainFuncCar==NULL\n");
+ return;
+ }
+
+ xx = GetTrkExtraData(trainFuncCar);
+ angle = FindAngle(xx->trvTrk.pos, trainFuncPos);
+ angle = NormalizeAngle(angle-xx->trvTrk.angle);
+ dir = (angle>90&&angle<270);
+
+ switch ((int)(long)action) {
+ case DO_UNCOUPLE:
+ if (GetTrkEndTrk(trainFuncCar,dir)) {
+ UncoupleCars(trainFuncCar, GetTrkEndTrk(trainFuncCar,dir));
+ }
+
+ break;
+
+ case DO_FLIPCAR:
+ temp0 = GetTrkEndTrk(trainFuncCar,0);
+ pos0 = GetTrkEndPos(trainFuncCar,0);
+ angle0 = GetTrkEndAngle(trainFuncCar,0);
+ temp1 = GetTrkEndTrk(trainFuncCar,1);
+ pos1 = GetTrkEndPos(trainFuncCar,1);
+ angle1 = GetTrkEndAngle(trainFuncCar,1);
+
+ if (temp0) {
+ ep0 = GetEndPtConnectedToMe(temp0,trainFuncCar);
+ trainFuncCar->endPt[0].track = NULL;
+ temp0->endPt[ep0].track = NULL;
+ }
+
+ if (temp1) {
+ ep1 = GetEndPtConnectedToMe(temp1,trainFuncCar);
+ trainFuncCar->endPt[1].track = NULL;
+ temp1->endPt[ep1].track = NULL;
+ }
+
+ xx->direction = !xx->direction;
+ FlipTraverseTrack(&xx->trvTrk);
+ SetTrkEndPoint(trainFuncCar, 0, pos1, angle1);
+ SetTrkEndPoint(trainFuncCar, 1, pos0, angle0);
+
+ if (temp0) {
+ trainFuncCar->endPt[1].track = temp0;
+ temp0->endPt[ep0].track = trainFuncCar;
+ }
+
+ if (temp1) {
+ trainFuncCar->endPt[0].track = temp1;
+ temp1->endPt[ep1].track = trainFuncCar;
+ }
+
+ ControllerDialogSync(curTrainDlg);
+ PlaceCar(trainFuncCar);
+ break;
+
+ case DO_FLIPTRAIN:
+ FlipTrain(trainFuncCar);
+ /*PlaceTrain( trainFuncCar, xx->trk, xx->trvTrk.pos, xx->trvTrk.angle );*/
+ break;
+
+ case DO_DELCAR:
+ for (dir=0; dir<2; dir++)
+ if (GetTrkEndTrk(trainFuncCar,dir)) {
+ UncoupleCars(trainFuncCar, GetTrkEndTrk(trainFuncCar,dir));
+ }
+
+ if (CarItemIsLoco(xx->item)) {
+ LocoListChangeEntry(trainFuncCar, NULL);
+ }
+
+ trainFuncCar->deleted = TRUE;
+ /*DeleteTrack( trainFuncCar, FALSE );*/
+ CarItemUpdate(xx->item);
+ HotBarCancel();
+ InfoSubstituteControls(NULL, NULL);
+ break;
+
+ case DO_DELTRAIN:
+ dir = 0;
+ loco = FindMasterLoco(trainFuncCar, NULL);
+ WALK_CARS_START(trainFuncCar, xx, dir)
+ WALK_CARS_END(trainFuncCar, xx, dir)
+ dir = 1-dir;
+ temp0 = NULL;
+ WALK_CARS_START(trainFuncCar, xx, dir)
+
+ if (temp0) {
+ xx1 = GetTrkExtraData(temp0);
+ temp0->deleted = TRUE;
+ /*DeleteTrack( temp0, FALSE );*/
+ CarItemUpdate(xx1->item);
+ }
+
+ temp0 = trainFuncCar;
+ WALK_CARS_END(trainFuncCar, xx, dir)
+
+ if (temp0) {
+ xx1 = GetTrkExtraData(temp0);
+ temp0->deleted = TRUE;
+ /*DeleteTrack( temp0, FALSE );*/
+ CarItemUpdate(xx1->item);
+ }
+
+ if (loco) {
+ LocoListChangeEntry(loco, NULL);
+ }
+
+ HotBarCancel();
+ InfoSubstituteControls(NULL, NULL);
+ break;
+
+ case DO_MUMASTER:
+ if (CarItemIsLoco(xx->item)) {
+ loco = FindMasterLoco(trainFuncCar, NULL);
+
+ if (loco != trainFuncCar) {
+ SetLocoMaster(xx);
+ LOG(log_trainMove, 1, ("%s gets master\n", CarItemNumber(xx->item)))
+
+ if (loco) {
+ xx1 = GetTrkExtraData(loco);
+ ClrLocoMaster(xx1);
+ LOG(log_trainMove, 1, ("%s looses master\n", CarItemNumber(xx1->item)))
+ xx->speed = xx1->speed;
+ xx1->speed = 0;
+ }
+
+ LocoListChangeEntry(loco, trainFuncCar);
+ }
+ }
+
+ break;
+
+ case DO_CHANGEDIR:
+ loco = FindMasterLoco(trainFuncCar, NULL);
+
+ if (loco) {
+ xx = GetTrkExtraData(loco);
+ xx->direction = !xx->direction;
+ SetTrainDirection(loco);
+ ControllerDialogSync(curTrainDlg);
+ }
+
+ break;
+
+ case DO_STOP:
+ loco = FindMasterLoco(trainFuncCar, NULL);
+
+ if (loco) {
+ StopTrain(loco, ST_StopManual);
+ ControllerDialogSync(curTrainDlg);
+ }
+
+ break;
+ }
- xx = GetTrkExtraData(trainFuncCar);
- angle = FindAngle( xx->trvTrk.pos, trainFuncPos );
- angle = NormalizeAngle( angle-xx->trvTrk.angle );
- dir = (angle>90&&angle<270);
-
- switch ((int)(long)action) {
- case DO_UNCOUPLE:
- if ( GetTrkEndTrk(trainFuncCar,dir) )
- UncoupleCars( trainFuncCar, GetTrkEndTrk(trainFuncCar,dir) );
- break;
- case DO_FLIPCAR:
- temp0 = GetTrkEndTrk(trainFuncCar,0);
- pos0 = GetTrkEndPos(trainFuncCar,0);
- angle0 = GetTrkEndAngle(trainFuncCar,0);
- temp1 = GetTrkEndTrk(trainFuncCar,1);
- pos1 = GetTrkEndPos(trainFuncCar,1);
- angle1 = GetTrkEndAngle(trainFuncCar,1);
- if ( temp0 ) {
- ep0 = GetEndPtConnectedToMe(temp0,trainFuncCar);
- trainFuncCar->endPt[0].track = NULL;
- temp0->endPt[ep0].track = NULL;
- }
- if ( temp1 ) {
- ep1 = GetEndPtConnectedToMe(temp1,trainFuncCar);
- trainFuncCar->endPt[1].track = NULL;
- temp1->endPt[ep1].track = NULL;
- }
- xx->direction = !xx->direction;
- FlipTraverseTrack( &xx->trvTrk );
- SetTrkEndPoint( trainFuncCar, 0, pos1, angle1 );
- SetTrkEndPoint( trainFuncCar, 1, pos0, angle0 );
- if ( temp0 ) {
- trainFuncCar->endPt[1].track = temp0;
- temp0->endPt[ep0].track = trainFuncCar;
- }
- if ( temp1 ) {
- trainFuncCar->endPt[0].track = temp1;
- temp1->endPt[ep1].track = trainFuncCar;
- }
- ControllerDialogSync( curTrainDlg );
- PlaceCar( trainFuncCar );
- break;
- case DO_FLIPTRAIN:
- FlipTrain( trainFuncCar );
- /*PlaceTrain( trainFuncCar, xx->trk, xx->trvTrk.pos, xx->trvTrk.angle );*/
- break;
- case DO_DELCAR:
- for ( dir=0; dir<2; dir++ )
- if ( GetTrkEndTrk(trainFuncCar,dir) )
- UncoupleCars( trainFuncCar, GetTrkEndTrk(trainFuncCar,dir) );
- if ( CarItemIsLoco(xx->item) )
- LocoListChangeEntry( trainFuncCar, NULL );
- trainFuncCar->deleted = TRUE;
- /*DeleteTrack( trainFuncCar, FALSE );*/
- CarItemUpdate( xx->item );
- HotBarCancel();
- InfoSubstituteControls( NULL, NULL );
- break;
- case DO_DELTRAIN:
- dir = 0;
- loco = FindMasterLoco( trainFuncCar, NULL );
- WALK_CARS_START( trainFuncCar, xx, dir )
- WALK_CARS_END( trainFuncCar, xx, dir )
- dir = 1-dir;
- temp0 = NULL;
- WALK_CARS_START( trainFuncCar, xx, dir )
- if ( temp0 ) {
- xx1 = GetTrkExtraData(temp0);
- temp0->deleted = TRUE;
- /*DeleteTrack( temp0, FALSE );*/
- CarItemUpdate( xx1->item );
- }
- temp0 = trainFuncCar;
- WALK_CARS_END( trainFuncCar, xx, dir )
- if ( temp0 ) {
- xx1 = GetTrkExtraData(temp0);
- temp0->deleted = TRUE;
- /*DeleteTrack( temp0, FALSE );*/
- CarItemUpdate( xx1->item );
- }
- if ( loco )
- LocoListChangeEntry( loco, NULL );
- HotBarCancel();
- InfoSubstituteControls( NULL, NULL );
- break;
- case DO_MUMASTER:
- if ( CarItemIsLoco(xx->item) ) {
- loco = FindMasterLoco( trainFuncCar, NULL );
- if ( loco != trainFuncCar ) {
- SetLocoMaster(xx);
- LOG( log_trainMove, 1, ( "%s gets master\n", CarItemNumber(xx->item) ) )
- if ( loco ) {
- xx1 = GetTrkExtraData( loco );
- ClrLocoMaster(xx1);
- LOG( log_trainMove, 1, ( "%s looses master\n", CarItemNumber(xx1->item) ) )
- xx->speed = xx1->speed;
- xx1->speed = 0;
- }
- LocoListChangeEntry( loco, trainFuncCar );
- }
- }
- break;
- case DO_CHANGEDIR:
- loco = FindMasterLoco( trainFuncCar, NULL );
- if ( loco ) {
- xx = GetTrkExtraData(loco);
- xx->direction = !xx->direction;
- SetTrainDirection(loco);
- ControllerDialogSync( curTrainDlg );
- }
- break;
- case DO_STOP:
- loco = FindMasterLoco( trainFuncCar, NULL );
- if ( loco ) {
- StopTrain( loco, ST_StopManual );
- ControllerDialogSync( curTrainDlg );
- }
- break;
- }
MainRedraw(); //Redraw if Train altered
+ MapRedraw();
- if ( trainsState == TRAINS_PAUSE ) {
- RestartTrains();
- } else {
- DrawAllCars();
- }
+ if (trainsState == TRAINS_PAUSE) {
+ RestartTrains();
+ } else {
+ DrawAllCars();
+ }
}
-EXPORT void InitCmdTrain( wMenu_p menu )
+void InitCmdTrain(wMenu_p menu)
{
- log_trainMove = LogFindIndex( "trainMove" );
- log_trainPlayback = LogFindIndex( "trainPlayback" );
- trainPLs[I_ZERO].winLabel = (char*)wIconCreatePixMap(zero_xpm);
- ParamRegister( &trainPG );
- AddMenuButton( menu, CmdTrain, "cmdTrain", _("Train"), wIconCreatePixMap(train_xpm), LEVEL0_50, IC_POPUP2|IC_LCLICK|IC_RCLICK, 0, NULL );
- stopI = wIconCreatePixMap( ballred );
- goI = wIconCreatePixMap( ballgreen );
- trainPauseB = AddToolbarButton( "cmdTrainPause", stopI, IC_MODETRAIN_ONLY, CmdTrainStopGo, NULL );
- AddToolbarButton( "cmdTrainExit", wIconCreatePixMap(exit_xpm), IC_MODETRAIN_ONLY, CmdTrainExit, NULL );
- newcarB = AddToolbarButton( "cmdTrainNewCar", wIconCreatePixMap(newcar_xpm), IC_MODETRAIN_ONLY, CarItemLoadList, NULL );
-
- T_CAR = InitObject( &carCmds );
-
-#ifdef LATER
- trainPGp = ParamCreateGroup( "trainW", "train", 0, trainPLs, sizeof trainPLs/sizeof trainPLs[0], NULL, 0, _("Ok"), trainOk, wHide );
- ParamRegister( trainPGp );
-#endif
-
- trainPopupM = MenuRegister( "Train Commands" );
- trainPopupMI[DO_UNCOUPLE] = wMenuPushCreate( trainPopupM, "", _("Uncouple"), 0, TrainFunc, (void*)DO_UNCOUPLE );
- trainPopupMI[DO_FLIPCAR] = wMenuPushCreate( trainPopupM, "", _("Flip Car"), 0, TrainFunc, (void*)DO_FLIPCAR );
- trainPopupMI[DO_FLIPTRAIN] = wMenuPushCreate( trainPopupM, "", _("Flip Train"), 0, TrainFunc, (void*)DO_FLIPTRAIN );
- trainPopupMI[DO_MUMASTER] = wMenuPushCreate( trainPopupM, "", _("MU Master"), 0, TrainFunc, (void*)DO_MUMASTER );
- trainPopupMI[DO_CHANGEDIR] = wMenuPushCreate( trainPopupM, "", _("Change Direction"), 0, TrainFunc, (void*)DO_CHANGEDIR );
- trainPopupMI[DO_STOP] = wMenuPushCreate( trainPopupM, "", _("Stop"), 0, TrainFunc, (void*)DO_STOP );
- wMenuSeparatorCreate( trainPopupM );
- trainPopupMI[DO_DELCAR] = wMenuPushCreate( trainPopupM, "", _("Remove Car"), 0, TrainFunc, (void*)DO_DELCAR );
- trainPopupMI[DO_DELTRAIN] = wMenuPushCreate( trainPopupM, "", _("Remove Train"), 0, TrainFunc, (void*)DO_DELTRAIN );
-
-#ifdef LATER
- ParamRegister( &newCarPG );
- ParamCreateControls( &newCarPG, NULL );
- newCarControls[0] = newCarPLs[0].control;
- newCarControls[1] = newCarPLs[1].control;
-#endif
- AddPlaybackProc( "TRAINSTOPGO", (playbackProc_p)TrainStopGoPlayback, NULL );
- AddPlaybackProc( "TRAINPAUSE", (playbackProc_p)TrainTimeDoPause, NULL );
- AddPlaybackProc( "TRAINMOVIE", (playbackProc_p)TrainDoMovie, NULL );
+ log_trainMove = LogFindIndex("trainMove");
+ log_trainPlayback = LogFindIndex("trainPlayback");
+ trainPLs[I_ZERO].winLabel = (char*)wIconCreatePixMap(zero_xpm);
+ ParamRegister(&trainPG);
+ AddMenuButton(menu, CmdTrain, "cmdTrain", _("Train"),
+ wIconCreatePixMap(train_xpm), LEVEL0_50, IC_POPUP2|IC_LCLICK|IC_RCLICK, 0,
+ NULL);
+ stopI = wIconCreatePixMap(ballred);
+ goI = wIconCreatePixMap(ballgreen);
+ trainPauseB = AddToolbarButton("cmdTrainPause", stopI, IC_MODETRAIN_ONLY,
+ CmdTrainStopGo, NULL);
+ AddToolbarButton("cmdTrainExit", wIconCreatePixMap(exit_xpm), IC_MODETRAIN_ONLY,
+ CmdTrainExit, NULL);
+ newcarB = AddToolbarButton("cmdTrainNewCar", wIconCreatePixMap(newcar_xpm),
+ IC_MODETRAIN_ONLY, CarItemLoadList, NULL);
+ T_CAR = InitObject(&carCmds);
+ trainPopupM = MenuRegister("Train Commands");
+ trainPopupMI[DO_UNCOUPLE] = wMenuPushCreate(trainPopupM, "", _("Uncouple"), 0,
+ TrainFunc, (void*)DO_UNCOUPLE);
+ trainPopupMI[DO_FLIPCAR] = wMenuPushCreate(trainPopupM, "", _("Flip Car"), 0,
+ TrainFunc, (void*)DO_FLIPCAR);
+ trainPopupMI[DO_FLIPTRAIN] = wMenuPushCreate(trainPopupM, "", _("Flip Train"),
+ 0, TrainFunc, (void*)DO_FLIPTRAIN);
+ trainPopupMI[DO_MUMASTER] = wMenuPushCreate(trainPopupM, "", _("MU Master"),
+ 0, TrainFunc, (void*)DO_MUMASTER);
+ trainPopupMI[DO_CHANGEDIR] = wMenuPushCreate(trainPopupM, "",
+ _("Change Direction"), 0, TrainFunc, (void*)DO_CHANGEDIR);
+ trainPopupMI[DO_STOP] = wMenuPushCreate(trainPopupM, "", _("Stop"), 0,
+ TrainFunc, (void*)DO_STOP);
+ wMenuSeparatorCreate(trainPopupM);
+ trainPopupMI[DO_DELCAR] = wMenuPushCreate(trainPopupM, "", _("Remove Car"),
+ 0, TrainFunc, (void*)DO_DELCAR);
+ trainPopupMI[DO_DELTRAIN] = wMenuPushCreate(trainPopupM, "",
+ _("Remove Train"), 0, TrainFunc, (void*)DO_DELTRAIN);
+ AddPlaybackProc("TRAINSTOPGO", (playbackProc_p)TrainStopGoPlayback, NULL);
+ AddPlaybackProc("TRAINPAUSE", (playbackProc_p)TrainTimeDoPause, NULL);
+ AddPlaybackProc("TRAINMOVIE", (playbackProc_p)TrainDoMovie, NULL);
}
diff --git a/app/bin/ctrain.h b/app/bin/ctrain.h
index 10f836f..daa083c 100644
--- a/app/bin/ctrain.h
+++ b/app/bin/ctrain.h
@@ -1,5 +1,5 @@
-/*
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/ctrain.h,v 1.1 2005-12-07 15:46:59 rc-flyer Exp $
+/** \file ctrain.h
+ * Definitions and prototypes for train operations
*/
/* XTrkCad - Model Railroad CAD
@@ -20,6 +20,11 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
+#ifndef HAVE_CTRAIN_H
+#define HAVE_CTRAIN_H
+
+#include "common.h"
+#include "track.h"
struct carItem_t;
typedef struct carItem_t carItem_t;
@@ -53,3 +58,4 @@ int CarAvailableCount( void );
BOOL_T TraverseTrack2( traverseTrack_p, DIST_T );
void FlipTraverseTrack( traverseTrack_p );
+#endif // !HAVE_CTRAIN_H \ No newline at end of file
diff --git a/app/bin/cturnout.c b/app/bin/cturnout.c
index 55b7a4d..c3125ad 100644
--- a/app/bin/cturnout.c
+++ b/app/bin/cturnout.c
@@ -1,8 +1,5 @@
-/*
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/cturnout.c,v 1.8 2009-08-16 13:07:14 m_fischer Exp $
- *
+/** \file cturnout.c
* T_TURNOUT
- *
*/
/* XTrkCad - Model Railroad CAD
@@ -24,14 +21,24 @@
*/
#include <ctype.h>
-#include "track.h"
+#include <math.h>
+#include <stdint.h>
+#include <string.h>
+
#include "ccurve.h"
-#include "cstraigh.h"
-#include "compound.h"
+#include "tbezier.h"
#include "cjoin.h"
+#include "compound.h"
+#include "cstraigh.h"
+#include "cundo.h"
+#include "custom.h"
+#include "fileio.h"
#include "i18n.h"
-
-#include <stdint.h>
+#include "layout.h"
+#include "messages.h"
+#include "param.h"
+#include "track.h"
+#include "utility.h"
EXPORT TRKTYP_T T_TURNOUT = -1;
@@ -120,6 +127,8 @@ EXPORT turnoutInfo_t * CreateNewTurnout(
}
to->segCnt = segCnt;
to->segs = (trkSeg_p)memdup( segData, (sizeof *segData) * segCnt );
+ FixUpBezierSegs(to->segs,to->segCnt);
+
GetSegBounds( zero, 0.0, segCnt, to->segs, &to->orig, &to->size );
to->endCnt = endPtCnt;
to->endPt = (trkEndPt_t*)memdup( endPts, (sizeof *endPts) * to->endCnt );
@@ -154,7 +163,7 @@ EXPORT wIndex_t CheckPaths(
PATHPTR_T paths )
{
int pc, ps;
- PATHPTR_T pp;
+ PATHPTR_T pp = 0;
int inx, inx1;
static dynArr_t segMap_da;
int segInx[2], segEp[2];
@@ -663,7 +672,7 @@ static ANGLE_T GetAngleTurnout(
pos.x -= xx->orig.x;
pos.y -= xx->orig.y;
Rotate( &pos, zero, -xx->angle );
- angle = GetAngleSegs( segCnt, xx->segs, pos, &segInx );
+ angle = GetAngleSegs( segCnt, xx->segs, &pos, &segInx, NULL, NULL, NULL, NULL );
return NormalizeAngle( angle+xx->angle );
}
@@ -1202,7 +1211,6 @@ static BOOL_T TraverseTurnout(
coOrd pos0, pos1, pos2;
DIST_T d, dist;
PATHPTR_T path, pathCurr;
- BOOL_T backwards=FALSE;
trkSeg_p segPtr;
EPINX_T ep, epCnt, ep2;
int segInx;
@@ -1222,10 +1230,6 @@ LOG( log_traverseTurnout, 1, ( "TraverseTurnout( T%d, [%0.3f %0.3f] [%0.3f %0.3f
continue;
GetSegInxEP( path[0], &segInx, &segEP );
segPtr = xx->segs+segInx;
-#ifdef LATER
- for ( inx = 0; inx<xx->segCnt; inx++ ) {
- segPtr = xx->segs+inx;
-#endif
segProcData.distance.pos1 = pos0;
SegProc( SEGPROC_DISTANCE, segPtr, &segProcData );
if ( segProcData.distance.dd < d ) {
@@ -1241,46 +1245,63 @@ LOG( log_traverseTurnout, 1, ( "TraverseTurnout( T%d, [%0.3f %0.3f] [%0.3f %0.3f
LOG( log_traverseTurnout, 1, ( " PC=%d ", pathCurr[0] ) )
GetSegInxEP( pathCurr[0], &segInx, &segEP );
segPtr = xx->segs+segInx;
-#ifdef LATER
- for ( pathCurr = xx->pathCurr+strlen((char*)xx->pathCurr)+1; pathCurr[0] || pathCurr[1]; pathCurr++ ) {
- if ( pathCurr[0] == 0 )
- continue;
- if ( Abs(pathCurr[0])-1 == currInx )
- break;
- }
- if ( pathCurr[0] == 0 ) {
- fprintf( stderr, "Open turnout [%d]\n", currInx );
- return FALSE;
- }
- segPtr = xx->segs+currInx;
-#endif
segProcData.traverse1.pos = pos2;
- segProcData.traverse1.angle = xx->angle-trvTrk->angle;
+ segProcData.traverse1.angle = -xx->angle+trvTrk->angle;
SegProc( SEGPROC_TRAVERSE1, segPtr, &segProcData );
dist += segProcData.traverse1.dist;
- backwards = segProcData.traverse1.backwards;
- if ( segEP ) backwards = !backwards;
-LOG( log_traverseTurnout, 2, ( " B%d D%0.3f\n", backwards, dist ) )
-
+ //Get ready for Traverse2 - copy all Traverse1 first
+ BOOL_T backwards = segProcData.traverse1.backwards;
+ BOOL_T segs_backwards = segProcData.traverse1.segs_backwards;
+ BOOL_T neg = segProcData.traverse1.negative;
+ int BezSegInx = segProcData.traverse1.BezSegInx;
+
+ // Backwards means universally we going towards EP=0 on this segment.
+ // But the overall direction we are going can have two types of reversal,
+ // a curve that is flipped is negative (the end points are reversed) which Traverse1 handles,
+ // and a path can also be reversed (negative path number) and will have segEP = 1
+ BOOL_T turnout_backwards = backwards;
+ if (segEP) turnout_backwards = !turnout_backwards; //direction modified if path reversed
+
+LOG( log_traverseTurnout, 2, ( " SI%d TB%d SP%d B%d SB%d N%d BSI%d D%0.3f\n", segInx, turnout_backwards, segEP, backwards, segs_backwards, neg, BezSegInx, dist ) )
while ( *pathCurr ) {
+ //Set up Traverse2
GetSegInxEP( pathCurr[0], &segInx, &segEP );
segPtr = xx->segs+segInx;
- segProcData.traverse2.segDir = (backwards?1-segEP:segEP);
+ segProcData.traverse2.segDir = backwards;
segProcData.traverse2.dist = dist;
+ segProcData.traverse2.BezSegInx = BezSegInx;
+ segProcData.traverse2.segs_backwards = segs_backwards;
SegProc( SEGPROC_TRAVERSE2, segPtr, &segProcData );
if ( segProcData.traverse2.dist <= 0 ) {
*distR = 0;
REORIGIN( trvTrk->pos, segProcData.traverse2.pos, xx->angle, xx->orig );
trvTrk->angle = NormalizeAngle( xx->angle+segProcData.traverse2.angle );
+LOG( log_traverseTurnout, 2, ( " -> [%0.3f %0.3f] A%0.3f D%0.3f\n", trvTrk->pos.x, trvTrk->pos.y, trvTrk->angle, *distR ))
return TRUE;
}
- dist = segProcData.traverse2.dist;
- pathCurr += (backwards?-1:1);
-LOG( log_traverseTurnout, 1, ( " D%0.3f\n", dist ) )
- }
-
- pathCurr += (backwards?1:-1);
- pos1 = MapPathPos( xx, pathCurr[0], (backwards?0:1) );
+ dist = segProcData.traverse2.dist; //Remainder after segment
+ pathCurr += (turnout_backwards?-1:1); //Use master direction for turnout
+ //Redrive Traverse 1 for each segment for Bezier - to pick up backwards elements
+ if (pathCurr[0] == '\0') continue; //
+ //Set up Traverse1 - copy all of Traverse2 values first
+ GetSegInxEP( pathCurr[0], &segInx, &segEP );
+ segPtr = xx->segs+segInx;
+ ANGLE_T angle = segProcData.traverse2.angle;
+ coOrd pos = segProcData.traverse2.pos;
+LOG( log_traverseTurnout, 1, ( " Loop2-1 SI%d SP%d [%0.3f %0.3f] A%0.3f D%0.3f\n", segInx, segEP, pos.x, pos.y, angle, dist ) )
+ segProcData.traverse1.pos = pos;
+ segProcData.traverse1.angle = angle;
+ SegProc( SEGPROC_TRAVERSE1, segPtr, &segProcData );
+ // dist += segProcData.traverse1.dist; //Add distance from end to pos (could be zero or whole length if backwards)
+ backwards = segProcData.traverse1.backwards;
+ segs_backwards = segProcData.traverse1.segs_backwards;
+ neg = segProcData.traverse1.negative;
+ BezSegInx = segProcData.traverse1.BezSegInx;
+LOG( log_traverseTurnout, 1, ( " Loop1-2 B%d SB%d N%d BSI%d D%0.3f\n", backwards, segs_backwards, neg, BezSegInx, dist ) )
+ }
+
+ pathCurr += (turnout_backwards?1:-1);
+ pos1 = MapPathPos( xx, pathCurr[0], (turnout_backwards?0:1) );
*distR = dist;
epCnt = GetTrkEndPtCnt(trk);
ep = 0;
@@ -1354,10 +1375,25 @@ static STATUS_T ModifyTurnout( track_p trk, wAction_t action, coOrd pos )
static BOOL_T GetParamsTurnout( int inx, track_p trk, coOrd pos, trackParams_t * params )
{
- params->type = curveTypeStraight;
- params->ep = PickUnconnectedEndPoint( pos, trk );
+
+
+ params->type = curveTypeStraight; //TODO should check if last segment is actually straight
+ if (inx == PARAMS_CORNU || inx == PARAMS_BEZIER) {
+ params->arcR = 0.0;
+ params->arcP = zero;
+ params->ep = PickEndPoint(pos,trk); //Nearest
+ if (params->ep>=0) {
+ params->angle = GetTrkEndAngle(trk,params->ep);
+ params->track_angle = params->angle + params->ep?0:180;
+ } else {
+ params->angle = params-> track_angle = 0;
+ return FALSE;
+ }
+ return TRUE;
+ }
+ params->ep = PickUnconnectedEndPointSilent( pos, trk );
if (params->ep == -1)
- return FALSE;
+ return FALSE;
params->lineOrig = GetTrkEndPos(trk,params->ep);
params->lineEnd = params->lineOrig;
params->len = 0.0;
@@ -1405,7 +1441,15 @@ static BOOL_T QueryTurnout( track_p trk, int query )
case Q_NOT_PLACE_FROGPOINTS:
case Q_HAS_DESC:
case Q_MODIFY_REDRAW_DONT_UNDRAW_TRACK:
+ case Q_CAN_EXTEND:
return TRUE;
+ case Q_MODIFY_CAN_SPLIT:
+ if (GetTrkEndPtCnt(trk) <= 2) { // allow splitting of simple track und buffers
+ return TRUE ;
+ }
+ else {
+ return FALSE;
+ }
case Q_CAN_PARALLEL:
if( GetTrkEndPtCnt( trk ) == 2 && fabs( GetTrkEndAngle( trk, 0 ) - GetTrkEndAngle( trk, 1 )) == 180.0 )
return TRUE;
@@ -1413,6 +1457,8 @@ static BOOL_T QueryTurnout( track_p trk, int query )
return FALSE;
case Q_CAN_NEXT_POSITION:
return ( GetTrkEndPtCnt(trk) > 2 );
+ case Q_CORNU_CAN_MODIFY:
+ return FALSE;
default:
return FALSE;
}
@@ -1663,7 +1709,7 @@ static void TurnoutChange( long changes )
maxTurnoutDim.x = maxTurnoutDim.y = 0.0;
if (turnoutInfo_da.cnt <= 0)
return;
- curTurnout = TurnoutAdd( LABEL_TABBED|LABEL_MANUF|LABEL_PARTNO|LABEL_DESCR, curScaleInx, turnoutListL, &maxTurnoutDim, -1 );
+ curTurnout = TurnoutAdd( LABEL_TABBED|LABEL_MANUF|LABEL_PARTNO|LABEL_DESCR, GetLayoutCurScale(), turnoutListL, &maxTurnoutDim, -1 );
wListSetIndex( turnoutListL, 0 );
wControlShow( (wControl_p)turnoutListL, TRUE );
if (curTurnout == NULL) {
@@ -1871,6 +1917,9 @@ LOG( log_turnout, 3, ( "placeTurnout T%d (%0.3f %0.3f) A%0.3f\n",
}
}
}
+ } else {
+ trk = NULL;
+ *trkR = NULL;
}
*connCntR = connCnt;
*maxDR = maxD;
@@ -1975,14 +2024,6 @@ static void AddTurnout( void )
curTurnout->segs, curTurnout->segCnt, trackGauge, wDrawColorBlack );
UndoStart( _("Place New Turnout"), "addTurnout" );
titleLen = strlen( curTurnout->title );
-#ifdef LATER
- newTrk = NewTrack( 0, T_TURNOUT, curTurnout->endCnt, sizeof (*xx) + 1 );
- xx = GetTrkExtraData(newTrk);
- xx->orig = Dto.pos;
- xx->angle = Dto.angle;
- xx->customInfo = curTurnout->customInfo;
- xx->segs = MyMalloc( (curTurnout->segCnt)*sizeof curTurnout->segs[0] );
-#endif
DYNARR_SET( trkEndPt_t, tempEndPts_da, curTurnout->endCnt );
DYNARR_SET( junk_t, connection_da, curTurnout->endCnt );
@@ -2078,23 +2119,13 @@ LOG( log_turnout, 1, ( " deleting leftover T%d\n",
xx->customInfo = curTurnout->customInfo;
if (connection((int)curTurnoutEp).trk) {
CopyAttributes( connection((int)curTurnoutEp).trk, newTrk );
- SetTrkScale( newTrk, curScaleInx );
+ SetTrkScale( newTrk, GetLayoutCurScale());
}
xx->special = curTurnout->special;
xx->u = curTurnout->u;
-#ifdef LATER
- xx->segCnt = curTurnout->segCnt;
- memcpy( xx->segs, curTurnout->segs, xx->segCnt * sizeof *(trkSeg_p)0 );
- xx->title = curTurnout->title;
- xx->paths = xx->pathCurr = curTurnout->paths;
- xx->pathLen = curTurnout->pathLen;
-#endif
/* Make the connections */
-#ifdef LATER
- for (i=0; i<curTurnout->endCnt; i++)
- SetTrkEndPoint( newTrk, i, tempEndPts(i).pos, tempEndPts(i).angle );
-#endif
+
visible = FALSE;
noConnections = TRUE;
AuditTracks( "addTurnout T%d before connection", GetTrkIndex(newTrk) );
@@ -2102,6 +2133,9 @@ LOG( log_turnout, 1, ( " deleting leftover T%d\n",
if ( connection(i).trk != NULL ) {
p0 = GetTrkEndPos( newTrk, i );
p1 = GetTrkEndPos( connection(i).trk, connection(i).ep );
+ ANGLE_T a0 = GetTrkEndAngle( newTrk, i);
+ ANGLE_T a1 = GetTrkEndAngle( connection(i).trk, connection(i).ep );
+ ANGLE_T a = NormalizeAngle(a1-a0+180);
d = FindDistance( p0, p1 );
if ( d < connectDistance ) {
noConnections = FALSE;
@@ -2481,7 +2515,7 @@ EXPORT void AddHotBarTurnouts( void )
to = turnoutInfo(inx);
if ( !( IsParamValid(to->paramFileIndex) &&
to->segCnt > 0 &&
- CompatibleScale( TRUE, to->scaleInx, curScaleInx ) ) )
+ CompatibleScale( TRUE, to->scaleInx, GetLayoutCurScale()) ) )
continue;
AddHotBarElement( to->contentsLabel, to->size, to->orig, TRUE, to->barScale, to, CmdTurnoutHotBarProc );
}
diff --git a/app/bin/cturntbl.c b/app/bin/cturntbl.c
index 31f33ed..9264572 100644
--- a/app/bin/cturntbl.c
+++ b/app/bin/cturntbl.c
@@ -1,8 +1,5 @@
-/*
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/cturntbl.c,v 1.4 2008-03-06 19:35:06 m_fischer Exp $
- *
+/** \file cturntbl.c
* TURNTABLE
- *
*/
/* XTrkCad - Model Railroad CAD
@@ -23,9 +20,17 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-#include "track.h"
+#include <math.h>
+#include <string.h>
+
#include "cstraigh.h"
+#include "cundo.h"
+#include "fileio.h"
#include "i18n.h"
+#include "messages.h"
+#include "param.h"
+#include "track.h"
+#include "utility.h"
static TRKTYP_T T_TURNTABLE = -1;
@@ -233,7 +238,7 @@ static struct {
coOrd orig;
DIST_T diameter;
long epCnt;
- LAYER_T layerNumber;
+ unsigned int layerNumber;
} trntblData;
typedef enum { OR, RA, EC, LY } trntblDesc_e;
static descData_t trntblDesc[] = {
@@ -584,6 +589,26 @@ static STATUS_T ModifyTurntable( track_p trk, wAction_t action, coOrd pos )
return C_ERROR;
}
+EXPORT BOOL_T ConnectTurntableTracks(
+ track_p trk1,
+ EPINX_T ep1,
+ track_p trk2,
+ EPINX_T ep2 ) {
+ coOrd center, pos;
+ DIST_T radius;
+ TurntableGetCenter( trk1, &center, &radius );
+ pos = GetTrkEndPos(trk2,ep2);
+ ANGLE_T angle = FindAngle(center, GetTrkEndPos(trk2,ep2));
+ if (NormalizeAngle(GetTrkEndAngle(trk2,ep2) + 180 - angle) < connectAngle) {
+ if (FindDistance(center,pos)-radius < connectDistance) {
+ EPINX_T ep = NewTurntableEndPt(trk1,angle);
+ ConnectTracks( trk1, ep, trk2, ep2 );
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
static BOOL_T GetParamsTurntable( int inx, track_p trk, coOrd pos, trackParams_t * params )
{
@@ -604,6 +629,8 @@ static BOOL_T GetParamsTurntable( int inx, track_p trk, coOrd pos, trackParams_t
params->lineEnd = params->lineOrig;
params->len = 0.0;
params->arcR = 0.0;
+ params->ttcenter = center; //Turntable
+ params->ttradius = radius; //Turntable
return TRUE;
}
@@ -623,11 +650,27 @@ static BOOL_T MoveEndPtTurntable( track_p *trk, EPINX_T *ep, coOrd pos, DIST_T d
d -= d0;
Translate( &pos, pos, angle0+180, d0 );
}
- if (d < r) {
+ if (small((r-d)/2)) {
+ Translate( &pos, posCen, angle0+180, r); //Make radius equal if close
+ } else if (d < r) {
ErrorMessage( MSG_POINT_INSIDE_TURNTABLE );
return FALSE;
}
- *ep = NewTurntableEndPt( *trk, angle0 );
+ //Look for empty slot
+ BOOL_T found = FALSE;
+ for (*ep=0; *ep<GetTrkEndPtCnt(*trk); *ep=*ep+1) {
+ if ( (GetTrkEndTrk(*trk,*ep)) == NULL )
+ found = TRUE;
+ break;
+ }
+ if (!found)
+ *ep = NewTurntableEndPt(*trk,angle0);
+ else {
+ struct extraData *xx = GetTrkExtraData(*trk);
+ coOrd pos1;
+ PointOnCircle( &pos1, xx->pos, xx->radius, angle0 );
+ SetTrkEndPoint(*trk, *ep, pos1, angle0); //Reuse
+ }
if ((d-r) > connectDistance) {
trk1 = NewStraightTrack( GetTrkEndPos(*trk,*ep), pos );
CopyAttributes( *trk, trk1 );
@@ -650,7 +693,12 @@ static BOOL_T QueryTurntable( track_p trk, int query )
case Q_ISTRACK:
case Q_NOT_PLACE_FROGPOINTS:
case Q_MODIFY_REDRAW_DONT_UNDRAW_TRACK:
+ case Q_CAN_ADD_ENDPOINTS:
+ case Q_CAN_EXTEND:
return TRUE;
+ case Q_MODIFY_CAN_SPLIT:
+ case Q_CORNU_CAN_MODIFY:
+ return FALSE;
default:
return FALSE;
}
diff --git a/app/bin/cundo.c b/app/bin/cundo.c
index 13d7af0..e27ee75 100644
--- a/app/bin/cundo.c
+++ b/app/bin/cundo.c
@@ -24,9 +24,18 @@
#include <time.h>
#include <stdarg.h>
#include <errno.h>
+#include <string.h>
+
+#include "cselect.h"
+#include "custom.h"
+#include "fileio.h"
+#include "i18n.h"
+#include "messages.h"
+#include "paths.h"
#include "track.h"
#include "trackx.h"
-#include "i18n.h"
+#include "cundo.h"
+
/*****************************************************************************
*
@@ -148,10 +157,11 @@ static BOOL_T UndoFail( char * cause, long val, char * fileName, int lineNumber
undoStack_p us;
FILE * outf;
time_t clock;
- char temp[STR_SIZE];
+ char *temp;
NoticeMessage( MSG_UNDO_ASSERT, _("Ok"), NULL, fileName, lineNumber, val, val, cause );
- sprintf( temp, "%s%s%s", workingDir, FILE_SEP_CHAR, sUndoF );
+ MakeFullpath(&temp, workingDir, sUndoF, NULL);
outf = fopen( temp, "a+" );
+ free(temp);
if ( outf == NULL ) {
NoticeMessage( MSG_OPEN_FAIL, _("Ok"), NULL, _("Undo Trace"), temp, strerror(errno) );
return FALSE;
@@ -361,6 +371,7 @@ static BOOL_T ReadObject( stream_p stream, BOOL_T needRedo )
tempTrk.extraData = trk->extraData;
if (!ReadStream( stream, tempTrk.extraData, tempTrk.extraSize ))
return FALSE;
+ RebuildTrackSegs(&tempTrk); //If we had an array of Segs - recreate it
if (recordUndo) Rprintf( "Restore T%D(%d) @ %lx\n", trk->index, tempTrk.index, (long)trk );
tempTrk.index = trk->index;
tempTrk.next = trk->next;
diff --git a/app/bin/cundo.h b/app/bin/cundo.h
index ef767ae..89beab3 100644
--- a/app/bin/cundo.h
+++ b/app/bin/cundo.h
@@ -1,5 +1,5 @@
-/*
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/cundo.h,v 1.1 2005-12-07 15:46:54 rc-flyer Exp $
+/** \file cundo.h
+ * Function prototypes for undo functionality
*/
/* XTrkCad - Model Railroad CAD
@@ -20,6 +20,12 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
+#ifndef HAVE_CUNDO_H
+#define HAVE_CUNDO_H
+
+#include "common.h"
+#include "track.h"
+
int UndoUndo( void );
int UndoRedo( void );
void UndoResume( void );
@@ -30,3 +36,5 @@ BOOL_T UndoDelete( track_p );
BOOL_T UndoNew( track_p );
void UndoEnd( void );
void UndoClear( void );
+
+#endif // !HAVE_CUNDO_H
diff --git a/app/bin/custom.c b/app/bin/custom.c
index 766dbd8..618a8ac 100644
--- a/app/bin/custom.c
+++ b/app/bin/custom.c
@@ -1,6 +1,6 @@
#define RENAME_H
-/*
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/custom.c,v 1.14 2010-01-01 13:24:59 m_fischer Exp $
+/** \file custom.c
+ *
*/
/* XTrkCad - Model Railroad CAD
@@ -40,13 +40,14 @@
#include <stdarg.h>
#include <errno.h>
-#include "track.h"
-#include "version.h"
+#include "cjoin.h"
#include "common.h"
-#include "misc.h"
+#include "custom.h"
#include "fileio.h"
-#include "cjoin.h"
#include "i18n.h"
+#include "misc.h"
+#include "track.h"
+#include "version.h"
#define Product "XTrackCAD"
#define product "xtrkcad"
@@ -73,6 +74,7 @@ char * sClipboardF = product ".clp";
char * sParamQF = product "." KEYCODE "tq";
char * sUndoF = product ".und";
char * sAuditF = product ".aud";
+char * sTipF = product ".tip";
char * sSourceFilePattern = NULL;
char * sImportFilePattern = NULL;
@@ -126,8 +128,10 @@ void DoStructDesignerRedir( void )
BOOL_T Initialize( void )
{
InitTrkCurve();
+ InitTrkBezier();
InitTrkStraight();
InitTrkEase();
+ InitTrkCornu();
InitTrkTurnout();
InitTrkTurntable();
InitTrkStruct();
diff --git a/app/bin/custom.h b/app/bin/custom.h
index b8ab213..a4d335a 100644
--- a/app/bin/custom.h
+++ b/app/bin/custom.h
@@ -1,5 +1,5 @@
-/*
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/custom.h,v 1.7 2010-01-01 13:24:59 m_fischer Exp $
+/** \file custom.h
+ *
*/
/* XTrkCad - Model Railroad CAD
@@ -23,6 +23,9 @@
#ifndef CUSTOM_H
#define CUSTOM_H
+#include "wlib.h"
+#include "misc.h"
+
#define ICON_WIDTH (64)
#define ICON_HEIGHT (64)
@@ -82,12 +85,13 @@ void InitCustom( void );
void CleanupCustom( void );
void InitTrkCurve( void );
+void InitTrkBezier( void );
void InitTrkDraw( void );
void InitTrkEase( void );
+void InitTrkCornu( void );
void InitTrkNote( void );
void InitTrkStraight( void );
void InitTrkStruct( void );
-void InitTrkTableEdge( void );
void InitTrkText( void );
void InitTrkTrack( void );
void InitTrkTurnout( void );
@@ -105,13 +109,13 @@ void InitCmdElevation( wMenu_p menu );
void InitCmdJoin( wMenu_p menu );
void InitCmdProfile( wMenu_p menu );
void InitCmdPull( wMenu_p menu );
-void InitCmdTighten( void );
void InitCmdModify( wMenu_p menu );
void InitCmdMove( wMenu_p menu );
void InitCmdMoveDescription( wMenu_p menu );
void InitCmdStraight( wMenu_p menu );
void InitCmdDescribe( wMenu_p menu );
void InitCmdSelect( wMenu_p menu );
+void InitCmdPan( wMenu_p menu);
void InitCmdDelete( void );
void InitCmdSplit( wMenu_p menu );
void InitCmdTunnel( void );
@@ -119,7 +123,6 @@ void InitCmdRuler( wMenu_p menu );
void InitCmdParallel( wMenu_p menu );
wIndex_t InitCmdPrint( wMenu_p menu );
-void InitCmdTableEdge( void );
void InitCmdText( wMenu_p menu );
void InitCmdTrain( wMenu_p menu );
void InitCmdTurnout( wMenu_p menu );
@@ -135,7 +138,6 @@ void InitCmdEasement( void );
char * MakeWindowTitle( char * );
addButtonCallBack_t EasementInit( void );
-addButtonCallBack_t StructDesignerInit( void );
void InitLayers( void );
void InitHotBar( void );
@@ -144,7 +146,7 @@ BOOL_T Initialize( void );
void DoEasementRedir( void );
void DoStructDesignerRedir( void );
void InitNewTurnRedir( wMenu_p );
-void RedrawAbout( wDraw_p, void *, wPos_t, wPos_t );
-void DoKeycheck( char * );
+
+void InitAppDefaults(void);
#endif
diff --git a/app/bin/dbench.c b/app/bin/dbench.c
index 4a32360..7e44713 100644
--- a/app/bin/dbench.c
+++ b/app/bin/dbench.c
@@ -20,9 +20,14 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-#include "track.h"
-#include "i18n.h"
+#include <math.h>
+#include <stdio.h>
+#include <string.h>
+#include "i18n.h"
+#include "param.h"
+#include "track.h"
+#include "utility.h"
/*****************************************************************************
*
diff --git a/app/bin/dbitmap.c b/app/bin/dbitmap.c
index 1c9c304..340bad1 100644
--- a/app/bin/dbitmap.c
+++ b/app/bin/dbitmap.c
@@ -1,5 +1,5 @@
-/*
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/dbitmap.c,v 1.3 2008-02-14 19:49:19 m_fischer Exp $
+/** \file dbitmap.c
+ * Print to Bitmap
*/
/* XTrkCad - Model Railroad CAD
@@ -20,14 +20,17 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-#include "track.h"
+#include <assert.h>
+
+#include "custom.h"
+#include "fileio.h"
#include "i18n.h"
+#include "layout.h"
+#include "messages.h"
+#include "param.h"
+#include "paths.h"
+#include "track.h"
-/*****************************************************************************
- *
- * Print to Bitmap
- *
- */
static long outputBitMapTogglesV = 3;
static double outputBitMapDensity = 10;
@@ -87,14 +90,14 @@ static int SaveBitmapFile(
if (outputBitMapTogglesV&1) {
fp = wStandardFont( F_TIMES, FALSE, FALSE );
fs = 18;
- DrawTextSize( &mainD, Title1, fp, fs, FALSE, &textsize );
+ DrawTextSize( &mainD, GetLayoutTitle(), fp, fs, FALSE, &textsize );
p[0].x = (bitmap_d.size.x - (textsize.x*bitmap_d.scale))/2.0 + bitmap_d.orig.x;
p[0].y = mapD.size.y + (y1+0.30)*bitmap_d.scale;
- DrawString( &bitmap_d, p[0], 0.0, Title1, fp, fs*bitmap_d.scale, wDrawColorBlack );
- DrawTextSize( &mainD, Title2, fp, fs, FALSE, &textsize );
+ DrawString( &bitmap_d, p[0], 0.0, GetLayoutTitle(), fp, fs*bitmap_d.scale, wDrawColorBlack );
+ DrawTextSize( &mainD, GetLayoutSubtitle(), fp, fs, FALSE, &textsize );
p[0].x = (bitmap_d.size.x - (textsize.x*bitmap_d.scale))/2.0 + bitmap_d.orig.x;
p[0].y = mapD.size.y + (y1+0.05)*bitmap_d.scale;
- DrawString( &bitmap_d, p[0], 0.0, Title2, fp, fs*bitmap_d.scale, wDrawColorBlack );
+ DrawString( &bitmap_d, p[0], 0.0, GetLayoutSubtitle(), fp, fs*bitmap_d.scale, wDrawColorBlack );
fp_bi = wStandardFont( F_TIMES, TRUE, TRUE );
DrawTextSize( &mainD, _("Drawn with "), fp, fs, FALSE, &textsize );
DrawTextSize( &mainD, sProdName, fp_bi, fs, FALSE, &textsize1 );
@@ -214,7 +217,7 @@ static void OutputBitMapOk( void * junk )
_("Bitmap files|*.xpm"),
#endif
SaveBitmapFile, NULL );
- wFilSelect( bitmap_fs, curDirName );
+ wFilSelect( bitmap_fs, GetCurrentPath( BITMAPPATHKEY ));
}
diff --git a/app/bin/dcar.c b/app/bin/dcar.c
index c64582f..9236f1b 100644
--- a/app/bin/dcar.c
+++ b/app/bin/dcar.c
@@ -1,6 +1,5 @@
/** \file dcar.c
* TRAIN
- *
*/
/* XTrkCad - Model Railroad CAD
@@ -24,14 +23,23 @@
#ifndef WINDOWS
#include <errno.h>
#endif
+#include <assert.h>
#include <ctype.h>
-
+#include <math.h>
#include <stdint.h>
+#include <string.h>
-#include "track.h"
+#include "cselect.h"
#include "ctrain.h"
-#include "i18n.h"
+#include "custom.h"
#include "fileio.h"
+#include "i18n.h"
+#include "layout.h"
+#include "messages.h"
+#include "param.h"
+#include "paths.h"
+#include "track.h"
+#include "utility.h"
static int log_carList;
static int log_carInvList;
@@ -626,6 +634,8 @@ static carProto_p CarProtoNew(
proto->type = type;
proto->dim = *dim;
proto->segCnt = segCnt;
+ //if (proto->segPtr) Can't do this because segPtr could be static
+ // free(proto->segPtr);
proto->segPtr = (trkSeg_p)memdup( segPtr, (sizeof *(trkSeg_p)0) * proto->segCnt );
CloneFilledDraw( proto->segCnt, proto->segPtr, FALSE );
GetSegBounds( zero, 0.0, proto->segCnt, proto->segPtr, &proto->orig, &proto->size );
@@ -660,6 +670,8 @@ static BOOL_T CarProtoRead(
if ( !ReadSegs() )
return FALSE;
CarProtoNew( NULL, curParamFileIndex, desc, options, type, &dim, tempSegs_da.cnt, &tempSegs(0) );
+ FreeFilledDraw(tempSegs_da.cnt,&tempSegs(0));
+ MyFree(desc);
return TRUE;
}
@@ -1255,7 +1267,7 @@ static BOOL_T CarItemWrite(
item->data.purchPrice, item->data.currPrice, item->data.condition, item->data.purchDate, item->data.serviceDate )>0;
if ( ( options&CAR_ITEM_ONLAYOUT) ) {
CarGetPos( item->car, &pos, &angle );
- rc &= fprintf( f, " %d %d %0.3f %0.3f %0.3f",
+ rc &= fprintf( f, " %d %u %0.3f %0.3f %0.3f",
GetTrkIndex(item->car), GetTrkLayer(item->car), pos.x, pos.y, angle )>0;
}
rc &= fprintf( f, "\n" )>0;
@@ -1614,7 +1626,7 @@ EXPORT int CarAvailableCount( void )
carItem_t * item;
for ( inx=0; inx < carItemHotbar_da.cnt; inx ++ ) {
item = carItemHotbar(inx);
- if ( item->scaleInx != curScaleInx )
+ if ( item->scaleInx != GetLayoutCurScale())
continue;
cnt++;
}
@@ -1636,7 +1648,7 @@ EXPORT void AddHotBarCarDesc( void )
item1 = carItemHotbar(inx);
if ( item1->car && !IsTrackDeleted(item1->car) )
continue;
- if ( item1->scaleInx != curScaleInx )
+ if ( item1->scaleInx != GetLayoutCurScale())
continue;
if ( (carHotbarModes[carHotbarModeInx]&0xF000)!=0 || ( item0 == NULL || Cmp_carHotbar( &item0, &item1 ) != 0 ) ) {
#ifdef DESCFIX
@@ -3811,7 +3823,7 @@ LOG( log_carDlgState, 3, ( "CarDlgOk()\n" ) )
sprintf( message+strlen(message), "%s: %s %s %s %s %s %s",
(partP?_(" and Part"):""),
carDlgManufStr, carDlgPartnoStr, carDlgProtoStr, carDlgDescStr,
- (carDlgRepmarkStr?carDlgRepmarkStr:carDlgRoadnameStr), carDlgNumberStr );
+ (carDlgRepmarkStr[ 0 ]?carDlgRepmarkStr:carDlgRoadnameStr), carDlgNumberStr );
carDlgQuantity = 1;
ParamLoadControl( &carDlgPG, I_CD_QTY );
@@ -3836,7 +3848,7 @@ LOG( log_carDlgState, 3, ( "CarDlgOk()\n" ) )
RestoreLocale(oldLocale);
}
reloadRoadnameList = TRUE;
- sprintf( message, _("%s Part: %s %s %s %s %s %s"), carDlgUpdatePartPtr==NULL?_("Added new"):_("Updated"), carDlgManufStr, carDlgPartnoStr, carDlgProtoStr, carDlgDescStr, carDlgRepmarkStr?carDlgRepmarkStr:carDlgRoadnameStr, carDlgNumberStr );
+ sprintf( message, _("%s Part: %s %s %s %s %s %s"), carDlgUpdatePartPtr==NULL?_("Added new"):_("Updated"), carDlgManufStr, carDlgPartnoStr, carDlgProtoStr, carDlgDescStr, carDlgRepmarkStr[ 0 ]?carDlgRepmarkStr:carDlgRoadnameStr, carDlgNumberStr );
} else if ( S_PROTO ) {
if ( carDlgUpdateProtoPtr==NULL ) {
@@ -3876,7 +3888,7 @@ LOG( log_carDlgState, 3, ( "CarDlgOk()\n" ) )
if ( carDlgUpdateItemPtr==NULL ) {
if ( partP ) {
TabStringExtract( title, 7, tabs );
- if ( CarDlgLoadLists( TRUE, tabs, curScaleInx ) )
+ if ( CarDlgLoadLists( TRUE, tabs, GetLayoutCurScale()) )
currState = S_ItemSel;
else
currState = S_ItemEnter;
@@ -4003,7 +4015,7 @@ static void DoCarPartDlg( carDlgAction_e *actions )
CarDlgLoadRoadnameList();
carProtoSegCnt = 0;
carProtoSegPtr = NULL;
- carDlgScaleInx = curScaleInx;
+ carDlgScaleInx = GetLayoutCurScale();
carDlgFlipToggle = FALSE;
carDlgChanged = 0;
@@ -4135,12 +4147,13 @@ static void CarInvDlgFind( void * junk )
if ( item == NULL || item->car == NULL || IsTrackDeleted(item->car) ) return;
CarGetPos( item->car, &pos, &angle );
CarSetVisible( item->car );
- DrawMapBoundingBox( FALSE );
+ //DrawMapBoundingBox( FALSE );
mainCenter = pos;
mainD.orig.x = pos.x-mainD.size.x/2;;
mainD.orig.y = pos.y-mainD.size.y/2;;
MainRedraw();
- DrawMapBoundingBox( TRUE );
+ MapRedraw();
+ //DrawMapBoundingBox( TRUE );
}
@@ -4345,7 +4358,7 @@ static void CarInvDlgSaveText( void )
if ( carInvSaveText_fs == NULL )
carInvSaveText_fs = wFilSelCreate( mainW, FS_SAVE, 0, _("List Cars"),
"Text|*.txt", CarInvSaveText, NULL );
- wFilSelect( carInvSaveText_fs, curDirName );
+ wFilSelect( carInvSaveText_fs, GetCurrentPath(CARSPATHKEY));
}
@@ -4630,7 +4643,7 @@ static void CarInvDlgImportCsv( void )
if ( carInvImportCsv_fs == NULL )
carInvImportCsv_fs = wFilSelCreate( mainW, FS_LOAD, 0, _("Import Cars"),
_("Comma-Separated-Values|*.csv"), CarInvImportCsv, NULL );
- wFilSelect( carInvImportCsv_fs, curDirName );
+ wFilSelect( carInvImportCsv_fs, GetCurrentPath(CARSPATHKEY));
}
@@ -4752,7 +4765,7 @@ static void CarInvDlgExportCsv( void )
if ( carInvExportCsv_fs == NULL )
carInvExportCsv_fs = wFilSelCreate( mainW, FS_SAVE, 0, _("Export Cars"),
_("Comma-Separated-Values|*.csv"), CarInvExportCsv, NULL );
- wFilSelect( carInvExportCsv_fs, curDirName );
+ wFilSelect( carInvExportCsv_fs, GetCurrentPath(CARSPATHKEY));
}
diff --git a/app/bin/dcmpnd.c b/app/bin/dcmpnd.c
index 2cff06c..8b92e41 100644
--- a/app/bin/dcmpnd.c
+++ b/app/bin/dcmpnd.c
@@ -1,6 +1,5 @@
/* \file dcmpnd.c
* Compound tracks: Turnouts and Structures
- *
*/
/* XTrkCad - Model Railroad CAD
@@ -22,11 +21,18 @@
*/
#include <ctype.h>
-#include "track.h"
+#include <string.h>
+
#include "compound.h"
-#include "shrtpath.h"
+#include "cundo.h"
+#include "custom.h"
+#include "fileio.h"
#include "i18n.h"
-
+#include "messages.h"
+#include "param.h"
+#include "shrtpath.h"
+#include "track.h"
+#include "utility.h"
/*****************************************************************************
*
diff --git a/app/bin/dcontmgm.c b/app/bin/dcontmgm.c
index 45fec89..e9e929f 100644
--- a/app/bin/dcontmgm.c
+++ b/app/bin/dcontmgm.c
@@ -60,11 +60,8 @@
static const char rcsid[] = "@(#) : $Id$";
-
-
-#include "track.h"
#include <errno.h>
-#include "i18n.h"
+#include <math.h>
#ifdef WINDOWS
#include <io.h>
@@ -73,6 +70,14 @@ static const char rcsid[] = "@(#) : $Id$";
#define access _access
#endif
+#include "cundo.h"
+#include "custom.h"
+#include "i18n.h"
+#include "param.h"
+#include "track.h"
+#include "wlib.h"
+
+
/*****************************************************************************
*
* Control List Management
diff --git a/app/bin/dcustmgm.c b/app/bin/dcustmgm.c
index 86f86b1..ce6bdeb 100644
--- a/app/bin/dcustmgm.c
+++ b/app/bin/dcustmgm.c
@@ -1,5 +1,5 @@
-/*
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/dcustmgm.c,v 1.4 2009-07-30 16:58:42 m_fischer Exp $
+/** \file dcustmgm.c
+ * Custom List Management
*/
/* XTrkCad - Model Railroad CAD
@@ -20,22 +20,27 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-#include "track.h"
+#include <assert.h>
#include <errno.h>
-#include "i18n.h"
+#include <string.h>
#ifdef WINDOWS
#include <io.h>
#define F_OK (0)
#define W_OK (2)
#define access _access
+#else
+#include <unistd.h>
#endif
-/*****************************************************************************
- *
- * Custom List Management
- *
- */
+#include "custom.h"
+#include "fileio.h"
+#include "i18n.h"
+#include "messages.h"
+#include "param.h"
+#include "paths.h"
+#include "track.h"
+#include "wlib.h"
static void CustomEdit( void * action );
static void CustomDelete( void * action );
@@ -241,7 +246,7 @@ static void CustomExport( void * junk )
if ( customMgmExport_fs == NULL )
customMgmExport_fs = wFilSelCreate( mainW, FS_UPDATE, 0, _("Move To XTP"),
_("Parameter File|*.xtp"), CustomDoExport, NULL );
- wFilSelect( customMgmExport_fs, curDirName );
+ wFilSelect( customMgmExport_fs, GetCurrentPath(CUSTOMPATHKEY));
}
diff --git a/app/bin/dease.c b/app/bin/dease.c
index 9b07129..7841857 100644
--- a/app/bin/dease.c
+++ b/app/bin/dease.c
@@ -1,8 +1,5 @@
-/*
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/dease.c,v 1.3 2008-03-06 19:35:08 m_fischer Exp $
- *
+/** \file dease.c
* Easement Button Hdlrs
- *
*/
/* XTrkCad - Model Railroad CAD
@@ -23,12 +20,15 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
+#include <math.h>
-#include "track.h"
#include "ccurve.h"
-#include "cstraigh.h"
#include "cjoin.h"
+#include "cstraigh.h"
+#include "custom.h"
#include "i18n.h"
+#include "param.h"
+#include "track.h"
static wButton_p easementB;
@@ -42,11 +42,13 @@ static DIST_T oldEasementVal;
static wIcon_p enone_bm;
static wIcon_p esharp_bm;
static wIcon_p egtsharp_bm;
+static wIcon_p eltsharp_bm;
static wIcon_p enormal_bm;
static wIcon_p eltbroad_bm;
static wIcon_p ebroad_bm;
static wIcon_p egtbroad_bm;
-
+static wIcon_p ecornu_bm;
+
/****************************************
*
* EASEMENTW
@@ -60,14 +62,14 @@ static void SetEasement( DIST_T, void * );
static void EasementOk( void );
static void EasementCancel( void );
-static char *easementChoiceLabels[] = { N_("None"), N_("Sharp"), N_("Normal"), N_("Broad"), NULL };
-static paramFloatRange_t r0o5_2 = { 0.5, 2.0, 60 };
+static char *easementChoiceLabels[] = { N_("None"), N_("Sharp"), N_("Normal"), N_("Broad"), N_("Cornu"), NULL };
+static paramFloatRange_t r0n1_100 = { -1.0, 100.0, 60 };
static paramFloatRange_t r0_100 = { 0.0, 100.0, 60 };
static paramFloatRange_t r0_10 = { 0.0, 10.0, 60 };
static long easeM;
static paramData_t easementPLs[] = {
#define I_EASEVAL (0)
- { PD_FLOAT, &easementVal, "val", PDO_NOPSHUPD, &r0o5_2, N_("Value") },
+ { PD_FLOAT, &easementVal, "val", PDO_NOPSHUPD, &r0n1_100, N_("Value") },
{ PD_FLOAT, &easeR, "r", PDO_DIM|PDO_DLGRESETMARGIN, &r0_100, N_("R"), BO_READONLY },
{ PD_FLOAT, &easeX, "x", PDO_DIM|PDO_DLGHORZ, &r0_10, N_("X"), BO_READONLY },
{ PD_FLOAT, &easeL, "l", PDO_DIM|PDO_DLGHORZ, &r0_100, N_("L"), BO_READONLY },
@@ -87,42 +89,57 @@ static void SetEasement(
long selVal = -1;
wIcon_p bm;
- if (val == 0.0) {
+ if (val < 0.0) {
easeX = easeR = easeL = 0.0;
- selVal = 0;
- bm = enone_bm;
- } else if (val <= 1.0) {
- z = 1.0/val - 1.0;
- easeR = Rvalues[1] - z * (Rvalues[1] - Rvalues[0]);
- easeL = Lvalues[1] - z * (Lvalues[1] - Lvalues[0]);
- if (easeR != 0.0)
- easeX = easeL*easeL/(24*easeR);
- else
- easeX = 0.0;
- if (val == 1.0) {
- selVal = 2;
- bm = enormal_bm;
- } else if (val == 0.5) {
- selVal = 1;
- bm = esharp_bm;
- } else {
- bm = egtsharp_bm;
- }
+ selVal = 4;
+ val = -1;
+ bm = ecornu_bm;
} else {
- z = val - 1.0;
- easeR = Rvalues[1] + z * (Rvalues[2] - Rvalues[1]);
- easeL = Lvalues[1] + z * (Lvalues[2] - Lvalues[1]);
- if (easeR != 0.0)
- easeX = easeL*easeL/(24*easeR);
- else
- easeX = 0.0;
- if (val == 2.0) {
- selVal = 3;
- bm = ebroad_bm;
- } else if (val < 2.0) {
- bm = eltbroad_bm;
+ if (val == 0.0) {
+ easeX = easeR = easeL = 0.0;
+ selVal = 0;
+ val = 0;
+ bm = enone_bm;
+ } else if (val <= 1.0) {
+ if (val < 0.21) val = 0.21; //Eliminate values that give negative radii
+ z = 1.0/val - 1.0;
+ easeR = Rvalues[1] - z * (Rvalues[1] - Rvalues[0]);
+ easeL = Lvalues[1] - z * (Lvalues[1] - Lvalues[0]);
+ if (easeR != 0.0)
+ easeX = easeL*easeL/(24*easeR);
+ else
+ easeX = 0.0;
+ if (val == 1.0) {
+ selVal = 2;
+ bm = enormal_bm;
+ } else if (val == 0.5) {
+ selVal = 1;
+ bm = esharp_bm;
+ } else if (val < 0.5) {
+ bm = eltsharp_bm;
+ selVal = 1;
+ } else {
+ selVal = 1;
+ bm = egtsharp_bm;
+ }
} else {
- bm = egtbroad_bm;
+ z = val - 1.0;
+ easeR = Rvalues[1] + z * (Rvalues[2] - Rvalues[1]);
+ easeL = Lvalues[1] + z * (Lvalues[2] - Lvalues[1]);
+ if (easeR != 0.0)
+ easeX = easeL*easeL/(24*easeR);
+ else
+ easeX = 0.0;
+ if (val == 2.0) {
+ selVal = 3;
+ bm = ebroad_bm;
+ } else if (val < 2.0) {
+ selVal = 3;
+ bm = eltbroad_bm;
+ } else {
+ selVal = 3;
+ bm = egtbroad_bm;
+ }
}
}
@@ -177,6 +194,9 @@ static void EasementSel(
case 3:
val = 2.0;
break;
+ case 4:
+ val = -1.0;
+ break;
default:
AbortProg( "easementSel: bad value %ld", arg);
val = 0.0;
@@ -241,10 +261,12 @@ static void EasementChange( long changes )
#include "bitmaps/enone.xpm"
#include "bitmaps/esharp.xpm"
#include "bitmaps/egtsharp.xpm"
+#include "bitmaps/eltsharp.xpm"
#include "bitmaps/enormal.xpm"
#include "bitmaps/eltbroad.xpm"
#include "bitmaps/ebroad.xpm"
#include "bitmaps/egtbroad.xpm"
+#include "bitmaps/ecornu.xpm"
EXPORT addButtonCallBack_t EasementInit( void )
@@ -252,12 +274,14 @@ EXPORT addButtonCallBack_t EasementInit( void )
ParamRegister( &easementPG );
enone_bm = wIconCreatePixMap( enone_xpm );
+ eltsharp_bm = wIconCreatePixMap( eltsharp_xpm );
esharp_bm = wIconCreatePixMap( esharp_xpm );
egtsharp_bm = wIconCreatePixMap( egtsharp_xpm );
enormal_bm = wIconCreatePixMap( enormal_xpm );
eltbroad_bm = wIconCreatePixMap( eltbroad_xpm );
ebroad_bm = wIconCreatePixMap( ebroad_xpm );
egtbroad_bm = wIconCreatePixMap( egtbroad_xpm );
+ ecornu_bm = wIconCreatePixMap( ecornu_xpm );
easementB = AddToolbarButton( "cmdEasement", enone_bm, 0, (addButtonCallBack_t)DoEasementRedir, NULL );
RegisterChangeNotification( EasementChange );
diff --git a/app/bin/denum.c b/app/bin/denum.c
index d27a135..b353627 100644
--- a/app/bin/denum.c
+++ b/app/bin/denum.c
@@ -20,16 +20,18 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
+#include <assert.h>
+#include <string.h>
#include <time.h>
-#include "track.h"
-#include "i18n.h"
-
-/****************************************************************************
- *
- * ENUMERATE
- *
- */
+#include "custom.h"
+#include "fileio.h"
+#include "layout.h"
+#include "i18n.h"
+#include "param.h"
+#include "paths.h"
+#include "track.h"
+#include "utility.h"
static wWin_p enumW;
@@ -37,6 +39,9 @@ static wWin_p enumW;
#define ENUMOP_PRINT (5)
#define ENUMOP_CLOSE (6)
+#undef max
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+
static void DoEnumOp( void * );
static long enableListPrices;
@@ -83,7 +88,7 @@ static void DoEnumOp(
{
switch( (int)(long)data ) {
case ENUMOP_SAVE:
- wFilSelect( enumFile_fs, curDirName );
+ wFilSelect( enumFile_fs, GetCurrentPath(PARTLISTPATHKEY) );
break;
case ENUMOP_PRINT:
wTextPrint( enumT );
@@ -151,13 +156,13 @@ void EnumerateStart(void)
message[0] = '\0';
cp = message;
- if ( Title1[0] ) {
- strcpy( cp, Title1 );
+ if ( *GetLayoutTitle() ) {
+ strcpy( cp, GetLayoutTitle() );
cp += strlen(cp);
*cp++ = '\n';
}
- if ( Title2[0] ) {
- strcpy( cp, Title2 );
+ if ( *GetLayoutSubtitle() ) {
+ strcpy( cp, GetLayoutSubtitle());
cp += strlen(cp);
*cp++ = '\n';
}
diff --git a/app/bin/dlayer.c b/app/bin/dlayer.c
index c79d783..71d151e 100644
--- a/app/bin/dlayer.c
+++ b/app/bin/dlayer.c
@@ -1,7 +1,5 @@
/** \file dlayer.c
* Functions and dialogs for handling layers.
- *
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/dlayer.c,v 1.9 2009-06-15 19:29:57 m_fischer Exp $
*/
/* XTrkCad - Model Railroad CAD
@@ -23,12 +21,17 @@
*/
#include <assert.h>
-
-#include "track.h"
-#include "i18n.h"
-
+#include <stdbool.h>
#include <stdint.h>
+#include <string.h>
+#include "custom.h"
+#include "dynstring.h"
+#include "fileio.h"
+#include "i18n.h"
+#include "messages.h"
+#include "param.h"
+#include "track.h"
/*****************************************************************************
*
@@ -45,195 +48,208 @@
#define LAYERPREF_COLOR "color"
#define LAYERPREF_FLAGS "flags"
-EXPORT LAYER_T curLayer;
-EXPORT long layerCount = 10;
+unsigned int curLayer;
+long layerCount = 10;
static long newLayerCount = 10;
-static LAYER_T layerCurrent = NUM_LAYERS;
+static unsigned int layerCurrent = NUM_LAYERS;
static BOOL_T layoutLayerChanged = FALSE;
-static wIcon_p show_layer_bmps[NUM_BUTTONS];
-/*static wIcon_p hide_layer_bmps[NUM_BUTTONS]; */
+static wIcon_p show_layer_bmps[NUM_BUTTONS];
static wButton_p layer_btns[NUM_BUTTONS]; /**< layer buttons on toolbar */
/** Layer selector on toolbar */
-static wList_p setLayerL;
+static wList_p setLayerL;
-/*static wMessage_p layerNumM;*/
/** Describe the properties of a layer */
typedef struct {
- char name[STR_SHORT_SIZE]; /**< Layer name */
- wDrawColor color; /**< layer color, is an index into a color table */
- BOOL_T frozen; /**< Frozen flag */
- BOOL_T visible; /**< visible flag */
- BOOL_T onMap; /**< is layer shown map */
- long objCount; /**< number of objects on layer */
- } layer_t;
-
+ char name[STR_SHORT_SIZE]; /**< Layer name */
+ wDrawColor color; /**< layer color, is an index into a color table */
+ BOOL_T frozen; /**< Frozen flag */
+ BOOL_T visible; /**< visible flag */
+ BOOL_T onMap; /**< is layer shown map */
+ long objCount; /**< number of objects on layer */
+} layer_t;
+
static layer_t layers[NUM_LAYERS];
static layer_t *layers_save = NULL;
static int oldColorMap[][3] = {
- { 255, 255, 255 }, /* White */
- { 0, 0, 0 }, /* Black */
- { 255, 0, 0 }, /* Red */
- { 0, 255, 0 }, /* Green */
- { 0, 0, 255 }, /* Blue */
- { 255, 255, 0 }, /* Yellow */
- { 255, 0, 255 }, /* Purple */
- { 0, 255, 255 }, /* Aqua */
- { 128, 0, 0 }, /* Dk. Red */
- { 0, 128, 0 }, /* Dk. Green */
- { 0, 0, 128 }, /* Dk. Blue */
- { 128, 128, 0 }, /* Dk. Yellow */
- { 128, 0, 128 }, /* Dk. Purple */
- { 0, 128, 128 }, /* Dk. Aqua */
- { 65, 105, 225 }, /* Royal Blue */
- { 0, 191, 255 }, /* DeepSkyBlue */
- { 125, 206, 250 }, /* LightSkyBlue */
- { 70, 130, 180 }, /* Steel Blue */
- { 176, 224, 230 }, /* Powder Blue */
- { 127, 255, 212 }, /* Aquamarine */
- { 46, 139, 87 }, /* SeaGreen */
- { 152, 251, 152 }, /* PaleGreen */
- { 124, 252, 0 }, /* LawnGreen */
- { 50, 205, 50 }, /* LimeGreen */
- { 34, 139, 34 }, /* ForestGreen */
- { 255, 215, 0 }, /* Gold */
- { 188, 143, 143 }, /* RosyBrown */
- { 139, 69, 19 }, /* SaddleBrown */
- { 245, 245, 220 }, /* Beige */
- { 210, 180, 140 }, /* Tan */
- { 210, 105, 30 }, /* Chocolate */
- { 165, 42, 42 }, /* Brown */
- { 255, 165, 0 }, /* Orange */
- { 255, 127, 80 }, /* Coral */
- { 255, 99, 71 }, /* Tomato */
- { 255, 105, 180 }, /* HotPink */
- { 255, 192, 203 }, /* Pink */
- { 176, 48, 96 }, /* Maroon */
- { 238, 130, 238 }, /* Violet */
- { 160, 32, 240 }, /* Purple */
- { 16, 16, 16 }, /* Gray */
- { 32, 32, 32 }, /* Gray */
- { 48, 48, 48 }, /* Gray */
- { 64, 64, 64 }, /* Gray */
- { 80, 80, 80 }, /* Gray */
- { 96, 96, 96 }, /* Gray */
- { 112, 112, 122 }, /* Gray */
- { 128, 128, 128 }, /* Gray */
- { 144, 144, 144 }, /* Gray */
- { 160, 160, 160 }, /* Gray */
- { 176, 176, 176 }, /* Gray */
- { 192, 192, 192 }, /* Gray */
- { 208, 208, 208 }, /* Gray */
- { 224, 224, 224 }, /* Gray */
- { 240, 240, 240 }, /* Gray */
- { 0, 0, 0 } /* BlackPixel */
- };
-
-static void DoLayerOp( void * data );
+ { 255, 255, 255 }, /* White */
+ { 0, 0, 0 }, /* Black */
+ { 255, 0, 0 }, /* Red */
+ { 0, 255, 0 }, /* Green */
+ { 0, 0, 255 }, /* Blue */
+ { 255, 255, 0 }, /* Yellow */
+ { 255, 0, 255 }, /* Purple */
+ { 0, 255, 255 }, /* Aqua */
+ { 128, 0, 0 }, /* Dk. Red */
+ { 0, 128, 0 }, /* Dk. Green */
+ { 0, 0, 128 }, /* Dk. Blue */
+ { 128, 128, 0 }, /* Dk. Yellow */
+ { 128, 0, 128 }, /* Dk. Purple */
+ { 0, 128, 128 }, /* Dk. Aqua */
+ { 65, 105, 225 }, /* Royal Blue */
+ { 0, 191, 255 }, /* DeepSkyBlue */
+ { 125, 206, 250 }, /* LightSkyBlue */
+ { 70, 130, 180 }, /* Steel Blue */
+ { 176, 224, 230 }, /* Powder Blue */
+ { 127, 255, 212 }, /* Aquamarine */
+ { 46, 139, 87 }, /* SeaGreen */
+ { 152, 251, 152 }, /* PaleGreen */
+ { 124, 252, 0 }, /* LawnGreen */
+ { 50, 205, 50 }, /* LimeGreen */
+ { 34, 139, 34 }, /* ForestGreen */
+ { 255, 215, 0 }, /* Gold */
+ { 188, 143, 143 }, /* RosyBrown */
+ { 139, 69, 19 }, /* SaddleBrown */
+ { 245, 245, 220 }, /* Beige */
+ { 210, 180, 140 }, /* Tan */
+ { 210, 105, 30 }, /* Chocolate */
+ { 165, 42, 42 }, /* Brown */
+ { 255, 165, 0 }, /* Orange */
+ { 255, 127, 80 }, /* Coral */
+ { 255, 99, 71 }, /* Tomato */
+ { 255, 105, 180 }, /* HotPink */
+ { 255, 192, 203 }, /* Pink */
+ { 176, 48, 96 }, /* Maroon */
+ { 238, 130, 238 }, /* Violet */
+ { 160, 32, 240 }, /* Purple */
+ { 16, 16, 16 }, /* Gray */
+ { 32, 32, 32 }, /* Gray */
+ { 48, 48, 48 }, /* Gray */
+ { 64, 64, 64 }, /* Gray */
+ { 80, 80, 80 }, /* Gray */
+ { 96, 96, 96 }, /* Gray */
+ { 112, 112, 122 }, /* Gray */
+ { 128, 128, 128 }, /* Gray */
+ { 144, 144, 144 }, /* Gray */
+ { 160, 160, 160 }, /* Gray */
+ { 176, 176, 176 }, /* Gray */
+ { 192, 192, 192 }, /* Gray */
+ { 208, 208, 208 }, /* Gray */
+ { 224, 224, 224 }, /* Gray */
+ { 240, 240, 240 }, /* Gray */
+ { 0, 0, 0 } /* BlackPixel */
+};
+
+static void DoLayerOp(void * data);
static void UpdateLayerDlg(void);
-/* static void LoadLayerLists(); */
-static void LayerSetCounts();
-static void InitializeLayers( void LayerInitFunc( void ), int newCurrLayer );
-static void LayerPrefSave( void );
-static void LayerPrefLoad( void );
-EXPORT BOOL_T GetLayerVisible( LAYER_T layer )
+static void InitializeLayers(void LayerInitFunc(void), int newCurrLayer);
+static void LayerPrefSave(void);
+static void LayerPrefLoad(void);
+
+int IsLayerValid(unsigned int layer)
{
- if (layer < 0 || layer >= NUM_LAYERS)
- return TRUE;
- else
- return layers[(int)layer].visible;
+ return (layer <= NUM_LAYERS);
}
-
-EXPORT BOOL_T GetLayerFrozen( LAYER_T layer )
+BOOL_T GetLayerVisible(unsigned int layer)
{
- if (layer < 0 || layer >= NUM_LAYERS)
- return TRUE;
- else
- return layers[(int)layer].frozen;
+ if (!IsLayerValid(layer)) {
+ return TRUE;
+ } else {
+ return layers[layer].visible;
+ }
}
-EXPORT BOOL_T GetLayerOnMap( LAYER_T layer )
+BOOL_T GetLayerFrozen(unsigned int layer)
{
- if (layer < 0 || layer >= NUM_LAYERS)
- return TRUE;
- else
- return layers[(int)layer].onMap;
+ if (!IsLayerValid(layer)) {
+ return TRUE;
+ } else {
+ return layers[layer].frozen;
+ }
}
-EXPORT char * GetLayerName( LAYER_T layer )
+BOOL_T GetLayerOnMap(unsigned int layer)
{
- if (layer < 0 || layer >= NUM_LAYERS)
- return NULL;
- else
- return layers[(int)layer].name;
+ if (!IsLayerValid(layer)) {
+ return TRUE;
+ } else {
+ return layers[layer].onMap;
+ }
}
-EXPORT void NewLayer( void )
+char * GetLayerName(unsigned int layer)
{
+ if (!IsLayerValid(layer)) {
+ return NULL;
+ } else {
+ return layers[layer].name;
+ }
}
-
-EXPORT wDrawColor GetLayerColor( LAYER_T layer )
+wDrawColor GetLayerColor(unsigned int layer)
{
- return layers[(int)layer].color;
+ return layers[layer].color;
}
-static void FlipLayer( void * arg )
+static void FlipLayer(unsigned int layer)
{
- LAYER_T l = (LAYER_T)(long)arg;
- wBool_t visible;
- if ( l < 0 || l >= NUM_LAYERS )
- return;
- if ( l == curLayer && layers[(int)l].visible) {
- wButtonSetBusy( layer_btns[(int)l], layers[(int)l].visible );
- NoticeMessage( MSG_LAYER_HIDE, _("Ok"), NULL );
- return;
- }
- RedrawLayer( l, FALSE );
- visible = !layers[(int)l].visible;
- layers[(int)l].visible = visible;
- if (l<NUM_BUTTONS) {
- wButtonSetBusy( layer_btns[(int)l], visible != 0 );
- wButtonSetLabel( layer_btns[(int)l], (char *)show_layer_bmps[(int)l]);
- }
- RedrawLayer( l, TRUE );
+ wBool_t visible;
+
+ if (!IsLayerValid(layer)) {
+ return;
+ }
+
+ if (layer == curLayer && layers[layer].visible) {
+ wButtonSetBusy(layer_btns[layer], layers[layer].visible);
+ NoticeMessage(MSG_LAYER_HIDE, _("Ok"), NULL);
+ return;
+ }
+
+ RedrawLayer(layer, FALSE);
+ visible = !layers[layer].visible;
+ layers[layer].visible = visible;
+
+ if (layer<NUM_BUTTONS) {
+ wButtonSetBusy(layer_btns[layer], visible != 0);
+ wButtonSetLabel(layer_btns[layer], (char *)show_layer_bmps[layer]);
+ }
+
+ RedrawLayer(layer, TRUE);
}
-static void SetCurrLayer( wIndex_t inx, const char * name, wIndex_t op, void * listContext, void * arg )
+static void SetCurrLayer(wIndex_t inx, const char * name, wIndex_t op,
+ void * listContext, void * arg)
{
- LAYER_T newLayer = (LAYER_T)(long)inx;
- if (layers[(int)newLayer].frozen) {
- NoticeMessage( MSG_LAYER_SEL_FROZEN, _("Ok"), NULL );
- wListSetIndex( setLayerL, curLayer );
- return;
- }
- curLayer = newLayer;
-
- if ( curLayer < 0 || curLayer >= NUM_LAYERS )
- curLayer = 0;
- if ( !layers[(int)curLayer].visible )
- FlipLayer( (void*)(intptr_t)inx );
- if ( recordF )
- fprintf( recordF, "SETCURRLAYER %d\n", inx );
+ unsigned int newLayer = (unsigned int)inx;
+
+ if (layers[newLayer].frozen) {
+ NoticeMessage(MSG_LAYER_SEL_FROZEN, _("Ok"), NULL);
+ wListSetIndex(setLayerL, curLayer);
+ return;
+ }
+
+ curLayer = newLayer;
+
+ if (!IsLayerValid(curLayer)) {
+ curLayer = 0;
+ }
+
+ if (!layers[curLayer].visible) {
+ FlipLayer(inx);
+ }
+
+ if (recordF) {
+ fprintf(recordF, "SETCURRLAYER %d\n", inx);
+ }
}
-static void PlaybackCurrLayer( char * line )
+static void PlaybackCurrLayer(char * line)
{
- wIndex_t layer;
- layer = atoi(line);
- wListSetIndex( setLayerL, layer );
- SetCurrLayer( layer, NULL, 0, NULL, NULL );
+ wIndex_t layer;
+ layer = atoi(line);
+ wListSetIndex(setLayerL, layer);
+ SetCurrLayer(layer, NULL, 0, NULL, NULL);
}
/**
@@ -243,19 +259,35 @@ static void PlaybackCurrLayer( char * line )
* \param color IN new color
*/
-static void SetLayerColor( int inx, wDrawColor color )
+static void SetLayerColor(unsigned int inx, wDrawColor color)
+{
+ if (color != layers[inx].color) {
+ if (inx < NUM_BUTTONS) {
+ wIconSetColor(show_layer_bmps[inx], color);
+ wButtonSetLabel(layer_btns[inx], (char*)show_layer_bmps[inx]);
+ }
+
+ layers[inx].color = color;
+ layoutLayerChanged = TRUE;
+ }
+}
+
+char *
+FormatLayerName(unsigned int layerNumber)
{
- if ( color != layers[inx].color ) {
- if (inx < NUM_BUTTONS) {
- wIconSetColor( show_layer_bmps[inx], color );
- wButtonSetLabel( layer_btns[inx], (char*)show_layer_bmps[inx] );
- }
- layers[inx].color = color;
- layoutLayerChanged = TRUE;
- }
+ DynString string;// = NaS;
+ char *result;
+ DynStringMalloc(&string, 0);
+ DynStringPrintf(&string,
+ "%2d %c %s",
+ layerNumber + 1,
+ (layers[layerNumber].objCount > 0 ? '+' : '-'),
+ layers[layerNumber].name);
+ result = strdup(DynStringToCStr(&string));
+ DynStringFree(&string);
+ return result;
}
-
#include "bitmaps/l1.xbm"
#include "bitmaps/l2.xbm"
@@ -359,31 +391,32 @@ static void SetLayerColor( int inx, wDrawColor color )
static char * show_layer_bits[NUM_BUTTONS] = {
- l1_bits, l2_bits, l3_bits, l4_bits, l5_bits, l6_bits, l7_bits, l8_bits, l9_bits, l10_bits,
- l11_bits, l12_bits, l13_bits, l14_bits, l15_bits, l16_bits, l17_bits, l18_bits, l19_bits, l20_bits,
- l21_bits, l22_bits, l23_bits, l24_bits, l25_bits, l26_bits, l27_bits, l28_bits, l29_bits, l30_bits,
- l31_bits, l32_bits, l33_bits, l34_bits, l35_bits, l36_bits, l37_bits, l38_bits, l39_bits, l40_bits,
- l41_bits, l42_bits, l43_bits, l44_bits, l45_bits, l46_bits, l47_bits, l48_bits, l49_bits, l50_bits,
- l51_bits, l52_bits, l53_bits, l54_bits, l55_bits, l56_bits, l57_bits, l58_bits, l59_bits, l60_bits,
- l61_bits, l62_bits, l63_bits, l64_bits, l65_bits, l66_bits, l67_bits, l68_bits, l69_bits, l70_bits,
- l71_bits, l72_bits, l73_bits, l74_bits, l75_bits, l76_bits, l77_bits, l78_bits, l79_bits, l80_bits,
- l81_bits, l82_bits, l83_bits, l84_bits, l85_bits, l86_bits, l87_bits, l88_bits, l89_bits, l90_bits,
- l91_bits, l92_bits, l93_bits, l94_bits, l95_bits, l96_bits, l97_bits, l98_bits, l99_bits,
+ l1_bits, l2_bits, l3_bits, l4_bits, l5_bits, l6_bits, l7_bits, l8_bits, l9_bits, l10_bits,
+ l11_bits, l12_bits, l13_bits, l14_bits, l15_bits, l16_bits, l17_bits, l18_bits, l19_bits, l20_bits,
+ l21_bits, l22_bits, l23_bits, l24_bits, l25_bits, l26_bits, l27_bits, l28_bits, l29_bits, l30_bits,
+ l31_bits, l32_bits, l33_bits, l34_bits, l35_bits, l36_bits, l37_bits, l38_bits, l39_bits, l40_bits,
+ l41_bits, l42_bits, l43_bits, l44_bits, l45_bits, l46_bits, l47_bits, l48_bits, l49_bits, l50_bits,
+ l51_bits, l52_bits, l53_bits, l54_bits, l55_bits, l56_bits, l57_bits, l58_bits, l59_bits, l60_bits,
+ l61_bits, l62_bits, l63_bits, l64_bits, l65_bits, l66_bits, l67_bits, l68_bits, l69_bits, l70_bits,
+ l71_bits, l72_bits, l73_bits, l74_bits, l75_bits, l76_bits, l77_bits, l78_bits, l79_bits, l80_bits,
+ l81_bits, l82_bits, l83_bits, l84_bits, l85_bits, l86_bits, l87_bits, l88_bits, l89_bits, l90_bits,
+ l91_bits, l92_bits, l93_bits, l94_bits, l95_bits, l96_bits, l97_bits, l98_bits, l99_bits,
};
-static EXPORT long layerRawColorTab[] = {
- wRGB( 0, 0,255), /* blue */
- wRGB( 0, 0,128), /* dk blue */
- wRGB( 0,128, 0), /* dk green */
- wRGB(255,255, 0), /* yellow */
- wRGB( 0,255, 0), /* green */
- wRGB( 0,255,255), /* lt cyan */
- wRGB(128, 0, 0), /* brown */
- wRGB(128, 0,128), /* purple */
- wRGB(128,128, 0), /* green-brown */
- wRGB(255, 0,255)}; /* lt-purple */
-static EXPORT wDrawColor layerColorTab[COUNT(layerRawColorTab)];
+static long layerRawColorTab[] = {
+ wRGB(0, 0,255), /* blue */
+ wRGB(0, 0,128), /* dk blue */
+ wRGB(0,128, 0), /* dk green */
+ wRGB(255,255, 0), /* yellow */
+ wRGB(0,255, 0), /* green */
+ wRGB(0,255,255), /* lt cyan */
+ wRGB(128, 0, 0), /* brown */
+ wRGB(128, 0,128), /* purple */
+ wRGB(128,128, 0), /* green-brown */
+ wRGB(255, 0,255)
+}; /* lt-purple */
+static wDrawColor layerColorTab[COUNT(layerRawColorTab)];
static wWin_p layerW;
@@ -392,7 +425,7 @@ static wDrawColor layerColor;
static long layerVisible = TRUE;
static long layerFrozen = FALSE;
static long layerOnMap = TRUE;
-static void LayerOk( void * );
+static void LayerOk(void *);
static BOOL_T layerRedrawMap = FALSE;
#define ENUMLAYER_RELOAD (1)
@@ -406,24 +439,24 @@ static paramIntegerRange_t i0_20 = { 0, NUM_BUTTONS };
static paramData_t layerPLs[] = {
#define I_LIST (0)
- { PD_DROPLIST, NULL, "layer", PDO_LISTINDEX|PDO_DLGNOLABELALIGN, (void*)250 },
+ { PD_DROPLIST, NULL, "layer", PDO_LISTINDEX|PDO_DLGNOLABELALIGN, (void*)250 },
#define I_NAME (1)
- { PD_STRING, layerName, "name", PDO_NOPREF, (void*)(250-54), N_("Name") },
+ { PD_STRING, layerName, "name", PDO_NOPREF, (void*)(250-54), N_("Name") },
#define I_COLOR (2)
- { PD_COLORLIST, &layerColor, "color", PDO_NOPREF, NULL, N_("Color") },
+ { PD_COLORLIST, &layerColor, "color", PDO_NOPREF, NULL, N_("Color") },
#define I_VIS (3)
- { PD_TOGGLE, &layerVisible, "visible", PDO_NOPREF, visibleLabels, N_("Visible"), BC_HORZ|BC_NOBORDER },
+ { PD_TOGGLE, &layerVisible, "visible", PDO_NOPREF, visibleLabels, N_("Visible"), BC_HORZ|BC_NOBORDER },
#define I_FRZ (4)
- { PD_TOGGLE, &layerFrozen, "frozen", PDO_NOPREF|PDO_DLGHORZ, frozenLabels, N_("Frozen"), BC_HORZ|BC_NOBORDER },
+ { PD_TOGGLE, &layerFrozen, "frozen", PDO_NOPREF|PDO_DLGHORZ, frozenLabels, N_("Frozen"), BC_HORZ|BC_NOBORDER },
#define I_MAP (5)
- { PD_TOGGLE, &layerOnMap, "onmap", PDO_NOPREF|PDO_DLGHORZ, onMapLabels, N_("On Map"), BC_HORZ|BC_NOBORDER },
+ { PD_TOGGLE, &layerOnMap, "onmap", PDO_NOPREF|PDO_DLGHORZ, onMapLabels, N_("On Map"), BC_HORZ|BC_NOBORDER },
#define I_COUNT (6)
- { PD_STRING, NULL, "object-count", PDO_NOPREF|PDO_DLGBOXEND, (void*)(80), N_("Count"), BO_READONLY },
- { PD_MESSAGE, N_("Personal Preferences"), NULL, PDO_DLGRESETMARGIN, (void *)180 },
- { PD_BUTTON, (void*)DoLayerOp, "reset", PDO_DLGRESETMARGIN, 0, N_("Load"), 0, (void *)ENUMLAYER_RELOAD },
- { PD_BUTTON, (void*)DoLayerOp, "save", PDO_DLGHORZ, 0, N_("Save"), 0, (void *)ENUMLAYER_SAVE },
- { PD_BUTTON, (void*)DoLayerOp, "clear", PDO_DLGHORZ | PDO_DLGBOXEND, 0, N_("Defaults"), 0, (void *)ENUMLAYER_CLEAR },
- { PD_LONG, &newLayerCount, "button-count", PDO_DLGBOXEND|PDO_DLGRESETMARGIN, &i0_20, N_("Number of Layer Buttons") },
+ { PD_STRING, NULL, "object-count", PDO_NOPREF|PDO_DLGBOXEND, (void*)(80), N_("Count"), BO_READONLY },
+ { PD_MESSAGE, N_("Personal Preferences"), NULL, PDO_DLGRESETMARGIN, (void *)180 },
+ { PD_BUTTON, (void*)DoLayerOp, "reset", PDO_DLGRESETMARGIN, 0, N_("Load"), 0, (void *)ENUMLAYER_RELOAD },
+ { PD_BUTTON, (void*)DoLayerOp, "save", PDO_DLGHORZ, 0, N_("Save"), 0, (void *)ENUMLAYER_SAVE },
+ { PD_BUTTON, (void*)DoLayerOp, "clear", PDO_DLGHORZ | PDO_DLGBOXEND, 0, N_("Defaults"), 0, (void *)ENUMLAYER_CLEAR },
+ { PD_LONG, &newLayerCount, "button-count", PDO_DLGBOXEND|PDO_DLGRESETMARGIN, &i0_20, N_("Number of Layer Buttons") },
};
static paramGroup_t layerPG = { "layer", 0, layerPLs, sizeof layerPLs/sizeof layerPLs[0] };
@@ -433,120 +466,125 @@ static paramGroup_t layerPG = { "layer", 0, layerPLs, sizeof layerPLs/sizeof lay
/**
* Load the layer settings to hard coded system defaults
*/
-
+
void
-LayerSystemDefaults( void )
+LayerSystemDefaults(void)
{
- int inx;
-
- for ( inx=0;inx<NUM_LAYERS; inx++ ) {
- strcpy( layers[inx].name, inx==0?_("Main"):"" );
- layers[inx].visible = TRUE;
- layers[inx].frozen = FALSE;
- layers[inx].onMap = TRUE;
- layers[inx].objCount = 0;
- SetLayerColor( inx, layerColorTab[inx%COUNT(layerColorTab)] );
- }
+ int inx;
+
+ for (inx=0; inx<NUM_LAYERS; inx++) {
+ strcpy(layers[inx].name, inx==0?_("Main"):"");
+ layers[inx].visible = TRUE;
+ layers[inx].frozen = FALSE;
+ layers[inx].onMap = TRUE;
+ layers[inx].objCount = 0;
+ SetLayerColor(inx, layerColorTab[inx%COUNT(layerColorTab)]);
+ }
}
/**
- * Load the layer listboxes in Manage Layers and the Toolbar with up-to-date information.
+ * Load the layer listboxes in Manage Layers and the Toolbar with up-to-date information.
*/
-EXPORT void LoadLayerLists( void )
+void LoadLayerLists(void)
{
- int inx;
-
- /* clear both lists */
- wListClear(setLayerL);
- if ( layerL )
- wListClear(layerL);
-
- /* add all layers to both lists */
- for ( inx=0; inx<NUM_LAYERS; inx++ ) {
-
- if ( layerL ) {
- sprintf( message, "%2d %c %s", inx+1, layers[inx].objCount>0?'+':'-', layers[inx].name );
- wListAddValue( layerL, message, NULL, NULL );
- }
-
- sprintf( message, "%2d : %s", inx+1, layers[inx].name );
- wListAddValue( setLayerL, message, NULL, NULL );
- }
-
- /* set current layer to selected */
- wListSetIndex( setLayerL, curLayer );
- if ( layerL )
- wListSetIndex( layerL, curLayer );
+ int inx;
+ /* clear both lists */
+ wListClear(setLayerL);
+
+ if (layerL) {
+ wListClear(layerL);
+ }
+
+ /* add all layers to both lists */
+ for (inx=0; inx<NUM_LAYERS; inx++) {
+ char *layerLabel;
+ layerLabel = FormatLayerName(inx);
+
+ if (layerL) {
+ wListAddValue(layerL, layerLabel, NULL, NULL);
+ }
+
+ wListAddValue(setLayerL, layerLabel, NULL, NULL);
+ free(layerLabel);
+ }
+
+ /* set current layer to selected */
+ wListSetIndex(setLayerL, curLayer);
+
+ if (layerL) {
+ wListSetIndex(layerL, curLayer);
+ }
}
/**
* Handle button presses for the layer dialog. For all button presses in the layer
* dialog, this function is called. The parameter identifies the button pressed and
- * the operation is performed.
+ * the operation is performed.
*
* \param[IN] data identifier for the button prerssed
- * \return
+ * \return
*/
-static void DoLayerOp( void * data )
+static void DoLayerOp(void * data)
{
- switch((long)data ) {
-
- case ENUMLAYER_CLEAR:
- InitializeLayers( LayerSystemDefaults, -1 );
- break;
- case ENUMLAYER_SAVE:
- LayerPrefSave();
- break;
- case ENUMLAYER_RELOAD:
- LayerPrefLoad();
- break;
- }
-
- UpdateLayerDlg();
- if( layoutLayerChanged ) {
- MainProc( mainW, wResize_e, NULL );
- layoutLayerChanged = FALSE;
- changed = TRUE;
- SetWindowTitle();
- }
+ switch ((long)data) {
+ case ENUMLAYER_CLEAR:
+ InitializeLayers(LayerSystemDefaults, -1);
+ break;
+
+ case ENUMLAYER_SAVE:
+ LayerPrefSave();
+ break;
+
+ case ENUMLAYER_RELOAD:
+ LayerPrefLoad();
+ break;
+ }
+
+ UpdateLayerDlg();
+
+ if (layoutLayerChanged) {
+ MainProc(mainW, wResize_e, NULL);
+ layoutLayerChanged = FALSE;
+ changed = TRUE;
+ SetWindowTitle();
+ }
}
/**
- * Update all dialogs and dialog elements after changing layers preferences. Once the global array containing
+ * Update all dialogs and dialog elements after changing layers preferences. Once the global array containing
* the settings for the labels has been changed, this function needs to be called to update all the user interface
* elements to the new settings.
*/
-static void
+static void
UpdateLayerDlg()
{
- int inx;
-
- /* update the globals for the layer dialog */
- layerVisible = layers[curLayer].visible;
- layerFrozen = layers[curLayer].frozen;
- layerOnMap = layers[curLayer].onMap;
- layerColor = layers[curLayer].color;
- strcpy( layerName, layers[curLayer].name );
- layerCurrent = curLayer;
-
- /* now re-load the layer list boxes */
- LoadLayerLists();
-
- sprintf( message, "%ld", layers[curLayer].objCount );
- ParamLoadMessage( &layerPG, I_COUNT, message );
-
- /* force update of the 'manage layers' dialogbox */
- if( layerL )
- ParamLoadControls( &layerPG );
-
- /* finally show the layer buttons with ballon text */
- for( inx = 0; inx < NUM_BUTTONS; inx++ ) {
- wButtonSetBusy( layer_btns[inx], layers[inx].visible != 0 );
- wControlSetBalloonText( (wControl_p)layer_btns[inx], (layers[inx].name[0] != '\0' ? layers[inx].name :_("Show/Hide Layer") ));
- }
+ int inx;
+ /* update the globals for the layer dialog */
+ layerVisible = layers[curLayer].visible;
+ layerFrozen = layers[curLayer].frozen;
+ layerOnMap = layers[curLayer].onMap;
+ layerColor = layers[curLayer].color;
+ strcpy(layerName, layers[curLayer].name);
+ layerCurrent = curLayer;
+ /* now re-load the layer list boxes */
+ LoadLayerLists();
+ sprintf(message, "%ld", layers[curLayer].objCount);
+ ParamLoadMessage(&layerPG, I_COUNT, message);
+
+ /* force update of the 'manage layers' dialogbox */
+ if (layerL) {
+ ParamLoadControls(&layerPG);
+ }
+
+ /* finally show the layer buttons with ballon text */
+ for (inx = 0; inx < NUM_BUTTONS; inx++) {
+ wButtonSetBusy(layer_btns[inx], layers[inx].visible != 0);
+ wControlSetBalloonText((wControl_p)layer_btns[inx],
+ (layers[inx].name[0] != '\0' ? layers[inx].name :_("Show/Hide Layer")));
+ }
}
/**
@@ -557,164 +595,170 @@ UpdateLayerDlg()
*/
static void
-InitializeLayers( void LayerInitFunc( void ), int newCurrLayer )
+InitializeLayers(void LayerInitFunc(void), int newCurrLayer)
{
- /* reset the data structures to default valuses */
- LayerInitFunc();
-
- /* count the objects on each layer */
- LayerSetCounts();
-
- /* Switch the current layer when requested */
- if( newCurrLayer != -1 )
- {
- curLayer = newCurrLayer;
- }
-}
+ /* reset the data structures to default valuses */
+ LayerInitFunc();
+ /* count the objects on each layer */
+ LayerSetCounts();
+
+ /* Switch the current layer when requested */
+ if (newCurrLayer != -1) {
+ curLayer = newCurrLayer;
+ }
+}
/**
* Save the customized layer information to preferences.
*/
-
-static void
-LayerPrefSave( void )
+
+static void
+LayerPrefSave(void)
{
- int inx;
- int flags;
- char buffer[ 80 ];
- char layersSaved[ 3 * NUM_LAYERS ]; /* 0..99 plus separator */
-
- /* FIXME: values for layers that are configured to default now should be overwritten in the settings */
-
- layersSaved[ 0 ] = '\0';
-
- for( inx = 0; inx < NUM_LAYERS; inx++ ) {
- /* if a name is set that is not the default value or a color different from the default has been set,
- information about the layer needs to be saved */
- if( (layers[inx].name[0] && inx != 0 ) ||
- layers[inx].frozen || (!layers[inx].onMap) || (!layers[inx].visible) ||
- layers[inx].color != layerColorTab[inx%COUNT(layerColorTab)])
- {
- sprintf( buffer, LAYERPREF_NAME ".%0d", inx );
- wPrefSetString( LAYERPREF_SECTION, buffer, layers[inx].name );
-
- sprintf( buffer, LAYERPREF_COLOR ".%0d", inx );
- wPrefSetInteger( LAYERPREF_SECTION, buffer, wDrawGetRGB(layers[inx].color));
-
- flags = 0;
- if( layers[inx].frozen )
- flags |= LAYERPREF_FROZEN;
- if( layers[inx].onMap )
- flags |= LAYERPREF_ONMAP;
- if( layers[inx].visible )
- flags |= LAYERPREF_VISIBLE;
-
- sprintf( buffer, LAYERPREF_FLAGS ".%0d", inx );
- wPrefSetInteger( LAYERPREF_SECTION, buffer, flags );
-
- /* extend the list of layers that are set up via the preferences */
- if( layersSaved[ 0 ] )
- strcat( layersSaved, "," );
-
- sprintf( layersSaved, "%s%d", layersSaved, inx );
- }
- }
-
- wPrefSetString( LAYERPREF_SECTION, "layers", layersSaved );
+ unsigned int inx;
+ int flags;
+ char buffer[ 80 ];
+ char layersSaved[ 3 * NUM_LAYERS + 1 ]; /* 0..99 plus separator */
+ /* FIXME: values for layers that are configured to default now should be overwritten in the settings */
+ layersSaved[ 0 ] = '\0';
+
+ for (inx = 0; inx < NUM_LAYERS; inx++) {
+ /* if a name is set that is not the default value or a color different from the default has been set,
+ information about the layer needs to be saved */
+ if ((layers[inx].name[0] && inx != 0) ||
+ layers[inx].frozen || (!layers[inx].onMap) || (!layers[inx].visible) ||
+ layers[inx].color != layerColorTab[inx%COUNT(layerColorTab)]) {
+ sprintf(buffer, LAYERPREF_NAME ".%0u", inx);
+ wPrefSetString(LAYERPREF_SECTION, buffer, layers[inx].name);
+ sprintf(buffer, LAYERPREF_COLOR ".%0u", inx);
+ wPrefSetInteger(LAYERPREF_SECTION, buffer, wDrawGetRGB(layers[inx].color));
+ flags = 0;
+
+ if (layers[inx].frozen) {
+ flags |= LAYERPREF_FROZEN;
+ }
+
+ if (layers[inx].onMap) {
+ flags |= LAYERPREF_ONMAP;
+ }
+
+ if (layers[inx].visible) {
+ flags |= LAYERPREF_VISIBLE;
+ }
+
+ sprintf(buffer, LAYERPREF_FLAGS ".%0u", inx);
+ wPrefSetInteger(LAYERPREF_SECTION, buffer, flags);
+
+ /* extend the list of layers that are set up via the preferences */
+ if (layersSaved[ 0 ]) {
+ strcat(layersSaved, ",");
+ }
+
+ sprintf(buffer, "%u", inx);
+ strcat(layersSaved, buffer);
+ }
+ }
+
+ wPrefSetString(LAYERPREF_SECTION, "layers", layersSaved);
}
/**
- * Load the settings for all layers from the preferences.
+ * Load the settings for all layers from the preferences.
*/
static void
-LayerPrefLoad( void )
+LayerPrefLoad(void)
{
-
- int inx;
- char layersSaved[ 3 * NUM_LAYERS ];
- char layerOption[ 20 ];
- const char *layerValue;
- const char *prefString;
- long rgb;
- int color;
- long flags;
-
- /* reset layer preferences to system default */
- LayerSystemDefaults();
-
- prefString = wPrefGetString( LAYERPREF_SECTION, "layers" );
- if( prefString && prefString[ 0 ] ) {
- strncpy( layersSaved, prefString, sizeof( layersSaved ));
- prefString = strtok( layersSaved, "," );
- while( prefString ) {
- inx = atoi( prefString );
- sprintf( layerOption, LAYERPREF_NAME ".%d", inx );
- layerValue = wPrefGetString( LAYERPREF_SECTION, layerOption );
- if( layerValue )
- strcpy( layers[inx].name, layerValue );
- else
- *(layers[inx].name) = '\0';
-
- /* get and set the color, using the system default color in case color is not available from prefs */
- sprintf( layerOption, LAYERPREF_COLOR ".%d", inx );
- wPrefGetInteger( LAYERPREF_SECTION, layerOption, &rgb, layerColorTab[inx%COUNT(layerColorTab)] );
- color = wDrawFindColor(rgb);
- SetLayerColor( inx, color );
-
- /* get and set the flags */
- sprintf( layerOption, LAYERPREF_FLAGS ".%d", inx );
- wPrefGetInteger( LAYERPREF_SECTION, layerOption, &flags, LAYERPREF_ONMAP | LAYERPREF_VISIBLE );
-
- layers[inx].frozen = ((flags & LAYERPREF_FROZEN) != 0 );
- layers[inx].onMap = ((flags & LAYERPREF_ONMAP) != 0 );
- layers[inx].visible = (( flags & LAYERPREF_VISIBLE ) != 0 );
-
- prefString = strtok( NULL, ",");
- }
- }
+ const char *prefString;
+ long rgb;
+ long flags;
+ /* reset layer preferences to system default */
+ LayerSystemDefaults();
+ prefString = wPrefGetString(LAYERPREF_SECTION, "layers");
+
+ if (prefString && prefString[ 0 ]) {
+ char layersSaved[3 * NUM_LAYERS];
+ strncpy(layersSaved, prefString, sizeof(layersSaved));
+ prefString = strtok(layersSaved, ",");
+
+ while (prefString) {
+ int inx;
+ char layerOption[20];
+ const char *layerValue;
+ int color;
+ inx = atoi(prefString);
+ sprintf(layerOption, LAYERPREF_NAME ".%d", inx);
+ layerValue = wPrefGetString(LAYERPREF_SECTION, layerOption);
+
+ if (layerValue) {
+ strcpy(layers[inx].name, layerValue);
+ } else {
+ *(layers[inx].name) = '\0';
+ }
+
+ /* get and set the color, using the system default color in case color is not available from prefs */
+ sprintf(layerOption, LAYERPREF_COLOR ".%d", inx);
+ wPrefGetInteger(LAYERPREF_SECTION, layerOption, &rgb,
+ layerColorTab[inx%COUNT(layerColorTab)]);
+ color = wDrawFindColor(rgb);
+ SetLayerColor(inx, color);
+ /* get and set the flags */
+ sprintf(layerOption, LAYERPREF_FLAGS ".%d", inx);
+ wPrefGetInteger(LAYERPREF_SECTION, layerOption, &flags,
+ LAYERPREF_ONMAP | LAYERPREF_VISIBLE);
+ layers[inx].frozen = ((flags & LAYERPREF_FROZEN) != 0);
+ layers[inx].onMap = ((flags & LAYERPREF_ONMAP) != 0);
+ layers[inx].visible = ((flags & LAYERPREF_VISIBLE) != 0);
+ prefString = strtok(NULL, ",");
+ }
+ }
}
/**
- * Count the number of elements on a layer.
- * NOTE: This function has been implemented but not actually been tested. As it might prove useful in the
- * future I left it in place. So you have been warned!
- * \param IN layer to count
- * \return number of elements
+ * Increment the count of objects on a given layer.
+ *
+ * \param index IN the layer to change
*/
-/*
-static int LayerCount( int layer )
+
+void IncrementLayerObjects(unsigned int layer)
{
- track_p trk;
- int inx;
- int count = 0;
-
- for( trk = NULL; TrackIterate(&trk); ) {
- inx = GetTrkLayer( trk );
- if( inx == layer )
- count++;
- }
-
- return count;
-}
+ assert(layer <= NUM_LAYERS);
+ layers[layer].objCount++;
+}
+
+/**
+* Decrement the count of objects on a given layer.
+*
+* \param index IN the layer to change
*/
+void DecrementLayerObjects(unsigned int layer)
+{
+ assert(layer <= NUM_LAYERS);
+ layers[layer].objCount--;
+}
+
/**
* Count the number of objects on each layer and store result in layers data structure.
*/
-EXPORT void LayerSetCounts( void )
+void LayerSetCounts(void)
{
- int inx;
- track_p trk;
- for ( inx=0; inx<NUM_LAYERS; inx++ )
- layers[inx].objCount = 0;
- for ( trk=NULL; TrackIterate(&trk); ) {
- inx = GetTrkLayer(trk);
- if ( inx >= 0 && inx < NUM_LAYERS )
- layers[inx].objCount++;
- }
+ int inx;
+ track_p trk;
+
+ for (inx=0; inx<NUM_LAYERS; inx++) {
+ layers[inx].objCount = 0;
+ }
+
+ for (trk=NULL; TrackIterate(&trk);) {
+ inx = GetTrkLayer(trk);
+
+ if (inx >= 0 && inx < NUM_LAYERS) {
+ layers[inx].objCount++;
+ }
+ }
}
/**
@@ -722,16 +766,16 @@ EXPORT void LayerSetCounts( void )
* from the preferences file.
*/
-EXPORT void
+void
DefaultLayerProperties(void)
{
- InitializeLayers( LayerPrefLoad, 0 );
+ InitializeLayers(LayerPrefLoad, 0);
+ UpdateLayerDlg();
- UpdateLayerDlg();
- if( layoutLayerChanged ) {
- MainProc( mainW, wResize_e, NULL );
- layoutLayerChanged = FALSE;
- }
+ if (layoutLayerChanged) {
+ MainProc(mainW, wResize_e, NULL);
+ layoutLayerChanged = FALSE;
+ }
}
/**
@@ -739,158 +783,190 @@ DefaultLayerProperties(void)
*
*/
-static void LayerUpdate( void )
+static void LayerUpdate(void)
{
- BOOL_T redraw;
- ParamLoadData( &layerPG );
- if (layerCurrent < 0 || layerCurrent >= NUM_LAYERS)
- return;
- if (layerCurrent == curLayer && layerFrozen) {
- NoticeMessage( MSG_LAYER_FREEZE, _("Ok"), NULL );
- layerFrozen = FALSE;
- ParamLoadControl( &layerPG, I_FRZ );
- }
- if (layerCurrent == curLayer && !layerVisible) {
- NoticeMessage( MSG_LAYER_HIDE, _("Ok"), NULL );
- layerVisible = TRUE;
- ParamLoadControl( &layerPG, I_VIS );
- }
-
- if( strcmp( layers[(int)layerCurrent].name, layerName ) ||
- layerColor != layers[(int)layerCurrent].color ||
- layers[(int)layerCurrent].visible != (BOOL_T)layerVisible ||
- layers[(int)layerCurrent].frozen != (BOOL_T)layerFrozen ||
- layers[(int)layerCurrent].onMap != (BOOL_T)layerOnMap ) {
-
- changed = TRUE;
- SetWindowTitle();
- }
-
- if ( layerL ) {
- strncpy( layers[(int)layerCurrent].name, layerName, sizeof layers[(int)layerCurrent].name );
- sprintf( message, "%2d %c %s", (int)layerCurrent+1, layers[(int)layerCurrent].objCount>0?'+':'-', layers[(int)layerCurrent].name );
- wListSetValues( layerL, layerCurrent, message, NULL, NULL );
- }
-
- sprintf( message, "%2d : %s", (int)layerCurrent+1, layers[(int)layerCurrent].name );
- wListSetValues( setLayerL, layerCurrent, message, NULL, NULL );
- if (layerCurrent < NUM_BUTTONS) {
- if (strlen(layers[(int)layerCurrent].name)>0)
- wControlSetBalloonText( (wControl_p)layer_btns[(int)layerCurrent], layers[(int)layerCurrent].name );
- else
- wControlSetBalloonText( (wControl_p)layer_btns[(int)layerCurrent], _("Show/Hide Layer") );
- }
- redraw = ( layerColor != layers[(int)layerCurrent].color ||
- (BOOL_T)layerVisible != layers[(int)layerCurrent].visible );
- if ( (!layerRedrawMap) && redraw)
- RedrawLayer( (LAYER_T)layerCurrent, FALSE );
-
- SetLayerColor( layerCurrent, layerColor );
-
- if (layerCurrent<NUM_BUTTONS && layers[(int)layerCurrent].visible!=(BOOL_T)layerVisible) {
- wButtonSetBusy( layer_btns[(int)layerCurrent], layerVisible );
- }
- layers[(int)layerCurrent].visible = (BOOL_T)layerVisible;
- layers[(int)layerCurrent].frozen = (BOOL_T)layerFrozen;
- layers[(int)layerCurrent].onMap = (BOOL_T)layerOnMap;
- if ( layerRedrawMap )
- DoRedraw();
- else if (redraw)
- RedrawLayer( (LAYER_T)layerCurrent, TRUE );
- layerRedrawMap = FALSE;
+ BOOL_T redraw;
+ char *layerFormattedName;
+ ParamLoadData(&layerPG);
+
+ if (!IsLayerValid(layerCurrent)) {
+ return;
+ }
+
+ if (layerCurrent == curLayer && layerFrozen) {
+ NoticeMessage(MSG_LAYER_FREEZE, _("Ok"), NULL);
+ layerFrozen = FALSE;
+ ParamLoadControl(&layerPG, I_FRZ);
+ }
+
+ if (layerCurrent == curLayer && !layerVisible) {
+ NoticeMessage(MSG_LAYER_HIDE, _("Ok"), NULL);
+ layerVisible = TRUE;
+ ParamLoadControl(&layerPG, I_VIS);
+ }
+
+ if (strcmp(layers[(int)layerCurrent].name, layerName) ||
+ layerColor != layers[(int)layerCurrent].color ||
+ layers[(int)layerCurrent].visible != (BOOL_T)layerVisible ||
+ layers[(int)layerCurrent].frozen != (BOOL_T)layerFrozen ||
+ layers[(int)layerCurrent].onMap != (BOOL_T)layerOnMap) {
+ changed = TRUE;
+ SetWindowTitle();
+ }
+
+ if (layerL) {
+ strncpy(layers[(int)layerCurrent].name, layerName,
+ sizeof layers[(int)layerCurrent].name);
+ layerFormattedName = FormatLayerName(layerCurrent);
+ wListSetValues(layerL, layerCurrent, layerFormattedName, NULL, NULL);
+ free(layerFormattedName);
+ }
+
+ layerFormattedName = FormatLayerName(layerCurrent);
+ wListSetValues(setLayerL, layerCurrent, layerFormattedName, NULL, NULL);
+ free(layerFormattedName);
+
+ if (layerCurrent < NUM_BUTTONS) {
+ if (strlen(layers[(int)layerCurrent].name)>0) {
+ wControlSetBalloonText((wControl_p)layer_btns[(int)layerCurrent],
+ layers[(int)layerCurrent].name);
+ } else {
+ wControlSetBalloonText((wControl_p)layer_btns[(int)layerCurrent],
+ _("Show/Hide Layer"));
+ }
+ }
+
+ redraw = (layerColor != layers[(int)layerCurrent].color ||
+ (BOOL_T)layerVisible != layers[(int)layerCurrent].visible);
+
+ if ((!layerRedrawMap) && redraw) {
+ RedrawLayer((unsigned int)layerCurrent, FALSE);
+ }
+
+ SetLayerColor(layerCurrent, layerColor);
+
+ if (layerCurrent<NUM_BUTTONS &&
+ layers[(int)layerCurrent].visible!=(BOOL_T)layerVisible) {
+ wButtonSetBusy(layer_btns[(int)layerCurrent], layerVisible);
+ }
+
+ layers[(int)layerCurrent].visible = (BOOL_T)layerVisible;
+ layers[(int)layerCurrent].frozen = (BOOL_T)layerFrozen;
+ layers[(int)layerCurrent].onMap = (BOOL_T)layerOnMap;
+
+ if (layerRedrawMap) {
+ DoRedraw();
+ } else if (redraw) {
+ RedrawLayer((unsigned int)layerCurrent, TRUE);
+ }
+
+ layerRedrawMap = FALSE;
}
-static void LayerSelect(
- wIndex_t inx )
+static void LayerSelect(
+ wIndex_t inx)
{
- LayerUpdate();
- if (inx < 0 || inx >= NUM_LAYERS)
- return;
- layerCurrent = (LAYER_T)inx;
- strcpy( layerName, layers[inx].name );
- layerVisible = layers[inx].visible;
- layerFrozen = layers[inx].frozen;
- layerOnMap = layers[inx].onMap;
- layerColor = layers[inx].color;
- sprintf( message, "%ld", layers[inx].objCount );
-
- ParamLoadMessage( &layerPG, I_COUNT, message );
- ParamLoadControls( &layerPG );
+ LayerUpdate();
+
+ if (inx < 0 || inx >= NUM_LAYERS) {
+ return;
+ }
+
+ layerCurrent = (unsigned int)inx;
+ strcpy(layerName, layers[inx].name);
+ layerVisible = layers[inx].visible;
+ layerFrozen = layers[inx].frozen;
+ layerOnMap = layers[inx].onMap;
+ layerColor = layers[inx].color;
+ sprintf(message, "%ld", layers[inx].objCount);
+ ParamLoadMessage(&layerPG, I_COUNT, message);
+ ParamLoadControls(&layerPG);
}
-EXPORT void ResetLayers( void )
+void ResetLayers(void)
{
- int inx;
- for ( inx=0;inx<NUM_LAYERS; inx++ ) {
- strcpy( layers[inx].name, inx==0?_("Main"):"" );
- layers[inx].visible = TRUE;
- layers[inx].frozen = FALSE;
- layers[inx].onMap = TRUE;
- layers[inx].objCount = 0;
- SetLayerColor( inx, layerColorTab[inx%COUNT(layerColorTab)] );
- if ( inx<NUM_BUTTONS ) {
- wButtonSetLabel( layer_btns[inx], (char*)show_layer_bmps[inx] );
- }
- }
- wControlSetBalloonText( (wControl_p)layer_btns[0], _("Main") );
- for ( inx=1; inx<NUM_BUTTONS; inx++ ) {
- wControlSetBalloonText( (wControl_p)layer_btns[inx], _("Show/Hide Layer") );
- }
- curLayer = 0;
- layerVisible = TRUE;
- layerFrozen = FALSE;
- layerOnMap = TRUE;
- layerColor = layers[0].color;
- strcpy( layerName, layers[0].name );
- LoadLayerLists();
-
- if (layerL) {
- ParamLoadControls( &layerPG );
- ParamLoadMessage( &layerPG, I_COUNT, "0" );
- }
+ int inx;
+
+ for (inx=0; inx<NUM_LAYERS; inx++) {
+ strcpy(layers[inx].name, inx==0?_("Main"):"");
+ layers[inx].visible = TRUE;
+ layers[inx].frozen = FALSE;
+ layers[inx].onMap = TRUE;
+ layers[inx].objCount = 0;
+ SetLayerColor(inx, layerColorTab[inx%COUNT(layerColorTab)]);
+
+ if (inx<NUM_BUTTONS) {
+ wButtonSetLabel(layer_btns[inx], (char*)show_layer_bmps[inx]);
+ }
+ }
+
+ wControlSetBalloonText((wControl_p)layer_btns[0], _("Main"));
+
+ for (inx=1; inx<NUM_BUTTONS; inx++) {
+ wControlSetBalloonText((wControl_p)layer_btns[inx], _("Show/Hide Layer"));
+ }
+
+ curLayer = 0;
+ layerVisible = TRUE;
+ layerFrozen = FALSE;
+ layerOnMap = TRUE;
+ layerColor = layers[0].color;
+ strcpy(layerName, layers[0].name);
+ LoadLayerLists();
+
+ if (layerL) {
+ ParamLoadControls(&layerPG);
+ ParamLoadMessage(&layerPG, I_COUNT, "0");
+ }
}
-EXPORT void SaveLayers( void )
+void SaveLayers(void)
{
- layers_save = malloc( NUM_LAYERS * sizeof( layer_t ));
- assert( layers_save != NULL );
-
- memcpy( layers_save, layers, NUM_LAYERS * sizeof layers[0] );
- ResetLayers();
+ layers_save = malloc(NUM_LAYERS * sizeof(layers[0]));
+
+ if (layers_save == NULL) {
+ abort();
+ }
+
+ memcpy(layers_save, layers, NUM_LAYERS * sizeof layers[0]);
+ ResetLayers();
}
-EXPORT void RestoreLayers( void )
+void RestoreLayers(void)
{
- int inx;
- char * label;
- wDrawColor color;
-
- assert( layers_save != NULL );
- memcpy( layers, layers_save, NUM_LAYERS * sizeof layers[0] );
- free( layers_save );
-
- for ( inx=0; inx<NUM_BUTTONS; inx++ ) {
- color = layers[inx].color;
- layers[inx].color = -1;
- SetLayerColor( inx, color );
- if ( layers[inx].name[0] == '\0' ) {
- if ( inx == 0 ) {
- label = _("Main");
- } else {
- label = _("Show/Hide Layer");
- }
- } else {
- label = layers[inx].name;
- }
- wControlSetBalloonText( (wControl_p)layer_btns[inx], label );
- }
- if (layerL) {
- ParamLoadControls( &layerPG );
- ParamLoadMessage( &layerPG, I_COUNT, "0" );
- }
- LoadLayerLists();
+ int inx;
+ char * label;
+ wDrawColor color;
+ assert(layers_save != NULL);
+ memcpy(layers, layers_save, NUM_LAYERS * sizeof layers[0]);
+ free(layers_save);
+
+ for (inx=0; inx<NUM_BUTTONS; inx++) {
+ color = layers[inx].color;
+ layers[inx].color = -1;
+ SetLayerColor(inx, color);
+
+ if (layers[inx].name[0] == '\0') {
+ if (inx == 0) {
+ label = _("Main");
+ } else {
+ label = _("Show/Hide Layer");
+ }
+ } else {
+ label = layers[inx].name;
+ }
+
+ wControlSetBalloonText((wControl_p)layer_btns[inx], label);
+ }
+
+ if (layerL) {
+ ParamLoadControls(&layerPG);
+ ParamLoadMessage(&layerPG, I_COUNT, "0");
+ }
+
+ LoadLayerLists();
}
/**
@@ -900,172 +976,240 @@ EXPORT void RestoreLayers( void )
* \param IN ignored
*
*/
-
-static void LayerOk( void * junk )
+
+static void LayerOk(void * junk)
{
- LayerSelect( layerCurrent );
-
- if (newLayerCount != layerCount) {
- layoutLayerChanged = TRUE;
- if ( newLayerCount > NUM_BUTTONS )
- newLayerCount = NUM_BUTTONS;
- layerCount = newLayerCount;
- }
- if (layoutLayerChanged)
- MainProc( mainW, wResize_e, NULL );
- wHide( layerW );
+ LayerSelect(layerCurrent);
+
+ if (newLayerCount != layerCount) {
+ layoutLayerChanged = TRUE;
+
+ if (newLayerCount > NUM_BUTTONS) {
+ newLayerCount = NUM_BUTTONS;
+ }
+
+ layerCount = newLayerCount;
+ }
+
+ if (layoutLayerChanged) {
+ MainProc(mainW, wResize_e, NULL);
+ }
+
+ wHide(layerW);
}
static void LayerDlgUpdate(
- paramGroup_p pg,
- int inx,
- void * valueP )
+ paramGroup_p pg,
+ int inx,
+ void * valueP)
{
- switch (inx) {
- case I_LIST:
- LayerSelect( (wIndex_t)*(long*)valueP );
- break;
- case I_NAME:
- LayerUpdate();
- break;
- case I_MAP:
- layerRedrawMap = TRUE;
- break;
- }
+ switch (inx) {
+ case I_LIST:
+ LayerSelect((wIndex_t)*(long*)valueP);
+ break;
+
+ case I_NAME:
+ LayerUpdate();
+ break;
+
+ case I_MAP:
+ layerRedrawMap = TRUE;
+ break;
+ }
}
-static void DoLayer( void * junk )
+static void DoLayer(void * junk)
{
- if (layerW == NULL)
- layerW = ParamCreateDialog( &layerPG, MakeWindowTitle(_("Layers")), _("Done"), LayerOk, NULL, TRUE, NULL, 0, LayerDlgUpdate );
+ if (layerW == NULL) {
+ layerW = ParamCreateDialog(&layerPG, MakeWindowTitle(_("Layers")), _("Done"),
+ LayerOk, NULL, TRUE, NULL, 0, LayerDlgUpdate);
+ }
+
+ /* set the globals to the values for the current layer */
+ UpdateLayerDlg();
+ layerRedrawMap = FALSE;
+ wShow(layerW);
+ layoutLayerChanged = FALSE;
+}
- /* set the globals to the values for the current layer */
- UpdateLayerDlg();
-
- layerRedrawMap = FALSE;
- wShow( layerW );
- layoutLayerChanged = FALSE;
+BOOL_T ReadLayers(char * line)
+{
+ char * name;
+ int inx, visible, frozen, color, onMap;
+ unsigned long rgb;
+
+ /* older files didn't support layers */
+
+ if (paramVersion < 7) {
+ return TRUE;
+ }
+
+ /* set the current layer */
+
+ if (strncmp(line, "CURRENT", 7) == 0) {
+ curLayer = atoi(line+7);
+
+ if (!IsLayerValid(curLayer)) {
+ curLayer = 0;
+ }
+
+ if (layerL) {
+ wListSetIndex(layerL, curLayer);
+ }
+
+ if (setLayerL) {
+ wListSetIndex(setLayerL, curLayer);
+ }
+
+ return TRUE;
+ }
+
+ /* get the properties for a layer from the file and update the layer accordingly */
+
+ if (!GetArgs(line, "ddddu0000q", &inx, &visible, &frozen, &onMap, &rgb,
+ &name)) {
+ return FALSE;
+ }
+
+ if (paramVersion < 9) {
+ if ((int)rgb < sizeof oldColorMap/sizeof oldColorMap[0]) {
+ rgb = wRGB(oldColorMap[(int)rgb][0], oldColorMap[(int)rgb][1],
+ oldColorMap[(int)rgb][2]);
+ } else {
+ rgb = 0;
+ }
+ }
+
+ if (inx < 0 || inx >= NUM_LAYERS) {
+ return FALSE;
+ }
+
+ color = wDrawFindColor(rgb);
+ SetLayerColor(inx, color);
+ strncpy(layers[inx].name, name, sizeof layers[inx].name);
+ layers[inx].visible = visible;
+ layers[inx].frozen = frozen;
+ layers[inx].onMap = onMap;
+ layers[inx].color = color;
+
+ if (inx<NUM_BUTTONS) {
+ if (strlen(name) > 0) {
+ wControlSetBalloonText((wControl_p)layer_btns[(int)inx], layers[inx].name);
+ }
+
+ wButtonSetBusy(layer_btns[(int)inx], visible);
+ }
+ MyFree(name);
+
+ return TRUE;
}
+/**
+ * Find out whether layer information should be saved to the layout file.
+ * Usually only layers where settings are off from the default are written.
+ * NOTE: as a fix for a problem with XTrkCadReader a layer definition is
+ * written for each layer that is used.
+ *
+ * \param layerNumber IN index of the layer
+ * \return TRUE if configured, FALSE if not
+ */
-EXPORT BOOL_T ReadLayers( char * line )
+bool
+IsLayerConfigured(unsigned int layerNumber)
{
- char * name;
- int inx, visible, frozen, color, onMap;
- unsigned long rgb;
-
- /* older files didn't support layers */
-
- if (paramVersion < 7)
- return TRUE;
-
- /* set the current layer */
-
- if ( strncmp( line, "CURRENT", 7 ) == 0 ) {
- curLayer = atoi( line+7 );
- if ( curLayer < 0 )
- curLayer = 0;
-
- if (layerL)
- wListSetIndex( layerL, curLayer );
- if (setLayerL)
- wListSetIndex( setLayerL, curLayer );
-
- return TRUE;
- }
-
- /* get the properties for a layer from the file and update the layer accordingly */
-
- if (!GetArgs( line, "ddddu0000q", &inx, &visible, &frozen, &onMap, &rgb, &name ))
- return FALSE;
- if (paramVersion < 9) {
- if ( rgb >= 0 && (int)rgb < sizeof oldColorMap/sizeof oldColorMap[0] )
- rgb = wRGB( oldColorMap[(int)rgb][0], oldColorMap[(int)rgb][1], oldColorMap[(int)rgb][2] );
- else
- rgb = 0;
- }
- if (inx < 0 || inx >= NUM_LAYERS)
- return FALSE;
- color = wDrawFindColor(rgb);
- SetLayerColor( inx, color );
- strncpy( layers[inx].name, name, sizeof layers[inx].name );
- layers[inx].visible = visible;
- layers[inx].frozen = frozen;
- layers[inx].onMap = onMap;
- layers[inx].color = color;
- if (inx<NUM_BUTTONS) {
- if (strlen(name) > 0) {
- wControlSetBalloonText( (wControl_p)layer_btns[(int)inx], layers[inx].name );
- }
- wButtonSetBusy( layer_btns[(int)inx], visible );
- }
- return TRUE;
+ return (!layers[layerNumber].visible ||
+ layers[layerNumber].frozen ||
+ !layers[layerNumber].onMap ||
+ layers[layerNumber].color !=
+ layerColorTab[layerNumber % (COUNT(layerColorTab))] ||
+ layers[layerNumber].name[0] ||
+ layers[layerNumber].objCount);
}
+/**
+ * Save the layer information to the file.
+ *
+ * \paran f IN open file handle
+ * \return always TRUE
+ */
-EXPORT BOOL_T WriteLayers( FILE * f )
+BOOL_T WriteLayers(FILE * f)
{
- int inx;
- BOOL_T rc = TRUE;
- for (inx=0; inx<NUM_LAYERS; inx++)
- if ((!layers[inx].visible) || layers[inx].frozen || (!layers[inx].onMap) ||
- layers[inx].color!=layerColorTab[inx%(COUNT(layerColorTab))] ||
- layers[inx].name[0] )
- rc &= fprintf( f, "LAYERS %d %d %d %d %ld %d %d %d %d \"%s\"\n", inx, layers[inx].visible, layers[inx].frozen, layers[inx].onMap, wDrawGetRGB(layers[inx].color), 0, 0, 0, 0, PutTitle(layers[inx].name) )>0;
- rc &= fprintf( f, "LAYERS CURRENT %d\n", curLayer )>0;
- return TRUE;
+ unsigned int inx;
+
+ for (inx = 0; inx < NUM_LAYERS; inx++) {
+ if (IsLayerConfigured(inx)) {
+ fprintf(f, "LAYERS %u %d %d %d %ld %d %d %d %d \"%s\"\n",
+ inx,
+ layers[inx].visible,
+ layers[inx].frozen,
+ layers[inx].onMap,
+ wDrawGetRGB(layers[inx].color),
+ 0, 0, 0, 0,
+ PutTitle(layers[inx].name));
+ }
+ }
+
+ fprintf(f, "LAYERS CURRENT %u\n", curLayer);
+ return TRUE;
}
-EXPORT void InitLayers( void )
+void InitLayers(void)
{
- int i;
-
- wPrefGetInteger( PREFSECT, "layer-button-count", &layerCount, layerCount );
- for ( i = 0; i<COUNT(layerRawColorTab); i++ )
- layerColorTab[i] = wDrawFindColor( layerRawColorTab[i] );
-
- /* create the bitmaps for the layer buttons */
- /* all bitmaps have to have the same dimensions */
- for ( i = 0; i<NUM_BUTTONS; i++ ) {
- show_layer_bmps[i] = wIconCreateBitMap( l1_width, l1_height, show_layer_bits[i], layerColorTab[i%(COUNT(layerColorTab))] );
- layers[i].color = layerColorTab[i%(COUNT(layerColorTab))];
- }
-
- /* layer list for toolbar */
- setLayerL = wDropListCreate( mainW, 0, 0, "cmdLayerSet", NULL, 0, 10, 200, NULL, SetCurrLayer, NULL );
- wControlSetBalloonText( (wControl_p)setLayerL, GetBalloonHelpStr("cmdLayerSet") );
- AddToolbarControl( (wControl_p)setLayerL, IC_MODETRAIN_TOO );
-
- for ( i = 0; i<NUM_LAYERS; i++ ) {
- if (i<NUM_BUTTONS) {
- /* create the layer button */
- sprintf( message, "cmdLayerShow%d", i );
- layer_btns[i] = wButtonCreate( mainW, 0, 0, message,
- (char*)(show_layer_bmps[i]),
- BO_ICON, 0, (wButtonCallBack_p)FlipLayer, (void*)(intptr_t)i );
-
- /* add the help text */
- wControlSetBalloonText( (wControl_p)layer_btns[i], _("Show/Hide Layer") );
-
- /* put on toolbar */
- AddToolbarControl( (wControl_p)layer_btns[i], IC_MODETRAIN_TOO );
-
- /* set state of button */
- wButtonSetBusy( layer_btns[i], 1 );
- }
- sprintf( message, "%2d : %s", i+1, (i==0?_("Main"):"") );
- wListAddValue( setLayerL, message, NULL, (void*)(intptr_t)i );
- }
- AddPlaybackProc( "SETCURRLAYER", PlaybackCurrLayer, NULL );
- AddPlaybackProc( "LAYERS", (playbackProc_p)ReadLayers, NULL );
+ unsigned int i;
+ wPrefGetInteger(PREFSECT, "layer-button-count", &layerCount, layerCount);
+
+ for (i = 0; i<COUNT(layerRawColorTab); i++) {
+ layerColorTab[i] = wDrawFindColor(layerRawColorTab[i]);
+ }
+
+ /* create the bitmaps for the layer buttons */
+ /* all bitmaps have to have the same dimensions */
+ for (i = 0; i<NUM_BUTTONS; i++) {
+ show_layer_bmps[i] = wIconCreateBitMap(l1_width, l1_height, show_layer_bits[i],
+ layerColorTab[i%(COUNT(layerColorTab))]);
+ layers[i].color = layerColorTab[i%(COUNT(layerColorTab))];
+ }
+
+ /* layer list for toolbar */
+ setLayerL = wDropListCreate(mainW, 0, 0, "cmdLayerSet", NULL, 0, 10, 200, NULL,
+ SetCurrLayer, NULL);
+ wControlSetBalloonText((wControl_p)setLayerL, GetBalloonHelpStr("cmdLayerSet"));
+ AddToolbarControl((wControl_p)setLayerL, IC_MODETRAIN_TOO);
+
+ for (i = 0; i<NUM_LAYERS; i++) {
+ char *layerName;
+
+ if (i<NUM_BUTTONS) {
+ /* create the layer button */
+ sprintf(message, "cmdLayerShow%u", i);
+ layer_btns[i] = wButtonCreate(mainW, 0, 0, message,
+ (char*)(show_layer_bmps[i]),
+ BO_ICON, 0, (wButtonCallBack_p)FlipLayer, (void*)(intptr_t)i);
+ /* add the help text */
+ wControlSetBalloonText((wControl_p)layer_btns[i], _("Show/Hide Layer"));
+ /* put on toolbar */
+ AddToolbarControl((wControl_p)layer_btns[i], IC_MODETRAIN_TOO);
+ /* set state of button */
+ wButtonSetBusy(layer_btns[i], 1);
+ }
+
+ layerName = FormatLayerName(i);
+ wListAddValue(setLayerL, layerName, NULL, (void*)(long)i);
+ free(layerName);
+ }
+
+ AddPlaybackProc("SETCURRLAYER", PlaybackCurrLayer, NULL);
+ AddPlaybackProc("LAYERS", (playbackProc_p)ReadLayers, NULL);
}
-EXPORT addButtonCallBack_t InitLayersDialog( void ) {
- ParamRegister( &layerPG );
- return &DoLayer;
+addButtonCallBack_t InitLayersDialog(void)
+{
+ ParamRegister(&layerPG);
+ return &DoLayer;
}
diff --git a/app/bin/doption.c b/app/bin/doption.c
index b1533c9..ae36d21 100644
--- a/app/bin/doption.c
+++ b/app/bin/doption.c
@@ -21,9 +21,14 @@
*/
#include <ctype.h>
-#include "track.h"
+
#include "ccurve.h"
+#include "cselect.h"
+#include "custom.h"
#include "i18n.h"
+#include "messages.h"
+#include "param.h"
+#include "track.h"
static paramIntegerRange_t i0_64 = { 0, 64 };
static paramIntegerRange_t i1_64 = { 1, 64 };
@@ -36,17 +41,16 @@ static paramIntegerRange_t i10_100 = { 10, 100 };
static paramFloatRange_t r0o1_1 = { 0.1, 1 };
static paramFloatRange_t r1_10 = { 1, 10 };
static paramFloatRange_t r1_1000 = { 1, 1000 };
-static paramFloatRange_t r1_10000 = { 1, 10000 };
-static paramFloatRange_t r0_90 = { 0, 90 };
static paramFloatRange_t r0_180 = { 0, 180 };
-static paramFloatRange_t r1_9999999 = { 1, 9999999 };
static void UpdatePrefD( void );
+static void UpdateMeasureFmt(void);
+
+static wIndex_t distanceFormatInx;
EXPORT long enableBalloonHelp = 1;
-static long GetChanges(
- paramGroup_p pg )
+long GetChanges( paramGroup_p pg )
{
long changes;
long changed;
@@ -73,8 +77,13 @@ static void OptionDlgUpdate(
quickMove = *(long*)valueP;
UpdateQuickMove(NULL);
quickMove = quickMoveOld;
- } else if ( pg->paramPtr[inx].valueP == &units ) {
- UpdatePrefD();
+ } else {
+ if (pg->paramPtr[inx].valueP == &units) {
+ UpdatePrefD();
+ }
+ if (pg->paramPtr[inx].valueP == &distanceFormatInx) {
+ UpdateMeasureFmt();
+ }
}
}
@@ -86,121 +95,6 @@ static void OptionDlgCancel(
wHide( win );
}
-/****************************************************************************
- *
- * Layout Dialog
- *
- */
-
-static wWin_p layoutW;
-static coOrd newSize;
-
-static paramData_t layoutPLs[] = {
- { PD_FLOAT, &newSize.x, "roomsizeX", PDO_NOPREF|PDO_DIM|PDO_NOPSHUPD|PDO_DRAW, &r1_9999999, N_("Room Width"), 0, (void*)(CHANGE_MAIN|CHANGE_MAP) },
- { PD_FLOAT, &newSize.y, "roomsizeY", PDO_NOPREF|PDO_DIM|PDO_NOPSHUPD|PDO_DRAW|PDO_DLGHORZ, &r1_9999999, N_(" Height"), 0, (void*)(CHANGE_MAIN|CHANGE_MAP) },
- { PD_STRING, &Title1, "title1", PDO_NOPSHUPD, NULL, N_("Layout Title") },
- { PD_STRING, &Title2, "title2", PDO_NOPSHUPD, NULL, N_("Subtitle") },
- { PD_DROPLIST, &curScaleDescInx, "scale", PDO_NOPREF|PDO_NOPSHUPD|PDO_NORECORD|PDO_NOUPDACT, (void *)120, N_("Scale"), 0, (void*)(CHANGE_SCALE) },
- { PD_DROPLIST, &curGaugeInx, "gauge", PDO_NOPREF |PDO_NOPSHUPD|PDO_NORECORD|PDO_NOUPDACT|PDO_DLGHORZ, (void *)120, N_(" Gauge"), 0, (void *)(CHANGE_SCALE) },
- { PD_FLOAT, &minTrackRadius, "mintrackradius", PDO_DIM|PDO_NOPSHUPD|PDO_NOPREF, &r1_10000, N_("Min Track Radius"), 0, (void*)(CHANGE_MAIN|CHANGE_LIMITS) },
- { PD_FLOAT, &maxTrackGrade, "maxtrackgrade", PDO_NOPSHUPD|PDO_DLGHORZ, &r0_90 , N_(" Max Track Grade"), 0, (void*)(CHANGE_MAIN) }
- };
-
-
-static paramGroup_t layoutPG = { "layout", PGO_RECORD|PGO_PREFMISC, layoutPLs, sizeof layoutPLs/sizeof layoutPLs[0] };
-
-static void LayoutDlgUpdate( paramGroup_p pg, int inx, void * valueP );
-
-
-static void LayoutOk( void * junk )
-{
- long changes;
- char prefString[ 30 ];
-
- changes = GetChanges( &layoutPG );
-
- /* [mf Nov. 15, 2005] Get the gauge/scale settings */
- if (changes & CHANGE_SCALE) {
- SetScaleGauge( curScaleDescInx, curGaugeInx );
- }
- /* [mf Nov. 15, 2005] end */
-
- if (changes & CHANGE_MAP) {
- SetRoomSize( newSize );
- }
-
- wHide( layoutW );
- DoChangeNotification(changes);
-
- if( changes & CHANGE_LIMITS ) {
- // now set the minimum track radius
- sprintf( prefString, "minTrackRadius-%s", curScaleName );
- wPrefSetFloat( "misc", prefString, minTrackRadius );
- }
-}
-
-
-static void LayoutChange( long changes )
-{
- if (changes & (CHANGE_SCALE|CHANGE_UNITS))
- if (layoutW != NULL && wWinIsVisible(layoutW) )
- ParamLoadControls( &layoutPG );
-}
-
-
-static void DoLayout( void * junk )
-{
- newSize = mapD.size;
- if (layoutW == NULL) {
- layoutW = ParamCreateDialog( &layoutPG, MakeWindowTitle(_("Layout Options")), _("Ok"), LayoutOk, wHide, TRUE, NULL, 0, LayoutDlgUpdate );
- LoadScaleList( (wList_p)layoutPLs[4].control );
- }
- LoadGaugeList( (wList_p)layoutPLs[5].control, curScaleDescInx ); /* set correct gauge list here */
- ParamLoadControls( &layoutPG );
- wShow( layoutW );
-}
-
-
-
-EXPORT addButtonCallBack_t LayoutInit( void )
-{
- ParamRegister( &layoutPG );
- RegisterChangeNotification( LayoutChange );
- return &DoLayout;
-}
-
-/* [mf Nov. 15, 2005] Catch changes done in the LayoutDialog */
-static void
-LayoutDlgUpdate(
- paramGroup_p pg,
- int inx,
- void * valueP )
-{
- char prefString[ 100 ];
- char scaleDesc[ 100 ];
-
- /* did the scale change ? */
- if( inx == 4 ) {
- LoadGaugeList( (wList_p)layoutPLs[5].control, *((int *)valueP) );
- // set the first entry as default, usually the standard gauge for a scale
- wListSetIndex( (wList_p)layoutPLs[5].control, 0 );
-
- // get the minimum radius
- // get the selected scale first
- wListGetValues((wList_p)layoutPLs[4].control, scaleDesc, 99, NULL, NULL );
- // split of the name from the scale
- strtok( scaleDesc, " " );
-
- // now get the minimum track radius
- sprintf( prefString, "minTrackRadius-%s", scaleDesc );
- wPrefGetFloat( "misc", prefString, &minTrackRadius, 0.0 );
-
- // put the scale's minimum value into the dialog
- wStringSetValue( (wString_p)layoutPLs[6].control, FormatDistance( minTrackRadius ) );
- }
-}
-
-/* [mf Nov. 15, 2005] end */
/****************************************************************************
*
@@ -213,6 +107,7 @@ static wWin_p displayW;
static char * autoPanLabels[] = { N_("Auto Pan"), NULL };
static char * drawTunnelLabels[] = { N_("Hide"), N_("Dash"), N_("Normal"), NULL };
static char * drawEndPtLabels3[] = { N_("None"), N_("Turnouts"), N_("All"), NULL };
+static char * drawEndPtUnconnectedSize[] = { N_("Normal"), N_("Thick"), N_("Exception"), NULL };
static char * tiedrawLabels[] = { N_("None"), N_("Outline"), N_("Solid"), NULL };
static char * drawCenterCircle[] = { N_("Off"), N_("On"), NULL };
static char * labelEnableLabels[] = { N_("Track Descriptions"), N_("Lengths"), N_("EndPt Elevations"), N_("Track Elevations"), N_("Cars"), NULL };
@@ -221,6 +116,7 @@ static char * listLabelsLabels[] = { N_("Manuf"), N_("Part No"), N_("Descr"), NU
static char * colorLayersLabels[] = { N_("Tracks"), N_("Other"), NULL };
static char * liveMapLabels[] = { N_("Live Map"), NULL };
static char * hideTrainsInTunnelsLabels[] = { N_("Hide Trains On Hidden Track"), NULL };
+static char * zoomCornerLabels[] = {N_("Zoom keeps lower corner in view"), NULL};
extern long trainPause;
@@ -228,10 +124,12 @@ static paramData_t displayPLs[] = {
{ PD_TOGGLE, &colorLayers, "color-layers", PDO_NOPSHUPD|PDO_DRAW, colorLayersLabels, N_("Color Layers"), BC_HORZ, (void*)(CHANGE_MAIN) },
{ PD_RADIO, &drawTunnel, "tunnels", PDO_NOPSHUPD|PDO_DRAW, drawTunnelLabels, N_("Draw Tunnel"), BC_HORZ, (void*)(CHANGE_MAIN) },
{ PD_RADIO, &drawEndPtV, "endpt", PDO_NOPSHUPD|PDO_DRAW, drawEndPtLabels3, N_("Draw EndPts"), BC_HORZ, (void*)(CHANGE_MAIN) },
+ { PD_RADIO, &drawUnconnectedEndPt, "unconnected-endpt", PDO_NOPSHUPD|PDO_DRAW, drawEndPtUnconnectedSize, N_("Draw Unconnected EndPts"), BC_HORZ, (void*)(CHANGE_MAIN) },
{ PD_RADIO, &tieDrawMode, "tiedraw", PDO_NOPSHUPD|PDO_DRAW, tiedrawLabels, N_("Draw Ties"), BC_HORZ, (void*)(CHANGE_MAIN) },
{ PD_RADIO, &centerDrawMode, "centerdraw", PDO_NOPSHUPD|PDO_DRAW, drawCenterCircle, N_("Draw Centers"), BC_HORZ, (void*)(CHANGE_MAIN | CHANGE_MAP) },
{ PD_LONG, &twoRailScale, "tworailscale", PDO_NOPSHUPD, &i1_64, N_("Two Rail Scale"), 0, (void*)(CHANGE_MAIN) },
{ PD_LONG, &mapScale, "mapscale", PDO_NOPSHUPD, &i1_256, N_("Map Scale"), 0, (void*)(CHANGE_MAP) },
+ { PD_TOGGLE, &zoomCorner, "zoom-corner", PDO_NOPSHUPD, zoomCornerLabels, "", BC_HORZ },
{ PD_TOGGLE, &liveMap, "livemap", PDO_NOPSHUPD, liveMapLabels, "", BC_HORZ },
{ PD_TOGGLE, &autoPan, "autoPan", PDO_NOPSHUPD, autoPanLabels, "", BC_HORZ },
{ PD_TOGGLE, &labelEnable, "labelenable", PDO_NOPSHUPD, labelEnableLabels, N_("Label Enable"), 0, (void*)(CHANGE_MAIN) },
@@ -241,7 +139,7 @@ static paramData_t displayPLs[] = {
{ PD_TOGGLE, &layoutLabels, "layoutlabels", PDO_NOPSHUPD, listLabelsLabels, N_("Layout Labels"), BC_HORZ, (void*)(CHANGE_MAIN) },
{ PD_TOGGLE, &listLabels, "listlabels", PDO_NOPSHUPD, listLabelsLabels, N_("List Labels"), BC_HORZ, (void*)(CHANGE_PARAMS) },
/* ATTENTION: update the define below if you add entries above */
-#define I_HOTBARLABELS (15)
+#define I_HOTBARLABELS (17)
{ PD_DROPLIST, &carHotbarModeInx, "carhotbarlabels", PDO_NOPSHUPD|PDO_DLGUNDERCMDBUTT|PDO_LISTINDEX, (void*)250, N_("Car Labels"), 0, (void*)CHANGE_SCALE },
{ PD_LONG, &trainPause, "trainpause", PDO_NOPSHUPD, &i10_1000 , N_("Train Update Delay"), 0, 0 },
{ PD_TOGGLE, &hideTrainsInTunnels, "hideTrainsInTunnels", PDO_NOPSHUPD, hideTrainsInTunnelsLabels, "", BC_HORZ }
@@ -312,7 +210,7 @@ static char * moveQlabels[] = {
N_("End-Points"),
NULL };
-static char * preSelectLabels[] = { N_("Describe"), N_("Select"), NULL };
+static char * preSelectLabels[] = { N_("Properties"), N_("Select"), NULL };
#ifdef HIDESELECTIONWINDOW
static char * hideSelectionWindowLabels[] = { N_("Hide"), NULL };
@@ -374,7 +272,6 @@ EXPORT addButtonCallBack_t CmdoptInit( void )
static wWin_p prefW;
static long displayUnits;
-static wIndex_t distanceFormatInx;
static char * unitsLabels[] = { N_("English"), N_("Metric"), NULL };
static char * angleSystemLabels[] = { N_("Polar"), N_("Cartesian"), NULL };
static char * enableBalloonHelpLabels[] = { N_("Balloon Help"), NULL };
@@ -384,14 +281,14 @@ static paramData_t prefPLs[] = {
{ PD_RADIO, &angleSystem, "anglesystem", PDO_NOPSHUPD, angleSystemLabels, N_("Angles"), BC_HORZ },
{ PD_RADIO, &units, "units", PDO_NOPSHUPD|PDO_NOUPDACT, unitsLabels, N_("Units"), BC_HORZ, (void*)(CHANGE_MAIN|CHANGE_UNITS) },
#define I_DSTFMT (2)
- { PD_DROPLIST, &distanceFormatInx, "dstfmt", PDO_NOPSHUPD|PDO_LISTINDEX, (void*)150, N_("Length Format"), 0, (void*)(CHANGE_MAIN|CHANGE_UNITS) },
+ { PD_DROPLIST, &distanceFormatInx, "dstfmt", PDO_DIM|PDO_NOPSHUPD|PDO_LISTINDEX, (void*)150, N_("Length Format"), 0, (void*)(CHANGE_MAIN|CHANGE_UNITS) },
{ PD_FLOAT, &minLength, "minlength", PDO_DIM|PDO_SMALLDIM|PDO_NOPSHUPD, &r0o1_1, N_("Min Track Length") },
{ PD_FLOAT, &connectDistance, "connectdistance", PDO_DIM|PDO_SMALLDIM|PDO_NOPSHUPD, &r0o1_1, N_("Connection Distance"), },
{ PD_FLOAT, &connectAngle, "connectangle", PDO_NOPSHUPD, &r1_10, N_("Connection Angle") },
{ PD_FLOAT, &turntableAngle, "turntable-angle", PDO_NOPSHUPD, &r0_180, N_("Turntable Angle") },
{ PD_LONG, &maxCouplingSpeed, "coupling-speed-max", PDO_NOPSHUPD, &i10_100, N_("Max Coupling Speed"), 0 },
{ PD_TOGGLE, &enableBalloonHelp, "balloonhelp", PDO_NOPSHUPD, enableBalloonHelpLabels, "", BC_HORZ },
- { PD_LONG, &dragPixels, "dragpixels", PDO_NOPSHUPD|PDO_DRAW, &r1_1000, N_("Drag Distance") },
+ { PD_LONG, &dragPixels, "dragpixels", PDO_NOPSHUPD|PDO_DRAW, &i1_1000, N_("Drag Distance") },
{ PD_LONG, &dragTimeout, "dragtimeout", PDO_NOPSHUPD|PDO_DRAW, &i1_1000, N_("Drag Timeout") },
{ PD_LONG, &minGridSpacing, "mingridspacing", PDO_NOPSHUPD|PDO_DRAW, &i1_100, N_("Min Grid Spacing"), 0, 0 },
{ PD_LONG, &checkPtInterval, "checkpoint", PDO_NOPSHUPD|PDO_FILE, &i0_10000, N_("Check Point") },
@@ -458,6 +355,10 @@ static void LoadDstFmtList( void )
wListAddValue( (wList_p)prefPLs[I_DSTFMT].control, _(dstFmts[units][inx].name), NULL, (void*)dstFmts[units][inx].fmt );
}
+/**
+* Handle changing of measurement system. The list of number formats is loaded
+* and the first entry is selected as default value.
+*/
static void UpdatePrefD( void )
{
@@ -467,41 +368,75 @@ static void UpdatePrefD( void )
if ( prefW==NULL || (!wWinIsVisible(prefW)) || prefPLs[1].control==NULL )
return;
newUnits = wRadioGetValue( (wChoice_p)prefPLs[1].control );
- if ( newUnits == displayUnits )
- return;
- oldUnits = units;
- units = newUnits;
- for ( inx = 0; inx<sizeof prefPLs/sizeof prefPLs[0]; inx++ ) {
- if ( (prefPLs[inx].option&PDO_DIM) ) {
- ParamLoadControl( &prefPG, inx );
+ if (newUnits != displayUnits) {
+ oldUnits = units;
+ units = newUnits;
+ LoadDstFmtList();
+ distanceFormatInx = 0;
+
+ for (inx = 0; inx < sizeof prefPLs / sizeof prefPLs[0]; inx++) {
+ if ((prefPLs[inx].option&PDO_DIM)) {
+ ParamLoadControl(&prefPG, inx);
+ }
}
+
+ units = oldUnits;
+ displayUnits = newUnits;
}
- LoadDstFmtList();
- units = oldUnits;
- displayUnits = newUnits;
+ return;
}
+/**
+ * Handle changes of the measurement format.
+ */
+
+static void UpdateMeasureFmt()
+{
+ int inx;
+
+ distanceFormatInx = wListGetIndex((wList_p)prefPLs[I_DSTFMT].control);
+ units = wRadioGetValue((wChoice_p)prefPLs[1].control);
+
+ for (inx = 0; inx < sizeof prefPLs / sizeof prefPLs[0]; inx++) {
+ if ((prefPLs[inx].option&PDO_DIM)) {
+ ParamLoadControl(&prefPG, inx);
+ }
+ }
+}
static void PrefOk( void * junk )
{
- wBool_t resetValues = FALSE;
+ wBool_t resetValuesLow = FALSE, resetValuesHigh = FALSE;
long changes;
changes = GetChanges( &prefPG );
if (connectAngle < 1.0) {
connectAngle = 1.0;
- resetValues = TRUE;
+ resetValuesLow = TRUE;
+ } else if (connectAngle > 10.0) {
+ connectAngle = 10.0;
+ resetValuesHigh = TRUE;
}
if (connectDistance < 0.1) {
connectDistance = 0.1;
- resetValues = TRUE;
+ resetValuesLow = TRUE;
+ } else if (connectDistance > 1.0) {
+ connectDistance = 1.0;
+ resetValuesHigh = TRUE;
}
if (minLength < 0.1) {
minLength = 0.1;
- resetValues = TRUE;
+ resetValuesLow = TRUE;
+ } else if (minLength > 1.0) {
+ minLength = 1.0;
+ resetValuesHigh = TRUE;
}
- if ( resetValues ) {
+ if ( resetValuesLow ) {
NoticeMessage2( 0, MSG_CONN_PARAMS_TOO_SMALL, _("Ok"), NULL ) ;
}
+ if ( resetValuesHigh ) {
+ NoticeMessage2( 0, MSG_CONN_PARAMS_TOO_BIG, _("Ok"), NULL ) ;
+ }
+
wHide( prefW );
DoChangeNotification(changes);
diff --git a/app/bin/dpricels.c b/app/bin/dpricels.c
index 7e17121..87df88b 100644
--- a/app/bin/dpricels.c
+++ b/app/bin/dpricels.c
@@ -1,5 +1,5 @@
-/*
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/dpricels.c,v 1.2 2008-01-20 23:29:15 mni77 Exp $
+/** \file dpricels.c
+ * Price List Dialog
*/
/* XTrkCad - Model Railroad CAD
@@ -19,16 +19,15 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
+#include <string.h>
-#include "track.h"
#include "compound.h"
+#include "custom.h"
#include "i18n.h"
-
-/*****************************************************************************
- *
- * Price List Dialog
- *
- */
+#include "layout.h"
+#include "messages.h"
+#include "param.h"
+#include "track.h"
static wWin_p priceListW;
@@ -115,8 +114,8 @@ static void PriceListChange( long changes )
priceListW == NULL || !wWinIsVisible( priceListW ) )
return;
wListClear( priceListSelL );
- to1 = TurnoutAdd( listLabels|LABEL_COST, curScaleInx, priceListSelL, NULL, -1 );
- to2 = StructAdd( listLabels|LABEL_COST, curScaleInx, priceListSelL, NULL );
+ to1 = TurnoutAdd( listLabels|LABEL_COST, GetLayoutCurScale(), priceListSelL, NULL, -1 );
+ to2 = StructAdd( listLabels|LABEL_COST, GetLayoutCurScale(), priceListSelL, NULL );
if (to1 == NULL)
to1 = to2;
priceListCurrent = NULL;
diff --git a/app/bin/dprmfile.c b/app/bin/dprmfile.c
index e9cfc80..24250e7 100644
--- a/app/bin/dprmfile.c
+++ b/app/bin/dprmfile.c
@@ -20,19 +20,18 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-#include <time.h>
-#include "track.h"
-#include "i18n.h"
-
+#include <assert.h>
#include <stdint.h>
+#include <string.h>
+#include <time.h>
-#define PARAM_SUBDIR FILE_SEP_CHAR "params"
-
-/****************************************************************************
- *
- * Param File Management
- *
- */
+#include "custom.h"
+#include "fileio.h"
+#include "i18n.h"
+#include "messages.h"
+#include "param.h"
+#include "paths.h"
+#include "track.h"
typedef struct {
char * name;
@@ -83,8 +82,9 @@ static BOOL_T UpdateParamFiles( void )
long updateTime;
long lastTime;
- sprintf( message, "%s%sxtrkcad.upd", libDir, FILE_SEP_CHAR );
- updateF = fopen( message, "r" );
+ MakeFullpath(&fileNameP, libDir, "xtrkcad.upd", NULL);
+ updateF = fopen( fileNameP, "r" );
+ free(fileNameP);
if ( updateF == NULL )
return FALSE;
if ( fgets( message, sizeof message, updateF ) == NULL ) {
@@ -95,14 +95,15 @@ static BOOL_T UpdateParamFiles( void )
updateTime = atol( message );
if ( lastTime >= updateTime )
return FALSE;
- sprintf( fileName, "%s%sparams%s", libDir, FILE_SEP_CHAR, FILE_SEP_CHAR );
- fileNameP = fileName+strlen(fileName);
- while ( ( fgets( fileNameP, (fileName+sizeof fileName)-fileNameP, updateF ) ) != NULL ) {
- Stripcr( fileNameP );
- InfoMessage( _("Updating %s"), fileNameP );
- paramF = fopen( fileName, "r" );
+
+ while ( ( fgets( fileName, STR_LONG_SIZE, updateF ) ) != NULL ) {
+ Stripcr( fileName );
+ InfoMessage( _("Updating %s"), fileName );
+ MakeFullpath(&fileNameP, libDir, "params", fileName, NULL);
+ paramF = fopen( fileNameP, "r" );
if ( paramF == NULL ) {
- NoticeMessage( MSG_PRMFIL_OPEN_NEW, _("Ok"), NULL, fileName );
+ NoticeMessage( MSG_PRMFIL_OPEN_NEW, _("Ok"), NULL, fileNameP );
+ free(fileNameP);
continue;
}
contents = NULL;
@@ -115,25 +116,29 @@ static BOOL_T UpdateParamFiles( void )
}
fclose( paramF );
if (contents == NULL) {
- NoticeMessage( MSG_PRMFIL_NO_CONTENTS, _("Ok"), NULL, fileName );
+ NoticeMessage( MSG_PRMFIL_NO_CONTENTS, _("Ok"), NULL, fileNameP );
+ free(fileNameP);
continue;
}
cp = wPrefGetString( "Parameter File Map", contents );
- wPrefSetString( "Parameter File Map", contents, fileName );
+ wPrefSetString( "Parameter File Map", contents, fileNameP );
if (cp!=NULL && *cp!='\0') {
/* been there, done that */
+ free(fileNameP);
continue;
}
DYNARR_APPEND( paramFileInfo_t, paramFileInfo_da, 10 );
curParamFileIndex = paramFileInfo_da.cnt-1;
- paramFileInfo(curParamFileIndex).name = MyStrdup( fileName );
+ paramFileInfo(curParamFileIndex).name = MyStrdup( fileNameP );
curContents = curSubContents = NULL;
paramFileInfo(curParamFileIndex).deleted = FALSE;
paramFileInfo(curParamFileIndex).valid = TRUE;
paramFileInfo(curParamFileIndex).deletedShadow =
- paramFileInfo(curParamFileIndex).deleted = !ReadParams( 0, NULL, fileName );
+ paramFileInfo(curParamFileIndex).deleted = !ReadParams( 0, NULL, fileNameP );
paramFileInfo(curParamFileIndex).contents = curContents;
+
+ free(fileNameP);
}
wPrefSetInteger( "file", "updatetime", updateTime );
return TRUE;
@@ -194,6 +199,7 @@ EXPORT void RememberParamFiles( void )
*cp = ' ';
}
wPrefSetString( "Parameter File Names", message, contents );
+ wPrefSetString("Parameter File Map", contents, paramFileInfo(fileInx).name);
}
}
sprintf( message, "File%d", fileNo++ );
@@ -278,8 +284,6 @@ EXPORT int LoadParamFile(
char ** fileName,
void * data )
{
- char * cp;
- char *name;
wIndex_t inx;
int i = 0;
@@ -354,8 +358,6 @@ static void UpdateParamFileButton(
wIndex_t selcnt = wListGetSelectedCount( paramFileL );
wIndex_t inx, cnt;
- void * data;
-
// set the default
wButtonSetLabel( paramFileActionB, _("Unload"));
paramFilePLs[ I_PRMFILACTION ].context = FALSE;
@@ -400,7 +402,6 @@ static void ParamFileAction( void * action )
wIndex_t selcnt = wListGetSelectedCount( paramFileL );
wIndex_t inx, cnt;
wIndex_t fileInx;
- void * data;
unsigned newDeletedState;
if( action )
@@ -522,8 +523,10 @@ static void DoParamFiles( void * junk )
strcpy( curParamDir, dir );
else {
// in case there is no preference setting, use the installation's param directory as default
- strcpy( curParamDir, libDir );
- strcat( curParamDir, PARAM_SUBDIR );
+ char *str;
+ MakeFullpath(&str, libDir, PARAM_SUBDIR, NULL);
+ strcpy( curParamDir, str );
+ free(str);
}
mtbox_bm = wIconCreateBitMap( mtbox_width, mtbox_height, mtbox_bits, drawColorBlack );
chkbox_bm = wIconCreateBitMap( chkbox_width, chkbox_height, chkbox_bits, drawColorBlack );
diff --git a/app/bin/draw.c b/app/bin/draw.c
index 92814e0..3f25830 100644
--- a/app/bin/draw.c
+++ b/app/bin/draw.c
@@ -1,7 +1,5 @@
/** \file draw.c
* Basic drawing functions.
- *
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/draw.c,v 1.17 2009-12-12 17:20:59 m_fischer Exp $
*/
/* XTrkCad - Model Railroad CAD
@@ -24,6 +22,8 @@
#include <stdlib.h>
#include <stdio.h>
+#include <string.h>
+
#ifdef HAVE_MALLOC_C
#include <malloc.h>
#endif
@@ -40,12 +40,16 @@
#include <sys/timeb.h>
#endif
-#include "track.h"
-#include "utility.h"
-#include "misc.h"
+#include "cselect.h"
+#include "custom.h"
#include "draw.h"
-#include "i18n.h"
#include "fileio.h"
+#include "i18n.h"
+#include "messages.h"
+#include "misc.h"
+#include "param.h"
+#include "track.h"
+#include "utility.h"
static void DrawRoomWalls( wBool_t );
EXPORT void DrawMarkers( void );
@@ -57,6 +61,8 @@ static int log_mouse = 0;
static wFontSize_t drawMaxTextFontSize = 100;
+extern long zoomCorner;
+
/****************************************************************************
*
* EXPORTED VARIABLES
@@ -67,6 +73,7 @@ static wFontSize_t drawMaxTextFontSize = 100;
#define INIT_MAP_SCALE (64.0)
#define MAX_MAIN_SCALE (256.0)
#define MIN_MAIN_SCALE (1.0)
+#define MIN_MAIN_MACRO (0.10)
// static char FAR message[STR_LONG_SIZE];
@@ -94,6 +101,7 @@ EXPORT DIST_T pixelBins = 80;
*/
static wPos_t infoHeight;
+static wPos_t textHeight;
EXPORT wWin_p mapW;
EXPORT BOOL_T mapVisible;
@@ -111,11 +119,11 @@ EXPORT wDrawColor exceptionColor;
static wFont_p rulerFp;
static struct {
- wMessage_p scale_m;
- wMessage_p count_m;
- wMessage_p posX_m;
- wMessage_p posY_m;
- wMessage_p info_m;
+ wStatus_p scale_m;
+ wStatus_p count_m;
+ wStatus_p posX_m;
+ wStatus_p posY_m;
+ wStatus_p info_m;
wPos_t scale_w;
wPos_t count_w;
wPos_t pos_w;
@@ -462,33 +470,55 @@ EXPORT void DrawMultiString(
coOrd * hi)
{
char * cp;
+ char * cp1;
POS_T lineH, lineW;
- coOrd size, textsize;
+ coOrd size, textsize, posl, orig;
POS_T descent;
+ char *line;
- DrawTextSize2( &mainD, "Aqjlp", fp, fs, TRUE, &textsize, &descent );
- lineH = textsize.y+descent;
+ if (!text || !*text) {
+ return; //No string or blank
+ }
+ line = malloc(strlen(text) + 1);
+
+ DrawTextSize2( &mainD, "Aqjlp", fp, fs, TRUE, &textsize, &descent);
+ POS_T ascent = textsize.y-descent;
+ lineH = ascent+descent*1.5;
size.x = 0.0;
size.y = 0.0;
- while (1) {
- cp = message;
+ orig.x = pos.x;
+ orig.y = pos.y;
+ cp = line; // Build up message to hold all of the strings separated by nulls
+ while (*text) {
+ cp1 = cp;
while (*text != '\0' && *text != '\n')
*cp++ = *text++;
*cp = '\0';
- DrawTextSize2( &mainD, message, fp, fs, TRUE, &textsize, &descent );
+ DrawTextSize2( &mainD, cp1, fp, fs, TRUE, &textsize, &descent);
lineW = textsize.x;
if (lineW>size.x)
size.x = lineW;
- DrawString( d, pos, 0.0, message, fp, fs, color );
+ posl.x = pos.x;
+ posl.y = pos.y;
+ Rotate( &posl, orig, a);
+ DrawString( d, posl, a, cp1, fp, fs, color );
pos.y -= lineH;
size.y += lineH;
- if (*text)
+ if (*text == '\0')
break;
text++;
+ cp++;
+ }
+ if (lo) {
+ lo->x = posl.x;
+ lo->y = posl.y-descent;
+ }
+ if (hi) {
+ hi->x = posl.x+size.x;
+ hi->y = posl.y+ascent;
}
- *lo = pos;
- hi->x = pos.x;
- hi->y = pos.y+size.y;
+
+ free(line);
}
@@ -613,6 +643,52 @@ EXPORT void DrawTextSize(
DrawTextSize2( dp, text, fp, fs, relative, size, &descent );
}
+EXPORT void DrawMultiLineTextSize(
+ drawCmd_p dp,
+ char * text,
+ wFont_p fp,
+ wFontSize_t fs,
+ BOOL_T relative,
+ coOrd * size,
+ coOrd * lastline )
+{
+ POS_T descent, lineW, lineH;
+ coOrd textsize, blocksize;
+
+ char *cp;
+ char *line = malloc(strlen(text) + 1);
+
+ DrawTextSize2( &mainD, "Aqlip", fp, fs, TRUE, &textsize, &descent);
+ POS_T ascent = textsize.y-descent;
+ lineH = ascent+descent*1.5;
+ blocksize.x = 0;
+ blocksize.y = 0;
+ lastline->x = 0;
+ lastline->y = 0;
+ while (text && *text != '\0' ) {
+ cp = line;
+ while (*text != '\0' && *text != '\n')
+ *cp++ = *text++;
+ *cp = '\0';
+ blocksize.y += lineH;
+ DrawTextSize2( &mainD, line, fp, fs, TRUE, &textsize, &descent);
+ lineW = textsize.x;
+ if (lineW>blocksize.x)
+ blocksize.x = lineW;
+ lastline->x = textsize.x;
+ if (*text =='\n') {
+ lastline->y -= lineH;
+ lastline->x = 0;
+ }
+ if (*text == '\0')
+ break;
+ text++;
+ }
+ size->x = blocksize.x;
+ size->y = blocksize.y;
+ free(line);
+}
+
static void DDrawBitMap( drawCmd_p d, coOrd p, wDrawBitMap_p bm, wDrawColor color)
{
@@ -689,7 +765,7 @@ static void TempSegString(
tempSegs(tempSegs_da.cnt-1).u.t.angle = a;
tempSegs(tempSegs_da.cnt-1).u.t.fontP = fp;
tempSegs(tempSegs_da.cnt-1).u.t.fontSize = fontSize;
- tempSegs(tempSegs_da.cnt-1).u.t.string = s;
+ tempSegs(tempSegs_da.cnt-1).u.t.string = MyStrdup(s);
}
@@ -792,6 +868,8 @@ static wPos_t info_yb_offset = 2;
static wPos_t info_ym_offset = 3;
static wPos_t six = 2;
static wPos_t info_xm_offset = 2;
+static wPos_t messageOrControlX = 0;
+static wPos_t messageOrControlY = 0;
#define NUM_INFOCTL (4)
static wControl_p curInfoControl[NUM_INFOCTL];
static wPos_t curInfoLabelWidth[NUM_INFOCTL];
@@ -827,9 +905,9 @@ static wPos_t GetInfoPosWidth( void )
dist = 9.0*12.0+11.0+3.0/4.0-1.0/64.0;
}
- labelWidth = (wLabelWidth( xLabel ) > wLabelWidth( yLabel ) ? wLabelWidth( xLabel ):wLabelWidth( yLabel ));
+ labelWidth = (wStatusGetWidth( xLabel ) > wStatusGetWidth( yLabel ) ? wStatusGetWidth( xLabel ):wStatusGetWidth( yLabel ));
- return wLabelWidth( FormatDistance(dist) ) + labelWidth;
+ return wStatusGetWidth( FormatDistance(dist) ) + labelWidth;
}
/**
@@ -841,35 +919,43 @@ EXPORT void InitInfoBar( void )
{
wPos_t width, height, y, yb, ym, x, boxH;
wWinGetSize( mainW, &width, &height );
- infoHeight = 3 + wMessageGetHeight( 0L ) + 3;
- y = height - infoHeight;
+ infoHeight = 3 + wStatusGetHeight( COMBOBOX ) + 3;
+ textHeight = wStatusGetHeight(0L);
+ y = height - max(infoHeight,textHeight)-10;
+
+#ifdef WINDOWS
y -= 19; /* Kludge for MSW */
- infoD.pos_w = GetInfoPosWidth() + 2;
- infoD.scale_w = wLabelWidth( "999:1" ) + wLabelWidth( zoomLabel ) + 6;
- /* we do not use the count label for the moment */
- infoD.count_w = 0;
- infoD.info_w = width - infoD.pos_w*2 - infoD.scale_w - infoD.count_w - 45;
+#endif
+
+ infoD.pos_w = GetInfoPosWidth() + 2;
+ infoD.scale_w = wStatusGetWidth( "999:1" ) + wStatusGetWidth( zoomLabel ) + 6;
+ /* we do not use the count label for the moment */
+ infoD.count_w = 0;
+ infoD.info_w = width - 20 - infoD.pos_w*2 - infoD.scale_w - infoD.count_w - 45; // Allow Window to resize down
if (infoD.info_w <= 0) {
infoD.info_w = 10;
}
yb = y+info_yb_offset;
- ym = y+info_ym_offset;
- boxH = infoHeight-5;
- x = 0;
+ ym = y+(infoHeight-textHeight)/2;
+ boxH = infoHeight;
+ x = 2;
infoD.scale_b = wBoxCreate( mainW, x, yb, NULL, wBoxBelow, infoD.scale_w, boxH );
- infoD.scale_m = wMessageCreate( mainW, x+info_xm_offset, ym, "infoBarScale", infoD.scale_w-six, zoomLabel );
+ infoD.scale_m = wStatusCreate( mainW, x+info_xm_offset, ym, "infoBarScale", infoD.scale_w-six, zoomLabel);
x += infoD.scale_w + 10;
infoD.posX_b = wBoxCreate( mainW, x, yb, NULL, wBoxBelow, infoD.pos_w, boxH );
- infoD.posX_m = wMessageCreate( mainW, x+info_xm_offset, ym, "infoBarPosX", infoD.pos_w-six, xLabel );
+ infoD.posX_m = wStatusCreate( mainW, x+info_xm_offset, ym, "infoBarPosX", infoD.pos_w-six, xLabel );
x += infoD.pos_w + 5;
infoD.posY_b = wBoxCreate( mainW, x, yb, NULL, wBoxBelow, infoD.pos_w, boxH );
- infoD.posY_m = wMessageCreate( mainW, x+info_xm_offset, ym, "infoBarPosY", infoD.pos_w-six, yLabel );
+ infoD.posY_m = wStatusCreate( mainW, x+info_xm_offset, ym, "infoBarPosY", infoD.pos_w-six, yLabel );
x += infoD.pos_w + 10;
+ messageOrControlX = x+info_xm_offset; //Remember Position
+ messageOrControlY = ym;
infoD.info_b = wBoxCreate( mainW, x, yb, NULL, wBoxBelow, infoD.info_w, boxH );
- infoD.info_m = wMessageCreate( mainW, x+info_xm_offset, ym, "infoBarStatus", infoD.info_w-six, "" );
+ infoD.info_m = wStatusCreate( mainW, x+info_xm_offset, ym, "infoBarStatus", infoD.info_w-six, "" );
}
+
static void SetInfoBar( void )
{
wPos_t width, height, y, yb, ym, x, boxH;
@@ -877,23 +963,23 @@ static void SetInfoBar( void )
static long oldDistanceFormat = -1;
long newDistanceFormat;
wWinGetSize( mainW, &width, &height );
- y = height - infoHeight;
+ y = height - max(infoHeight,textHeight)-10;
newDistanceFormat = GetDistanceFormat();
if ( newDistanceFormat != oldDistanceFormat ) {
infoD.pos_w = GetInfoPosWidth() + 2;
wBoxSetSize( infoD.posX_b, infoD.pos_w, infoHeight-5 );
- wMessageSetWidth( infoD.posX_m, infoD.pos_w-six );
+ wStatusSetWidth( infoD.posX_m, infoD.pos_w-six );
wBoxSetSize( infoD.posY_b, infoD.pos_w, infoHeight-5 );
- wMessageSetWidth( infoD.posY_m, infoD.pos_w-six );
+ wStatusSetWidth( infoD.posY_m, infoD.pos_w-six );
}
- infoD.info_w = width - infoD.pos_w*2 - infoD.scale_w - infoD.count_w - 40 + 4;
+ infoD.info_w = width - 20 - infoD.pos_w*2 - infoD.scale_w - infoD.count_w - 40 + 4;
if (infoD.info_w <= 0) {
infoD.info_w = 10;
}
yb = y+info_yb_offset;
- ym = y+info_ym_offset;
- boxH = infoHeight-5;
- wWinClear( mainW, 0, y, width, infoHeight );
+ ym = y+(infoHeight-textHeight)/2;
+ boxH = infoHeight;
+ wWinClear( mainW, 0, y, width-20, infoHeight );
x = 0;
wControlSetPos( (wControl_p)infoD.scale_b, x, yb );
wControlSetPos( (wControl_p)infoD.scale_m, x+info_xm_offset, ym );
@@ -907,18 +993,18 @@ static void SetInfoBar( void )
wControlSetPos( (wControl_p)infoD.info_b, x, yb );
wControlSetPos( (wControl_p)infoD.info_m, x+info_xm_offset, ym );
wBoxSetSize( infoD.info_b, infoD.info_w, boxH );
- wMessageSetWidth( infoD.info_m, infoD.info_w-six );
+ wStatusSetWidth( infoD.info_m, infoD.info_w-six );
+ messageOrControlX = x+info_xm_offset;
+ messageOrControlY = ym;
if (curInfoControl[0]) {
- x = wControlGetPosX( (wControl_p)infoD.info_m );
-#ifndef WINDOWS
- yb -= 2;
-#endif
for ( inx=0; curInfoControl[inx]; inx++ ) {
x += curInfoLabelWidth[inx];
- wControlSetPos( curInfoControl[inx], x, yb );
+ int y_this = ym + (textHeight/2) - (wControlGetHeight( curInfoControl[inx] )/2);
+ wControlSetPos( curInfoControl[inx], x, y_this );
x += wControlGetWidth( curInfoControl[inx] )+3;
wControlShow( curInfoControl[inx], TRUE );
}
+ wControlSetPos( (wControl_p)infoD.info_m, x+info_xm_offset, ym ); //Move to end
}
}
@@ -929,7 +1015,7 @@ static void InfoScale( void )
sprintf( message, "%s%0.0f:1", zoomLabel, mainD.scale );
else
sprintf( message, "%s1:%0.0f", zoomLabel, floor(1/mainD.scale+0.5) );
- wMessageSetValue( infoD.scale_m, message );
+ wStatusSetValue( infoD.scale_m, message );
}
EXPORT void InfoCount( wIndex_t count )
@@ -942,38 +1028,13 @@ EXPORT void InfoCount( wIndex_t count )
EXPORT void InfoPos( coOrd pos )
{
-#ifdef LATER
- wPos_t ww, hh;
- DIST_T w, h;
-#endif
- wPos_t x, y;
-
+ DrawMarkers();
sprintf( message, "%s%s", xLabel, FormatDistance(pos.x) );
- wMessageSetValue( infoD.posX_m, message );
+ wStatusSetValue( infoD.posX_m, message );
sprintf( message, "%s%s", yLabel, FormatDistance(pos.y) );
- wMessageSetValue( infoD.posY_m, message );
-#ifdef LATER
- wDrawGetSize( mainD.d, &ww, &hh );
- w = (DIST_T)(ww/mainD.dpi);
- h = (DIST_T)(hh/mainD.dpi);
- /*wDrawClip( mainD.d, 0, 0, w, h );*/
-#endif
- mainD.CoOrd2Pix(&mainD,oldMarker,&x,&y);
- wDrawLine( mainD.d, 0, y, (wPos_t)(LBORDER), y,
- 0, wDrawLineSolid, markerColor, wDrawOptTemp );
- wDrawLine( mainD.d, x, 0, x, (wPos_t)(BBORDER),
- 0, wDrawLineSolid, markerColor, wDrawOptTemp );
-
- mainD.CoOrd2Pix(&mainD,pos,&x,&y);
- wDrawLine( mainD.d, 0, y, (wPos_t)(LBORDER), y,
- 0, wDrawLineSolid, markerColor, wDrawOptTemp );
- wDrawLine( mainD.d, x, 0, x, (wPos_t)(BBORDER),
- 0, wDrawLineSolid, markerColor, wDrawOptTemp );
-#ifdef LATER
- /*wDrawClip( mainD.d, LBORDER, BBORDER,
- w-(LBORDER+RBORDER), h-(BBORDER+TBORDER) );*/
-#endif
+ wStatusSetValue( infoD.posY_m, message );
oldMarker = pos;
+ DrawMarkers();
}
static wControl_p deferSubstituteControls[NUM_INFOCTL+1];
@@ -998,94 +1059,34 @@ EXPORT void InfoSubstituteControls(
memcpy( deferSubstituteLabels, labels, sizeof deferSubstituteLabels );
}
if ( inError || controls == NULL || controls[0]==NULL ) {
+ wControlSetPos( (wControl_p)infoD.info_m, messageOrControlX, messageOrControlY);
wControlShow( (wControl_p)infoD.info_m, TRUE );
return;
}
- x = wControlGetPosX( (wControl_p)infoD.info_m );
- y = wControlGetPosY( (wControl_p)infoD.info_m );
-#ifndef WINDOWS
- y -= 3;
-#endif
- wMessageSetValue( infoD.info_m, "" );
+ //x = wControlGetPosX( (wControl_p)infoD.info_m );
+ x = messageOrControlX;
+ y = messageOrControlY;
+ wStatusSetValue( infoD.info_m, "" );
wControlShow( (wControl_p)infoD.info_m, FALSE );
for ( inx=0; controls[inx]; inx++ ) {
curInfoLabelWidth[inx] = wLabelWidth(_(labels[inx]));
x += curInfoLabelWidth[inx];
- wControlSetPos( controls[inx], x, y );
+ int y_this = y + (textHeight/2) - (wControlGetHeight( controls[inx] )/2);
+ wControlSetPos( controls[inx], x, y_this );
x += wControlGetWidth( controls[inx] );
wControlSetLabel( controls[inx], _(labels[inx]) );
wControlShow( controls[inx], TRUE );
curInfoControl[inx] = controls[inx];
x += 3;
}
+ wControlSetPos( (wControl_p)infoD.info_m, x, y );
curInfoControl[inx] = NULL;
deferSubstituteControls[0] = NULL;
}
-
-#ifdef LATER
-EXPORT void InfoSubstituteControl(
- wControl_p control1,
- char * label1,
- wControl_p control2,
- char * label2 )
-{
- wControl_p controls[3];
- wPos_t widths[2];
-
- if (control1 == NULL) {
- InfoSubstituteControls( NULL, NULL );
- } else {
- controls[0] = control1;
- controls[1] = control2;
- controls[2] = NULL;
- widths[0] = wLabelWidth( label1 );
- if (label2)
- widths[1] = wLabelWidth( label2 );
- else
- widths[1] = 0;
- InfoSubstituteControls( controls, widths );
-#ifdef LATER
- if (curInfoControl[0]) {
- wControlShow( curInfoControl[0], FALSE );
- curInfoControl[0] = NULL;
- }
- if (curInfoControl[1]) {
- wControlShow( curInfoControl[1], FALSE );
- curInfoControl[1] = NULL;
- }
- wControlShow( (wControl_p)infoD.info_m, TRUE );
- } else {
- if (curInfoControl[0])
- wControlShow( curInfoControl[0], FALSE );
- if (curInfoControl[1])
- wControlShow( curInfoControl[1], FALSE );
- x = wControlGetPosX( (wControl_p)infoD.info_m );
- y = wControlGetPosY( (wControl_p)infoD.info_m );
- curInfoLabelWidth[0] = wLabelWidth( label1 );
- x += curInfoLabelWidth[0];
- wControlShow( (wControl_p)infoD.info_m, FALSE );
- wControlSetPos( control1, x, y );
- wControlShow( control1, TRUE );
- curInfoControl[0] = control1;
- curInfoControl[1] = NULL;
- if (control2 != NULL) {
- curInfoLabelWidth[1] = wLabelWidth( label2 );
- x = wControlBeside( curInfoControl[0] ) + 10;
- x += curInfoLabelWidth[1]+10;
- wControlSetPos( control2, x, y );
- wControlShow( control2, TRUE );
- curInfoControl[1] = control2;
- }
-#endif
- }
-}
-#endif
-
-
EXPORT void SetMessage( char * msg )
{
- wMessageSetValue( infoD.info_m, msg );
+ wStatusSetValue( infoD.info_m, msg );
}
@@ -1289,8 +1290,15 @@ lprintf("mainRedraw\n");
wDrawDelayUpdate( mainD.d, FALSE );
}
+/**
+ * The wlib event handler for the main window.
+ *
+ * \param win wlib window information
+ * \param e the wlib event
+ * \param data additional data (unused)
+ */
-EXPORT void MainProc( wWin_p win, winProcEvent e, void * data )
+void MainProc( wWin_p win, winProcEvent e, void * data )
{
wPos_t width, height;
switch( e ) {
@@ -1300,31 +1308,28 @@ EXPORT void MainProc( wWin_p win, winProcEvent e, void * data )
DrawMapBoundingBox( FALSE );
wWinGetSize( mainW, &width, &height );
LayoutToolBar();
- height -= (toolbarHeight+infoHeight);
+ height -= (toolbarHeight+max(infoHeight,textHeight)+10);
if (height >= 0) {
- wDrawSetSize( mainD.d, width, height );
+ wDrawSetSize( mainD.d, width-20, height );
wControlSetPos( (wControl_p)mainD.d, 0, toolbarHeight );
SetMainSize();
ConstraintOrig( &mainD.orig, mainD.size );
tempD.orig = mainD.orig;
SetInfoBar();
MainRedraw();
+ MapRedraw();
wPrefSetInteger( "draw", "mainwidth", width );
wPrefSetInteger( "draw", "mainheight", height );
- }
- DrawMapBoundingBox( TRUE );
+ } else DrawMapBoundingBox( TRUE );
+ break;
+ case wState_e:
+ wPrefSetInteger( "draw", "maximized", wWinIsMaximized(win) );
break;
case wQuit_e:
- if (changed &&
- NoticeMessage( MSG_SAVE_CHANGES, _("Save"), _("Quit")))
- DoSave(NULL);
-
- CleanupFiles();
- SaveState();
CleanupCustom();
break;
case wClose_e:
- /* shutdown the application */
+ /* shutdown the application via "close window" button */
DoQuit();
break;
default:
@@ -1380,9 +1385,7 @@ static void DrawRoomWalls( wBool_t t )
if (mainD.d == NULL)
return;
-#ifdef LATER
- wDrawGetDim( mainD.d, &w, &h );
-#endif
+
DrawTicks( &mainD, mapD.size );
p01.x = p10.y = 0.0;
@@ -1390,10 +1393,7 @@ static void DrawRoomWalls( wBool_t t )
p01.y = p11.y = mapD.size.y;
DrawLine( &mainD, p01, p11, 3, t?borderColor:wDrawColorWhite );
DrawLine( &mainD, p11, p10, 3, t?borderColor:wDrawColorWhite );
-#ifdef LATER
- /*wDrawClip( mainD.d, LBORDER, BBORDER,
- w-(LBORDER+RBORDER), h-(BBORDER+TBORDER) );*/
-#endif
+
}
@@ -1591,6 +1591,7 @@ EXPORT void DrawTicks( drawCmd_p d, coOrd size )
DIST_T offset;
offset = 0.0;
+
if ( d->orig.x<0.0 )
offset = d->orig.x;
p0.x = 0.0/*d->orig.x*/; p1.x = size.x;
@@ -1599,6 +1600,7 @@ EXPORT void DrawTicks( drawCmd_p d, coOrd size )
p0.y = p1.y = min(d->orig.y + d->size.y, size.y);
DrawRuler( d, p0, p1, offset, FALSE, TRUE, borderColor );
offset = 0.0;
+
if ( d->orig.y<0.0 )
offset = d->orig.y;
p0.y = 0.0/*d->orig.y*/; p1.y = max(size.y,0.0);
@@ -1630,6 +1632,7 @@ static void ConstraintOrig( coOrd * orig, coOrd size )
LOG( log_pan, 2, ( "ConstraintOrig [ %0.3f, %0.3f ] RoomSize(%0.3f %0.3f), WxH=%0.3fx%0.3f",
orig->x, orig->y, mapD.size.x, mapD.size.y,
size.x, size.y ) )
+
if (orig->x+size.x > mapD.size.x ) {
orig->x = mapD.size.x-size.x;
orig->x += (units==UNITS_ENGLISH?1.0:(1.0/2.54));
@@ -1639,17 +1642,26 @@ LOG( log_pan, 2, ( "ConstraintOrig [ %0.3f, %0.3f ] RoomSize(%0.3f %0.3f), WxH=%
if (orig->y+size.y > mapD.size.y ) {
orig->y = mapD.size.y-size.y;
orig->y += (units==UNITS_ENGLISH?1.0:1.0/2.54);
-
+
}
if (orig->y < 0)
orig->y = 0;
+
if (mainD.scale >= 1.0) {
if (units == UNITS_ENGLISH) {
- orig->x = floor(orig->x);
- orig->y = floor(orig->y);
+ orig->x = floor(orig->x*4)/4; //>1:1 = 1/4 inch
+ orig->y = floor(orig->y*4)/4;
} else {
- orig->x = floor(orig->x*2.54)/2.54;
- orig->y = floor(orig->y*2.54)/2.54;
+ orig->x = floor(orig->x*2.54*2)/(2.54*2); //>1:1 = 0.5 cm
+ orig->y = floor(orig->y*2.54*2)/(2.54*2);
+ }
+ } else {
+ if (units == UNITS_ENGLISH) {
+ orig->x = floor(orig->x*64)/64; //<1:1 = 1/64 inch
+ orig->y = floor(orig->y*64)/64;
+ } else {
+ orig->x = floor(orig->x*25.4*2)/(25.4*2); //>1:1 = 0.5 mm
+ orig->y = floor(orig->y*25.4*2)/(25.4*2);
}
}
orig->x = (long)(orig->x*pixelBins+0.5)/pixelBins;
@@ -1719,11 +1731,27 @@ static int ScaleInx( DIST_T scale )
for ( inx=0; inx<sizeof zoomList/sizeof zoomList[0]; inx++ ) {
if( scale == zoomList[inx].value ) {
return inx;
- }
+ }
}
return -1;
}
+/*
+ * Find Nearest Scale
+ */
+
+static int NearestScaleInx ( DIST_T scale, BOOL_T larger ) {
+ int inx;
+
+ for ( inx=0; inx<sizeof zoomList/sizeof zoomList[0]; inx++ ) {
+ if( scale == zoomList[inx].value ) {
+ return inx;
+ }
+ if (scale < zoomList[inx].value) return inx;
+ }
+ return inx-1;
+}
+
/**
* Set up for new drawing scale. After the scale was changed, eg. via zoom button, everything
* is set up for the new scale.
@@ -1739,10 +1767,6 @@ static void DoNewScale( DIST_T scale )
scale = MAX_MAIN_SCALE;
DrawHilight( &mapD, mainD.orig, mainD.size );
-#ifdef LATER
- center.x = mainD.orig.x + mainD.size.x/2.0;
- center.y = mainD.orig.y + mainD.size.y/2.0;
-#endif
tempD.scale = mainD.scale = scale;
mainD.dpi = wDrawGetDPI( mainD.d );
if ( mainD.dpi == 75 ) {
@@ -1754,9 +1778,14 @@ static void DoNewScale( DIST_T scale )
SetZoomRadio( scale );
InfoScale();
- SetMainSize();
- mainD.orig.x = mainCenter.x - mainD.size.x/2.0;
- mainD.orig.y = mainCenter.y - mainD.size.y/2.0;
+ SetMainSize();
+ if (zoomCorner) {
+ mainCenter.x = mainD.orig.x + mainD.size.x/2.0;
+ mainCenter.y = mainD.orig.y + mainD.size.y/2.0;
+ } else {
+ mainD.orig.x = mainCenter.x - mainD.size.x/2.0;
+ mainD.orig.y = mainCenter.y - mainD.size.y/2.0;
+ }
ConstraintOrig( &mainD.orig, mainD.size );
MainRedraw();
tempD.orig = mainD.orig;
@@ -1783,22 +1812,32 @@ EXPORT void DoZoomUp( void * mode )
long newScale;
int i;
- if ( mode != NULL || (MyGetKeyState()&WKEY_SHIFT) == 0 ) {
+ if ( mode != NULL || (MyGetKeyState()&WKEY_SHIFT) == 0) {
i = ScaleInx( mainD.scale );
+ if (i < 0) i = NearestScaleInx(mainD.scale, TRUE);
/*
* Zooming into macro mode happens when we are at scale 1:1.
* To jump into macro mode, the CTRL-key has to be pressed and held.
*/
if( mainD.scale != 1.0 || (mainD.scale == 1.0 && (MyGetKeyState()&WKEY_CTRL))) {
- if( i )
+ if( i ) {
+ if (mainD.scale <=1.0)
+ InfoMessage(_("Macro Zoom Mode"));
+ else
+ InfoMessage(_("Use Shift+PageDwn to jump to preset Zoom In"));
DoNewScale( zoomList[ i - 1 ].value );
+
+ } else InfoMessage("Min Macro Zoom");
+ } else {
+ InfoMessage(_("Scale 1:1 - Use Ctrl+PageDwn to go to Macro Zoom Mode"));
}
} else if ( (MyGetKeyState()&WKEY_CTRL) == 0 ) {
wPrefGetInteger( "misc", "zoomin", &newScale, 4 );
+ InfoMessage(_("Preset Zoom In Value selected. Shift+Ctrl+PageDwn to reset value"));
DoNewScale( newScale );
} else {
wPrefSetInteger( "misc", "zoomin", (long)mainD.scale );
- InfoMessage( _("Zoom In Program Value %ld:1"), (long)mainD.scale );
+ InfoMessage( _("Zoom In Program Value %ld:1, Shift+PageDwn to use"), (long)mainD.scale );
}
}
@@ -1816,15 +1855,21 @@ EXPORT void DoZoomDown( void * mode)
if ( mode != NULL || (MyGetKeyState()&WKEY_SHIFT) == 0 ) {
i = ScaleInx( mainD.scale );
- if( i>= 0 && i < ( sizeof zoomList/sizeof zoomList[0] - 1 ))
- DoNewScale( zoomList[ i + 1 ].value );
+ if (i < 0) i = NearestScaleInx(mainD.scale, TRUE);
+ if( i>= 0 && i < ( sizeof zoomList/sizeof zoomList[0] - 1 )) {
+ InfoMessage(_("Use Shift+PageUp to jump to preset Zoom Out"));
+ DoNewScale( zoomList[ i + 1 ].value );
+ } else
+ InfoMessage(_("At Maximum Zoom Out"));
+
} else if ( (MyGetKeyState()&WKEY_CTRL) == 0 ) {
wPrefGetInteger( "misc", "zoomout", &newScale, 16 );
+ InfoMessage(_("Preset Zoom Out Value selected. Shift+Ctrl+PageUp to reset value"));
DoNewScale( newScale );
} else {
wPrefSetInteger( "misc", "zoomout", (long)mainD.scale );
- InfoMessage( _("Zoom Out Program Value %ld:1"), (long)mainD.scale );
+ InfoMessage( _("Zoom Out Program Value %ld:1 set, Shift+PageUp to use"), (long)mainD.scale );
}
}
@@ -1911,12 +1956,6 @@ LOG( log_pan, 2, ( "NEW = [ %0.3f, %0.3f ] \n", pos.x, pos.y ) )
if (!liveMap)
MainRedraw();
LOG( log_pan, 1, ( "FINAL = [ %0.3f, %0.3f ]\n", pos.x, pos.y ) )
-#ifdef LATER
- if (recordF) {
- fprintf( recordF, "ORIG %0.3f %0.3f %0.3f\n",
- mainD.scale, mainD.orig.x, mainD.orig.y );
- }
-#endif
mode = noPan;
break;
@@ -1928,11 +1967,6 @@ LOG( log_pan, 1, ( "FINAL = [ %0.3f, %0.3f ]\n", pos.x, pos.y ) )
DrawHilight( &mapD, mainD.orig, mainD.size );
newOrig = pos;
oldOrig = newOrig;
-#ifdef LATER
- xscale = INIT_MAP_SCALE;
- size.x = mapD.size.x/xscale;
- size.y = mapD.size.y/xscale;
-#endif
xscale = 1;
size.x = mainD.size.x/mainD.scale;
size.y = mainD.size.y/mainD.scale;
@@ -2001,43 +2035,48 @@ LOG( log_pan, 1, ( "FINAL = [ %0.3f, %0.3f ]\n", pos.x, pos.y ) )
return;
case wAccelKey_F5:
MainRedraw();
+ MapRedraw();
return;
#endif
case wAccelKey_Right:
- DrawHilight( &mapD, mainD.orig, mainD.size );
+ //DrawHilight( &mapD, mainD.orig, mainD.size );
mainD.orig.x += mainD.size.x/2;
ConstraintOrig( &mainD.orig, mainD.size );
mainCenter.x = mainD.orig.x + mainD.size.x/2.0;
mainCenter.y = mainD.orig.y + mainD.size.y/2.0;
MainRedraw();
- DrawHilight( &mapD, mainD.orig, mainD.size );
+ MapRedraw();
+ //DrawHilight( &mapD, mainD.orig, mainD.size );
break;
case wAccelKey_Left:
- DrawHilight( &mapD, mainD.orig, mainD.size );
+ //DrawHilight( &mapD, mainD.orig, mainD.size );
mainD.orig.x -= mainD.size.x/2;
ConstraintOrig( &mainD.orig, mainD.size );
mainCenter.x = mainD.orig.x + mainD.size.x/2.0;
mainCenter.y = mainD.orig.y + mainD.size.y/2.0;
MainRedraw();
- DrawHilight( &mapD, mainD.orig, mainD.size );
+ MapRedraw();
+ //DrawHilight( &mapD, mainD.orig, mainD.size );
break;
case wAccelKey_Up:
- DrawHilight( &mapD, mainD.orig, mainD.size );
+ //DrawHilight( &mapD, mainD.orig, mainD.size );
mainD.orig.y += mainD.size.y/2;
ConstraintOrig( &mainD.orig, mainD.size );
mainCenter.x = mainD.orig.x + mainD.size.x/2.0;
mainCenter.y = mainD.orig.y + mainD.size.y/2.0;
MainRedraw();
- DrawHilight( &mapD, mainD.orig, mainD.size );
+ MapRedraw();
+ //DrawHilight( &mapD, mainD.orig, mainD.size );
break;
case wAccelKey_Down:
- DrawHilight( &mapD, mainD.orig, mainD.size );
+ //DrawHilight( &mapD, mainD.orig, mainD.size );
mainD.orig.y -= mainD.size.y/2;
ConstraintOrig( &mainD.orig, mainD.size );
mainCenter.x = mainD.orig.x + mainD.size.x/2.0;
mainCenter.y = mainD.orig.y + mainD.size.y/2.0;
MainRedraw();
- DrawHilight( &mapD, mainD.orig, mainD.size );
+ MapRedraw();
+ //DrawHilight( &mapD, mainD.orig, mainD.size );
break;
default:
return;
@@ -2167,6 +2206,12 @@ static void DoMouse( wAction_t action, coOrd pos )
break;
case wActionExtKey:
mainD.CoOrd2Pix(&mainD,pos,&x,&y);
+ if ((MyGetKeyState() &
+ (WKEY_SHIFT | WKEY_CTRL)) == (WKEY_SHIFT | WKEY_CTRL)) break; //Allow SHIFT+CTRL for Move
+ if (((action>>8)&0xFF) == wAccelKey_LineFeed) {
+ action = C_TEXT+((int)(0x0A<<8));
+ break;
+ }
switch ((wAccelKey_e)(action>>8)) {
case wAccelKey_Del:
SelectDelete();
@@ -2180,40 +2225,56 @@ static void DoMouse( wAction_t action, coOrd pos )
break;
#endif
case wAccelKey_Right:
- DrawHilight( &mapD, mainD.orig, mainD.size );
- mainD.orig.x += mainD.size.x/2;
+ //DrawHilight( &mapD, mainD.orig, mainD.size );
+ if ((MyGetKeyState() & WKEY_SHIFT) != 0)
+ mainD.orig.x += 0.25*mainD.scale; //~1cm in 1::1, 1ft in 30:1, 1mm in 10:1
+ else
+ mainD.orig.x += mainD.size.x/2;
ConstraintOrig( &mainD.orig, mainD.size );
mainCenter.x = mainD.orig.x + mainD.size.x/2.0;
mainCenter.y = mainD.orig.y + mainD.size.y/2.0;
MainRedraw();
- DrawHilight( &mapD, mainD.orig, mainD.size );
+ MapRedraw();
+ //DrawHilight( &mapD, mainD.orig, mainD.size );
break;
case wAccelKey_Left:
- DrawHilight( &mapD, mainD.orig, mainD.size );
- mainD.orig.x -= mainD.size.x/2;
+ //DrawHilight( &mapD, mainD.orig, mainD.size );
+ if ((MyGetKeyState() & WKEY_SHIFT) != 0)
+ mainD.orig.x -= 0.25*mainD.scale;
+ else
+ mainD.orig.x -= mainD.size.x/2;
ConstraintOrig( &mainD.orig, mainD.size );
mainCenter.x = mainD.orig.x + mainD.size.x/2.0;
mainCenter.y = mainD.orig.y + mainD.size.y/2.0;
MainRedraw();
- DrawHilight( &mapD, mainD.orig, mainD.size );
+ MapRedraw();
+ //DrawHilight( &mapD, mainD.orig, mainD.size );
break;
case wAccelKey_Up:
- DrawHilight( &mapD, mainD.orig, mainD.size );
- mainD.orig.y += mainD.size.y/2;
+ //DrawHilight( &mapD, mainD.orig, mainD.size );
+ if ((MyGetKeyState() & WKEY_SHIFT) != 0)
+ mainD.orig.y += 0.25*mainD.scale;
+ else
+ mainD.orig.y += mainD.size.x/2;
ConstraintOrig( &mainD.orig, mainD.size );
mainCenter.x = mainD.orig.x + mainD.size.x/2.0;
mainCenter.y = mainD.orig.y + mainD.size.y/2.0;
MainRedraw();
- DrawHilight( &mapD, mainD.orig, mainD.size );
+ MapRedraw();
+ //DrawHilight( &mapD, mainD.orig, mainD.size );
break;
case wAccelKey_Down:
- DrawHilight( &mapD, mainD.orig, mainD.size );
- mainD.orig.y -= mainD.size.y/2;
+ //DrawHilight( &mapD, mainD.orig, mainD.size );
+ if ((MyGetKeyState() & WKEY_SHIFT) != 0)
+ mainD.orig.y -= 0.25*mainD.scale;
+ else
+ mainD.orig.y -= mainD.size.x/2;
ConstraintOrig( &mainD.orig, mainD.size );
mainCenter.x = mainD.orig.x + mainD.size.x/2.0;
mainCenter.y = mainD.orig.y + mainD.size.y/2.0;
MainRedraw();
- DrawHilight( &mapD, mainD.orig, mainD.size );
+ MapRedraw();
+ //DrawHilight( &mapD, mainD.orig, mainD.size );
break;
default:
return;
@@ -2222,9 +2283,9 @@ static void DoMouse( wAction_t action, coOrd pos )
InfoPos( pos );
return;
case C_TEXT:
- if ((action>>8) == 0x0D)
+ if ((action>>8) == 0x0D) {
action = C_OK;
- else if ((action>>8) == 0x1B) {
+ } else if ((action>>8) == 0x1B) {
ConfirmReset( TRUE );
return;
}
@@ -2321,11 +2382,12 @@ static void DoMousew( wDraw_p d, void * context, wAction_t action, wPos_t x, wPo
}
ConstraintOrig( &orig, mainD.size );
if ( orig.x != mainD.orig.x || orig.y != mainD.orig.y ) {
- DrawMapBoundingBox( FALSE );
+ //DrawMapBoundingBox( FALSE );
mainD.orig = orig;
MainRedraw();
+ MapRedraw();
/*DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack );*/
- DrawMapBoundingBox( TRUE );
+ //DrawMapBoundingBox( TRUE );
wFlush();
}
}
@@ -2384,8 +2446,10 @@ static void MapDlgUpdate(
static void DrawChange( long changes )
{
- if (changes & CHANGE_MAIN)
+ if (changes & CHANGE_MAIN) {
MainRedraw();
+ MapRedraw();
+ }
if (changes &CHANGE_UNITS)
SetInfoBar();
if (changes & CHANGE_MAP)
@@ -2397,9 +2461,10 @@ EXPORT void DrawInit( int initialZoom )
{
wPos_t w, h;
+
wWinGetSize( mainW, &w, &h );
/*LayoutToolBar();*/
- h -= toolbarHeight+infoHeight;
+ h = h - (toolbarHeight+max(textHeight,infoHeight)+10);
if ( w <= 0 ) w = 1;
if ( h <= 0 ) h = 1;
tempD.d = mainD.d = wDrawCreate( mainW, 0, toolbarHeight, "", BD_TICKS,
@@ -2454,3 +2519,170 @@ EXPORT void DrawInit( int initialZoom )
wAttachAccelKey( wAccelKey_Pgdn, 0, (wAccelKeyCallBack_p)doZoomDown, NULL );
#endif
}
+
+#include "bitmaps/pan.xpm"
+
+static STATUS_T CmdPan(
+ wAction_t action,
+ coOrd pos )
+{
+ static enum { PAN, ZOOM, NONE } panmode;
+
+ static coOrd base, size;
+
+ DIST_T scale_x,scale_y;
+
+ static coOrd start_pos;
+ if ( action == C_DOWN ) {
+ panmode = PAN;
+ } else if ( action == C_RDOWN) {
+ panmode = ZOOM;
+ }
+
+ switch (action&0xFF) {
+ case C_START:
+ start_pos = zero;
+ panmode = NONE; InfoMessage(_("Left Drag to Pan, Right Drag to Zoom, 0 to set Origin to 0,0, 1-9 to Zoom#, e to set to Extent"));
+ break;
+ case C_DOWN:
+ panmode = PAN;
+ start_pos = pos;
+ InfoMessage(_("Pan Mode - drag point to new position"));
+ break;
+ case C_RDOWN:
+ panmode = ZOOM;
+ start_pos = pos;
+ base = pos;
+ size = zero;
+ InfoMessage(_("Zoom Mode - drag Area to Zoom"));
+ break;
+ case C_MOVE:
+ if (panmode == PAN) {
+ double min_inc;
+ if (mainD.scale >= 1.0) {
+ if (units == UNITS_ENGLISH) {
+ min_inc = 1/4; //>1:1 = 1/4 inch
+ } else {
+ min_inc = 1/(2.54*2); //>1:1 = 0.5 cm
+ }
+ } else {
+ if (units == UNITS_ENGLISH) {
+ min_inc = 1/64; //<1:1 = 1/64 inch
+ } else {
+ min_inc = 1/(25.4*2); //>1:1 = 0.5 mm
+ }
+ }
+ if ((fabs(pos.x-start_pos.x) > min_inc) || (fabs(pos.y-start_pos.y) > min_inc)) {
+ DrawMapBoundingBox( TRUE );
+ mainD.orig.x -= (pos.x - start_pos.x);
+ mainD.orig.y -= (pos.y - start_pos.y);
+ ConstraintOrig( &mainD.orig, mainD.size );
+ tempD.orig = mainD.orig;
+ mainCenter.x = mainD.orig.x + mainD.size.x/2.0;
+ mainCenter.y = mainD.orig.y + mainD.size.y/2.0;
+ DrawMapBoundingBox( TRUE );
+ }
+ }
+ MainRedraw();
+ break;
+ case C_RMOVE:
+ if (panmode == ZOOM) {
+ base = start_pos;
+ size.x = pos.x - base.x;
+ if (size.x < 0) {
+ size.x = - size.x ;
+ base.x = pos.x;
+ }
+ size.y = pos.y - base.y;
+ if (size.y < 0) {
+ size.y = - size.y;
+ base.y = pos.y;
+ }
+ }
+ MainRedraw();
+ break;
+ case C_RUP:
+
+ scale_x = size.x/mainD.size.x*mainD.scale;
+ scale_y = size.y/mainD.size.y*mainD.scale;
+
+ if (scale_x<scale_y)
+ scale_x = scale_y;
+ if (scale_x>1) scale_x = ceil( scale_x );
+ else scale_x = 1/(ceil(1/scale_x));
+
+ if (scale_x > MAX_MAIN_SCALE) scale_x = MAX_MAIN_SCALE;
+ if (scale_x < MIN_MAIN_MACRO) scale_x = MIN_MAIN_MACRO;
+
+ mainCenter.x = base.x + size.x/2.0; //Put center for scale in center of square
+ mainCenter.y = base.y + size.y/2.0;
+ mainD.orig.x = base.x;
+ mainD.orig.y = base.y;
+
+ panmode = NONE;
+ DoNewScale(scale_x);
+ MapRedraw();
+ break;
+ case C_UP:
+ panmode = NONE;
+ break;
+ case C_REDRAW:
+ if (panmode == ZOOM) {
+ if (base.x && base.y && size.x && size.y)
+ DrawHilight( &mainD, base, size );
+ }
+ break;
+ case C_CANCEL:
+ base = zero;
+ return C_TERMINATE;
+ case C_TEXT:
+ panmode = NONE;
+ if ((action>>8) == 0x65) { //"e"
+ scale_x = mapD.size.x/(mainD.size.x/mainD.scale);
+ scale_y = mapD.size.y/(mainD.size.y/mainD.scale);
+ if (scale_x<scale_y)
+ scale_x = scale_y;
+ scale_x = ceil(scale_x);
+ if (scale_x < 1) scale_x = 1;
+ if (scale_x > MAX_MAIN_SCALE) scale_x = MAX_MAIN_SCALE;
+ mainD.orig = zero;
+ mainCenter.x = mainD.orig.x + mapD.size.x/2.0;
+ mainCenter.y = mainD.orig.y + mapD.size.y/2.0;
+ DoNewScale(scale_x);
+ ConstraintOrig( &mainD.orig, mainD.size );
+ mainCenter.x = mainD.orig.x + mainD.size.x/2.0;
+ mainCenter.y = mainD.orig.y + mainD.size.y/2.0;
+ MapRedraw();
+ MainRedraw();
+ }
+ if ((action>>8) == 0x30) { //"0"
+ mainD.orig = zero;
+ ConstraintOrig( &mainD.orig, mainD.size );
+ mainCenter.x = mainD.orig.x + mainD.size.x/2.0;
+ mainCenter.y = mainD.orig.y + mainD.size.y/2.0;
+ MapRedraw();
+ MainRedraw();
+ }
+ if ((action>>8) >= 0x31 && (action>>8) <= 0x39) { //"1" to "9"
+ scale_x = (action>>8)&0x0F;
+ DoNewScale(scale_x);
+ MapRedraw();
+ MainRedraw();
+ }
+ if ((action>>8) == 0x0D) {
+ return C_TERMINATE;
+ }
+ else if ((action>>8) == 0x1B) {
+ return C_TERMINATE;
+ }
+ break;
+ }
+
+ return C_CONTINUE;
+}
+
+EXPORT void InitCmdPan( wMenu_p menu )
+{
+ panCmdInx = AddMenuButton( menu, CmdPan, "cmdPan", _("Pan/Zoom"), wIconCreatePixMap(pan_xpm),
+ LEVEL0, IC_CANCEL|IC_POPUP|IC_LCLICK|IC_RCLICK|IC_CMDMENU, ACCL_PAN, NULL );
+}
diff --git a/app/bin/draw.h b/app/bin/draw.h
index db8d91a..6a7d806 100644
--- a/app/bin/draw.h
+++ b/app/bin/draw.h
@@ -1,5 +1,5 @@
/** \file draw.h
- *
+ * Definitions and prototypes for drawing operations
*/
/* XTrkCad - Model Railroad CAD
@@ -23,8 +23,8 @@
#ifndef DRAW_H
#define DRAW_H
-#define MSG_BASE (1000)
-#include "messages.h"
+#include "common.h"
+#include "wlib.h"
#define DC_TICKS (1<<1)
#define DC_PRINT (1<<2)
@@ -106,6 +106,7 @@ extern long drawCount;
extern BOOL_T drawEnable;
extern long currRedraw;
+
extern wDrawColor drawColorBlack;
extern wDrawColor drawColorWhite;
extern wDrawColor drawColorRed;
@@ -117,6 +118,7 @@ extern wDrawColor drawColorGold;
#define wDrawColorBlack drawColorBlack
#define wDrawColorWhite drawColorWhite
#define wDrawColorBlue drawColorBlue
+#define wDrawColorRed drawColorRed
extern wDrawColor markerColor;
extern wDrawColor borderColor;
@@ -167,8 +169,10 @@ void DrawHilightPolygon( drawCmd_p, coOrd *, int );
#define BOX_ARROW (4)
#define BOX_BACKGROUND (5)
void DrawBoxedString( int, drawCmd_p, coOrd, char *, wFont_p, wFontSize_t, wDrawColor, ANGLE_T );
-void DrawTextSize2( drawCmd_p, char *, wFont_p, wFontSize_t, BOOL_T, coOrd *, POS_T * );
+void DrawMultiLineTextSize(drawCmd_p dp, char * text, wFont_p fp, wFontSize_t fs, BOOL_T relative, coOrd * size, coOrd * lastline );
+void DrawTextSize2( drawCmd_p, char *, wFont_p, wFontSize_t, BOOL_T, coOrd *, POS_T *);
void DrawTextSize( drawCmd_p, char *, wFont_p, wFontSize_t, BOOL_T, coOrd * );
+void DrawMultiString(drawCmd_p d, coOrd pos, char * text, wFont_p fp, wFontSize_t fs, wDrawColor color, ANGLE_T a, coOrd * lo, coOrd * hi);
BOOL_T SetRoomSize( coOrd );
void GetRoomSize( coOrd * );
void DoRedraw( void );
@@ -192,6 +196,8 @@ void InfoPos( coOrd );
void InfoCount( wIndex_t );
void SetMessage( char * );
+wIndex_t panCmdInx;
+
void InfoSubstituteControls( wControl_p *, char * * );
void MapGrid( coOrd, coOrd, ANGLE_T, coOrd, ANGLE_T, POS_T, POS_T, int *, int *, int *, int * );
diff --git a/app/bin/drawgeom.c b/app/bin/drawgeom.c
index 8ef31e8..6d4b237 100644
--- a/app/bin/drawgeom.c
+++ b/app/bin/drawgeom.c
@@ -17,14 +17,22 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
+#include <math.h>
#include <stdarg.h>
-#include "track.h"
+#include <string.h>
+
#include "ccurve.h"
+#include "cbezier.h"
#include "compound.h"
+#include "cundo.h"
#include "drawgeom.h"
+#include "fileio.h"
#include "i18n.h"
+#include "messages.h"
+#include "param.h"
+#include "track.h"
+#include "utility.h"
-/*EXPORT drawContext_t * drawContext;*/
static long drawGeomCurveMode;
#define contextSegs(N) DYNARR_N( trkSeg_t, context->Segs_da, N )
@@ -68,6 +76,7 @@ static void EndPoly( drawContext_t * context, int cnt )
segPtr->u.p.pts = pts;
segPtr->u.p.angle = 0.0;
segPtr->u.p.orig = zero;
+ segPtr->u.p.polyType = FREEFORM;
UndoStart( _("Create Lines"), "newDraw" );
trk = MakeDrawFromSeg( zero, 0.0, segPtr );
DrawNewTrack( trk );
@@ -133,19 +142,38 @@ STATUS_T DrawGeomMouse(
case wActionLDown:
context->Started = TRUE;
+ if (context->State == 0) { //First Down only
+ switch (context->Op) { //Snap pos to nearest line end point if this is end and just shift is depressed for lines and some curves
+ case OP_LINE:
+ case OP_CURVE1:
+ if ((MyGetKeyState() & (WKEY_SHIFT|WKEY_CTRL|WKEY_ALT)) == WKEY_SHIFT ) {
+ coOrd p = pos;
+ track_p t;
+ if ((t=OnTrack(&p,FALSE,FALSE))) {
+ if (GetClosestEndPt(t,&p)) {
+ pos = p;
+ }
+ }
+ };
+ break;
+ default:
+ ;
+ }
+ }
if ((context->Op == OP_CURVE1 || context->Op == OP_CURVE2 || context->Op == OP_CURVE3 || context->Op == OP_CURVE4) && context->State == 1) {
;
} else {
- if ( (MyGetKeyState() & (WKEY_SHIFT|WKEY_CTRL|WKEY_ALT)) == WKEY_CTRL )
+ if ( (MyGetKeyState() & (WKEY_SHIFT|WKEY_CTRL|WKEY_ALT)) == WKEY_CTRL ) // Control snaps to nearest track (not necessarily the end)
OnTrack( &pos, FALSE, FALSE );
pos0 = pos;
pos1 = pos;
}
+
switch (context->Op) {
case OP_LINE:
case OP_DIMLINE:
case OP_BENCH:
- if ( lastValid && ( MyGetKeyState() & WKEY_SHIFT ) ) {
+ if ( lastValid && ( MyGetKeyState() & WKEY_CTRL ) ) {
pos = pos0 = lastPos;
}
DYNARR_SET( trkSeg_t, tempSegs_da, 1 );
@@ -166,7 +194,7 @@ STATUS_T DrawGeomMouse(
context->message( _("Drag to place next end point") );
break;
case OP_TBLEDGE:
- if ( lastValid && ( MyGetKeyState() & WKEY_SHIFT ) ) {
+ if ( lastValid && ( MyGetKeyState() & WKEY_CTRL ) ) {
pos = pos0 = lastPos;
}
OnTableEdgeEndPt( NULL, &pos );
@@ -278,8 +306,8 @@ STATUS_T DrawGeomMouse(
break;
case OP_CURVE1: case OP_CURVE2: case OP_CURVE3: case OP_CURVE4:
if (context->State == 0) {
+ CreateCurve( C_MOVE, pos, FALSE, context->Color, width, drawGeomCurveMode, context->message );
pos0x = pos;
- CreateCurve( C_MOVE, pos, TRUE, context->Color, width, drawGeomCurveMode, context->message );
} else {
PlotCurve( drawGeomCurveMode, pos0, pos0x, pos1, &context->ArcData, FALSE );
tempSegs(0).color = context->Color;
@@ -346,6 +374,7 @@ STATUS_T DrawGeomMouse(
else
DrawSegs( context->D, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack );
context->D->funcs->options = oldOptions;
+ if (context->Op == OP_DIMLINE) MainRedraw(); //Wipe Out Text
return C_CONTINUE;
case wActionLUp:
@@ -355,6 +384,45 @@ STATUS_T DrawGeomMouse(
DrawSegs( context->D, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack );
lastValid = FALSE;
createTrack = FALSE;
+ if ((context->State == 0 && (context->Op == OP_LINE )) || //first point release for line,
+ (context->State == 1 && context->Op == OP_CURVE1)) { //second point for curve from end
+ switch (context->Op) { //Snap pos to nearest line end point if this is on a line and just shift is depressed for lines and some curves
+ case OP_CURVE1:
+ case OP_LINE:
+ if ((MyGetKeyState() & (WKEY_SHIFT|WKEY_CTRL|WKEY_ALT)) == WKEY_SHIFT ) {
+ coOrd p = pos1;
+ track_p t;
+ if ((t=OnTrack(&p,FALSE,FALSE))) {
+ if (GetClosestEndPt(t,&p)) {
+ pos1 = p;
+ if (context->Op == OP_LINE) {
+ tempSegs(0).u.l.pos[1] = p;
+ } else {
+ PlotCurve( drawGeomCurveMode, pos0, pos0x, pos1, &context->ArcData, FALSE );
+ if (context->ArcData.type == curveTypeStraight) {
+ tempSegs(0).type = SEG_STRLIN;
+ tempSegs(0).u.l.pos[0] = pos0;
+ tempSegs(0).u.l.pos[1] = context->ArcData.pos1;
+ tempSegs_da.cnt = 1;
+ } else if (context->ArcData.type == curveTypeNone) {
+ tempSegs_da.cnt = 0;
+ } else if (context->ArcData.type == curveTypeCurve) {
+ tempSegs(0).type = SEG_CRVLIN;
+ tempSegs(0).u.c.center = context->ArcData.curvePos;
+ tempSegs(0).u.c.radius = context->ArcData.curveRadius;
+ tempSegs(0).u.c.a0 = context->ArcData.a0;
+ tempSegs(0).u.c.a1 = context->ArcData.a1;
+ tempSegs_da.cnt = 1;
+ }
+ }
+ }
+ }
+ };
+ break;
+ default:
+ ;
+ }
+ }
switch ( context->Op ) {
case OP_LINE:
case OP_DIMLINE:
@@ -368,7 +436,7 @@ STATUS_T DrawGeomMouse(
context->State = 1;
context->ArcAngle = FindAngle( pos0, pos1 );
pos0x = pos1;
- CreateCurve( C_UP, pos, TRUE, context->Color, width, drawGeomCurveMode, context->message );
+ CreateCurve( C_UP, pos, FALSE, context->Color, width, drawGeomCurveMode, context->message );
DrawSegs( context->D, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack );
segCnt = tempSegs_da.cnt;
context->message( _("Drag on Red arrows to adjust curve") );
@@ -417,17 +485,16 @@ STATUS_T DrawGeomMouse(
break;
case OP_BOX:
case OP_FILLBOX:
- if ( context->Op == OP_FILLBOX ) {
- pts = (coOrd*)MyMalloc( 4 * sizeof *(coOrd*)NULL );
- for ( inx=0; inx<4; inx++ )
- pts[inx] = tempSegs(inx).u.l.pos[0];
- tempSegs(0).type = SEG_FILPOLY;
- tempSegs(0).u.p.cnt = 4;
- tempSegs(0).u.p.pts = pts;
- tempSegs(0).u.p.angle = 0.0;
- tempSegs(0).u.p.orig = zero;
- tempSegs_da.cnt = 1;
- }
+ pts = (coOrd*)MyMalloc( 4 * sizeof *(coOrd*)NULL );
+ for ( inx=0; inx<4; inx++ )
+ pts[inx] = tempSegs(inx).u.l.pos[0];
+ tempSegs(0).type = (context->Op == OP_FILLBOX)?SEG_FILPOLY:SEG_POLY;
+ tempSegs(0).u.p.cnt = 4;
+ tempSegs(0).u.p.pts = pts;
+ tempSegs(0).u.p.angle = 0.0;
+ tempSegs(0).u.p.orig = zero;
+ tempSegs(0).u.p.polyType = RECTANGLE;
+ tempSegs_da.cnt = 1;
/*drawContext = context;
DrawGeomOp( (void*)context->Op );*/
break;
@@ -445,6 +512,7 @@ STATUS_T DrawGeomMouse(
return C_TERMINATE;
case wActionText:
+
if ( ((action>>8)&0xFF) == 0x0D ||
((action>>8)&0xFF) == ' ' ) {
EndPoly(context, segCnt);
@@ -453,6 +521,7 @@ STATUS_T DrawGeomMouse(
return C_TERMINATE;
case C_CANCEL:
+
oldOptions = context->D->funcs->options;
context->D->funcs->options |= wDrawOptTemp;
DrawSegs( context->D, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack );
@@ -487,18 +556,21 @@ STATUS_T DrawGeomModify(
{
ANGLE_T a;
coOrd p0, p1, pc;
+ static coOrd start_pos;
static wIndex_t segInx;
static EPINX_T segEp;
static ANGLE_T segA1;
- static int polyInx;
- int inx;
- DIST_T d, dd;
+ static int polyInx, inx_other, inx_line, inx_origin;
+ static BOOL_T corner_mode;
+ int inx, inx1, inx2;
+ DIST_T d, d1, d2, dd;
coOrd * newPts;
int mergePoints;
-
+ tempSegs_da.cnt = 1;
switch ( action ) {
case C_DOWN:
segInx = -1;
+ corner_mode = FALSE;
DistanceSegs( orig, angle, segCnt, segPtr, &pos, &segInx );
if (segInx == -1)
return C_ERROR;
@@ -523,7 +595,7 @@ STATUS_T DrawGeomModify(
REORIGIN( pc, segPtr[segInx].u.c.center, angle, orig )
tempSegs(0).type = segPtr[segInx].type;
tempSegs(0).u.c.center = pc;
- tempSegs(0).u.c.radius = segPtr[segInx].u.c.radius;
+ tempSegs(0).u.c.radius = fabs(segPtr[segInx].u.c.radius);
if (segPtr[segInx].u.c.a1 >= 360.0) {
tempSegs(0).u.c.a0 = 0.0;
tempSegs(0).u.c.a1 = 360.0;
@@ -531,8 +603,8 @@ STATUS_T DrawGeomModify(
tempSegs(0).u.c.a0 = NormalizeAngle( segPtr[segInx].u.c.a0+angle );
tempSegs(0).u.c.a1 = segPtr[segInx].u.c.a1;
segA1 = NormalizeAngle( segPtr[segInx].u.c.a0 + segPtr[segInx].u.c.a1 + angle );
- PointOnCircle( &p0, pc, segPtr[segInx].u.c.radius, segPtr[segInx].u.c.a0+angle );
- PointOnCircle( &p1, pc, segPtr[segInx].u.c.radius, segPtr[segInx].u.c.a0+segPtr[segInx].u.c.a1+angle );
+ PointOnCircle( &p0, pc, fabs(segPtr[segInx].u.c.radius), segPtr[segInx].u.c.a0+angle );
+ PointOnCircle( &p1, pc, fabs(segPtr[segInx].u.c.radius), segPtr[segInx].u.c.a0+segPtr[segInx].u.c.a1+angle );
}
break;
@@ -542,6 +614,7 @@ STATUS_T DrawGeomModify(
tempSegs(0).u.p.cnt = segPtr[segInx].u.p.cnt;
tempSegs(0).u.p.angle = 0.0;
tempSegs(0).u.p.orig = zero;
+ tempSegs(0).u.p.polyType = segPtr[segInx].u.p.polyType;
DYNARR_SET( coOrd, points_da, segPtr[segInx].u.p.cnt+1 );
tempSegs(0).u.p.pts = &points(0);
d = 10000;
@@ -557,28 +630,61 @@ STATUS_T DrawGeomModify(
polyInx = inx;
}
}
- inx = (polyInx==0?segPtr[segInx].u.p.cnt-1:polyInx-1);
- d = FindDistance( points(inx), pos );
- dd = FindDistance( points(inx), points(polyInx) );
- if ( d < 0.25*dd ) {
- polyInx = inx;
- } else if ( d > 0.75*dd ) {
- ;
- } else {
- tempSegs(0).u.p.cnt++;
- for (inx=points_da.cnt-1; inx>polyInx; inx-- ) {
- points(inx) = points(inx-1);
+ if (segPtr[segInx].u.p.polyType == RECTANGLE) {
+ d1 = FindDistance( points(polyInx), pos );
+ d2 = FindDistance( points(polyInx==0?segPtr[segInx].u.p.cnt-1:polyInx-1), pos );
+ if (d2<d1) {
+ inx_line = polyInx;
+ polyInx = polyInx==0?segPtr[segInx].u.p.cnt-1:polyInx-1;
+ } else {
+ inx_line = polyInx==0?segPtr[segInx].u.p.cnt-1:polyInx-1;
+ }
+ //polyInx is closest point
+ inx1 = (polyInx==0?3:polyInx-1); //Prev point
+ inx2 = (polyInx==3?0:polyInx+1); //Next Point
+ inx_origin = (inx2==3?0:inx2+1); //Opposite point
+ if ( IsClose(d2) || IsClose(d1) ) {
+ corner_mode = TRUE;
+ pos = points(polyInx);
+ start_pos = pos;
+ DrawArrowHeads( &tempSegs(1), pos, FindAngle(points(polyInx),points(inx_origin)), TRUE, wDrawColorRed );
+ tempSegs_da.cnt = 6;
+ InfoMessage( _("Drag to Move Corner Point"));
+ } else {
+ corner_mode = FALSE;
+ start_pos = pos;
+ pos.x = (points(polyInx).x + points(inx_line).x)/2;
+ pos.y = (points(polyInx).y + points(inx_line).y)/2;
+ DrawArrowHeads( &tempSegs(1), pos, FindAngle(points(polyInx),pos)+90, TRUE, wDrawColorRed );
+ tempSegs_da.cnt = 6;
+ InfoMessage( _("Drag to Move Edge "));
}
+ return C_CONTINUE;
+ } else {
+ inx = (polyInx==0?segPtr[segInx].u.p.cnt-1:polyInx-1);
+ d = FindDistance( points(inx), pos );
+ dd = FindDistance( points(inx), points(polyInx) );
+ if ( d < 0.25*dd ) {
+ polyInx = inx;
+ } else if ( d > 0.75*dd ) {
+ ;
+ } else {
+ tempSegs(0).u.p.cnt++;
+ for (inx=points_da.cnt-1; inx>polyInx; inx-- ) {
+ points(inx) = points(inx-1);
+ }
/*fprintf( stderr, "Inserting vertix before %d\n", polyInx );*/
+ }
+ points(polyInx) = pos;
}
- points(polyInx) = pos;
p1=p0;
break;
- default:
- ASSERT( FALSE ); /* CHECKME */
case SEG_TEXT:
segInx = -1;
return C_ERROR;
+ default:
+ ASSERT( FALSE ); /* CHECKME */
+
}
if ( FindDistance( p0, pos ) < FindDistance( p1, pos ) )
segEp = 0;
@@ -586,7 +692,6 @@ STATUS_T DrawGeomModify(
segEp = 1;
switch ( segPtr[segInx].type ) {
case SEG_TBLEDGE:
-
case SEG_STRLIN:
case SEG_DIMLIN:
case SEG_BENCH:
@@ -596,7 +701,6 @@ STATUS_T DrawGeomModify(
;
}
}
- tempSegs_da.cnt = 1;
return C_CONTINUE;
case C_MOVE:
if (segInx == -1)
@@ -608,6 +712,9 @@ STATUS_T DrawGeomModify(
} else if ( (MyGetKeyState() & (WKEY_SHIFT|WKEY_CTRL|WKEY_ALT)) == WKEY_CTRL ) {
OnTrack( &pos, FALSE, FALSE );
}
+ int prior_pnt, next_pnt, orig_pnt;
+ ANGLE_T prior_angle, next_angle, line_angle;
+ tempSegs_da.cnt = 1;
switch (tempSegs(0).type) {
case SEG_TBLEDGE:
@@ -617,12 +724,6 @@ STATUS_T DrawGeomModify(
tempSegs(0).u.l.pos[segEp] = pos;
InfoMessage( _("Length = %0.3f Angle = %0.3f"), FindDistance( tempSegs(0).u.l.pos[segEp], tempSegs(0).u.l.pos[1-segEp] ), FindAngle( tempSegs(0).u.l.pos[1-segEp], tempSegs(0).u.l.pos[segEp] ) );
break;
- pos.x -= orig.x;
- pos.y -= orig.y;
- pos.x -= orig.x;
- pos.y -= orig.y;
- Rotate( &pos, zero, -angle );
- Rotate( &pos, zero, -angle );
case SEG_CRVLIN:
case SEG_FILCRCL:
if (tempSegs(0).u.c.a1 >= 360.0) {
@@ -639,12 +740,50 @@ STATUS_T DrawGeomModify(
break;
case SEG_POLY:
case SEG_FILPOLY:
- points(polyInx) = pos;
+ switch (tempSegs(0).u.p.polyType) {
+ case RECTANGLE:
+ if (!corner_mode) {
+ /* Constrain movement to be perpendicular */
+ d = FindDistance(start_pos, pos);
+ line_angle = NormalizeAngle(FindAngle(points(inx_line),points(polyInx))-90);
+ a = FindAngle(pos,start_pos);
+ Translate( &pos, start_pos, line_angle, - d*cos(D2R(line_angle-a)));
+ }
+ d = FindDistance(start_pos,pos);
+ a = FindAngle(start_pos, pos);
+ start_pos = pos;
+ prior_pnt = (polyInx == 0)?3:polyInx-1;
+ next_pnt = (polyInx == 3)?0:polyInx+1;
+ orig_pnt = (prior_pnt == 0)?3:prior_pnt-1;
+ Translate( &points(polyInx), points(polyInx), a, d);
+ d = FindDistance(points(orig_pnt),points(polyInx));
+ a = FindAngle(points(orig_pnt),points(polyInx));
+ prior_angle = FindAngle(points(orig_pnt),points(prior_pnt));
+ Translate( &points(prior_pnt), points(orig_pnt), prior_angle, d*cos(D2R(prior_angle-a)));
+ next_angle = FindAngle(points(orig_pnt),points(next_pnt));
+ Translate( &points(next_pnt), points(orig_pnt), next_angle, d*cos(D2R(next_angle-a)));
+ if (!corner_mode) {
+ pos.x = (points(polyInx).x + points(inx_line).x)/2;
+ pos.y = (points(polyInx).y + points(inx_line).y)/2;
+ DrawArrowHeads( &tempSegs(1), pos, FindAngle(points(polyInx),points(inx_line))+90, TRUE, wDrawColorRed );
+ tempSegs_da.cnt = 6;
+ InfoMessage( _("Drag to Move Edge"));
+ } else {
+ pos = points(polyInx);
+ DrawArrowHeads( &tempSegs(1), pos, FindAngle(points(polyInx),points(inx_origin)), TRUE, wDrawColorRed );
+ tempSegs_da.cnt = 6;
+ InfoMessage( _("Drag to Move Corner Point"));
+ }
+ break;
+ default:
+ points(polyInx) = pos;
+ }
+
break;
default:
;
}
- tempSegs_da.cnt = 1;
+
return C_CONTINUE;
case C_UP:
if (segInx == -1)
@@ -664,7 +803,7 @@ STATUS_T DrawGeomModify(
case SEG_CRVLIN:
case SEG_FILCRCL:
if ( tempSegs(0).u.c.a1 >= 360.0 ) {
- segPtr[segInx].u.c.radius = tempSegs(0).u.c.radius;
+ segPtr[segInx].u.c.radius = fabs(tempSegs(0).u.c.radius);
} else {
a = FindAngle( tempSegs(0).u.c.center, pos );
a = NormalizeAngle( a-angle );
@@ -676,39 +815,54 @@ STATUS_T DrawGeomModify(
break;
case SEG_POLY:
case SEG_FILPOLY:
- mergePoints = FALSE;
- if ( IsClose( FindDistance( pos, points( polyInx==0?tempSegs(0).u.p.cnt-1:polyInx-1 ) ) ) ||
- IsClose( FindDistance( pos, points( (polyInx==tempSegs(0).u.p.cnt-1)?0:polyInx+1 ) ) ) ) {
- mergePoints = TRUE;
- if (segPtr[segInx].u.p.cnt <= 3) {
- ErrorMessage( MSG_POLY_SHAPES_3_SIDES );
- break;
+ switch(tempSegs(0).u.p.polyType) {
+ case RECTANGLE:
+ for (int i=0;i<4;i++) {
+ pos = points(i);
+ pos.x -= orig.x;
+ pos.y -= orig.y;
+ Rotate( &pos, zero, -angle );
+ segPtr[segInx].u.p.pts[i] = pos;
+ }
+ break;
+ default:
+ mergePoints = FALSE;
+ if ( IsClose( FindDistance( pos, points( polyInx==0?tempSegs(0).u.p.cnt-1:polyInx-1 ) ) ) ||
+ IsClose( FindDistance( pos, points( (polyInx==tempSegs(0).u.p.cnt-1)?0:polyInx+1 ) ) ) ) {
+ mergePoints = TRUE;
+ if (segPtr[segInx].u.p.cnt <= 3) {
+ ErrorMessage( MSG_POLY_SHAPES_3_SIDES );
+ break;
+ }
}
- }
- newPts = (coOrd*)MyMalloc( tempSegs(0).u.p.cnt * sizeof *(coOrd*)0 );
- memcpy( newPts, segPtr[segInx].u.p.pts, (segPtr[segInx].u.p.cnt) * sizeof *(coOrd*)0 );
- segPtr[segInx].u.p.pts = newPts;
+ coOrd * oldPts = segPtr[segInx].u.p.pts;
+ newPts = (coOrd*)MyMalloc( tempSegs(0).u.p.cnt * sizeof *(coOrd*)0 );
+ memcpy( newPts, segPtr[segInx].u.p.pts, (segPtr[segInx].u.p.cnt) * sizeof *(coOrd*)0 );
+ segPtr[segInx].u.p.pts = newPts;
+ MyFree(oldPts);
- if ( tempSegs(0).u.p.cnt > segPtr[segInx].u.p.cnt ) {
- ASSERT( tempSegs(0).u.p.cnt == segPtr[segInx].u.p.cnt+1 );
- for (inx=tempSegs(0).u.p.cnt-1; inx>polyInx; inx--)
- segPtr[segInx].u.p.pts[inx] = segPtr[segInx].u.p.pts[inx-1];
- segPtr[segInx].u.p.cnt++;
- }
+
+ if ( tempSegs(0).u.p.cnt > segPtr[segInx].u.p.cnt ) {
+ ASSERT( tempSegs(0).u.p.cnt == segPtr[segInx].u.p.cnt+1 );
+ for (inx=tempSegs(0).u.p.cnt-1; inx>polyInx; inx--)
+ segPtr[segInx].u.p.pts[inx] = segPtr[segInx].u.p.pts[inx-1];
+ segPtr[segInx].u.p.cnt++;
+ }
- pos = points(polyInx);
- if ( mergePoints ) {
- for (inx=polyInx+1; inx<points_da.cnt; inx++)
+ pos = points(polyInx);
+ if ( mergePoints ) {
+ for (inx=polyInx+1; inx<points_da.cnt; inx++)
segPtr[segInx].u.p.pts[inx-1] = segPtr[segInx].u.p.pts[inx];
- segPtr[segInx].u.p.cnt--;
+ segPtr[segInx].u.p.cnt--;
/*fprintf( stderr, "Merging with vertix %d\n", polyInx );*/
- break;
+ break;
+ }
+ pos.x -= orig.x;
+ pos.y -= orig.y;
+ Rotate( &pos, zero, -angle );
+ segPtr[segInx].u.p.pts[polyInx] = pos;
}
- pos.x -= orig.x;
- pos.y -= orig.y;
- Rotate( &pos, zero, -angle );
- segPtr[segInx].u.p.pts[polyInx] = pos;
break;
default:
;
diff --git a/app/bin/drawgeom.h b/app/bin/drawgeom.h
index 377ebaa..d9f54f8 100644
--- a/app/bin/drawgeom.h
+++ b/app/bin/drawgeom.h
@@ -1,4 +1,6 @@
-
+/** \file drawgeom.h
+ *
+ */
/* XTrkCad - Model Railroad CAD
* Copyright (C) 2005 Dave Bullis
*
@@ -17,6 +19,15 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
+#ifndef HAVE_DRAWGEOM_H
+#define HAVE_DRAWGEOM_H
+
+#include "ccurve.h"
+#include "common.h"
+#include "draw.h"
+#include "track.h"
+#include "wlib.h"
+
#define OP_LINE (0)
#define OP_DIMLINE (1)
#define OP_BENCH (2)
@@ -35,7 +46,8 @@
#define OP_FILLCIRCLE3 (15)
#define OP_FILLBOX (16)
#define OP_FILLPOLY (17)
-#define OP_LAST (OP_FILLPOLY)
+#define OP_BEZLIN (18)
+#define OP_LAST (OP_BEZLIN)
typedef struct {
void (*message)( char *, ... );
@@ -53,6 +65,10 @@ typedef struct {
} drawContext_t;
extern drawContext_t * drawContext;
+extern wDrawColor lineColor;
+extern long lineWidth;
+
void DrawGeomOp( void * );
STATUS_T DrawGeomMouse( wAction_t, coOrd, drawContext_t * );
STATUS_T DrawGeomModify( coOrd, ANGLE_T, wIndex_t, trkSeg_p, wAction_t, coOrd, wBool_t );
+#endif //HAVE_DRAWGEOM_H \ No newline at end of file
diff --git a/app/bin/dxfformat.c b/app/bin/dxfformat.c
index 4e1c775..3b79c7e 100644
--- a/app/bin/dxfformat.c
+++ b/app/bin/dxfformat.c
@@ -308,7 +308,7 @@ DxfPrologue(DynString *result, int layerCount, double x0, double y0, double x1,
DynStringCatCStr(result, "\
0\nSECTION\n\
2\nHEADER\n\
- 9\n$ACADVER\n 1\nAC1015\n");
+ 9\n$ACADVER\n 1\nAC1009\n");
DxfUnits(result);
DxfDimensionSize(result, DXF_DIMTEXTSIZE);
DxfDimensionSize(result, DXF_DIMARROWSIZE);
diff --git a/app/bin/dxfformat.h b/app/bin/dxfformat.h
index 88db568..dbb1ee9 100644
--- a/app/bin/dxfformat.h
+++ b/app/bin/dxfformat.h
@@ -1,6 +1,30 @@
+/** \file dxfformat.h
+ * Definitions and prototypes for DXF export
+ */
+
+/* XTrkCad - Model Railroad CAD
+ * Copyright (C) 2005 Dave Bullis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, 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 License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
#ifndef HAVE_DXFFORMAT_H
#define HAVE_DXFFORMAT_H
+#include "dynstring.h"
+
enum DXF_DIMENSIONS
{
DXF_DIMTEXTSIZE,
diff --git a/app/bin/dxfoutput.c b/app/bin/dxfoutput.c
index 91a822f..69c6df4 100644
--- a/app/bin/dxfoutput.c
+++ b/app/bin/dxfoutput.c
@@ -26,9 +26,6 @@
#ifdef WINDOWS
#include <io.h>
#include <windows.h>
- #if _MSC_VER >=1400
- #define strdup _strdup
- #endif
#else
#include <errno.h>
#endif
@@ -36,11 +33,18 @@
#include <xtrkcad-config.h>
#include <locale.h>
#include <assert.h>
-#include "track.h"
-#include "i18n.h"
+
#include <dynstring.h>
+#include "cselect.h"
+#include "custom.h"
#include "dxfformat.h"
+#include "fileio.h"
+#include "i18n.h"
+#include "messages.h"
+#include "paths.h"
+#include "track.h"
+#include "utility.h"
static struct wFilSel_t * exportDXFFile_fs;
@@ -224,7 +228,7 @@ void DoExportDXF(void)
exportDXFFile_fs = wFilSelCreate(mainW, FS_SAVE, 0, _("Export to DXF"),
sDXFFilePattern, DoExportDXFTracks, NULL);
- wFilSelect(exportDXFFile_fs, curDirName);
+ wFilSelect(exportDXFFile_fs, GetCurrentPath(DXFPATHKEY));
}
diff --git a/app/bin/elev.c b/app/bin/elev.c
index ec232a4..6b86bc1 100644
--- a/app/bin/elev.c
+++ b/app/bin/elev.c
@@ -1,5 +1,5 @@
-/*
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/elev.c,v 1.1 2005-12-07 15:47:20 rc-flyer Exp $
+/** \file elev.c
+ *
*/
/* XTrkCad - Model Railroad CAD
@@ -19,11 +19,16 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
+#include <math.h>
-#include "track.h"
-#include "shrtpath.h"
#include "ccurve.h"
-
+#include "cundo.h"
+#include "messages.h"
+#include "param.h"
+#include "shrtpath.h"
+#include "track.h"
+#include "utility.h"
+#include "string.h"
EXPORT long oldElevationEvaluation = 0;
static int log_fillElev = 0;
diff --git a/app/bin/fileio.c b/app/bin/fileio.c
index da0de68..4e2a21d 100644
--- a/app/bin/fileio.c
+++ b/app/bin/fileio.c
@@ -35,9 +35,9 @@
#ifdef WINDOWS
#include <io.h>
#include <windows.h>
- #if _MSC_VER >=1400
- #define strdup _strdup
- #endif
+ //#if _MSC_VER >=1400
+ // #define strdup _strdup
+ //#endif
#else
#endif
#include <sys/stat.h>
@@ -48,14 +48,23 @@
#include <assert.h>
-#include "track.h"
-#include "version.h"
#include "common.h"
-#include "utility.h"
-#include "draw.h"
-#include "misc.h"
#include "compound.h"
+#include "cselect.h"
+#include "cundo.h"
+#include "custom.h"
+#include "draw.h"
+#include "fileio.h"
#include "i18n.h"
+#include "layout.h"
+#include "messages.h"
+#include "misc.h"
+#include "param.h"
+#include "paths.h"
+#include "track.h"
+#include "utility.h"
+#include "version.h"
+
/*#define TIME_READTRACKFILE*/
@@ -65,67 +74,10 @@ EXPORT const char * libDir;
static char * customPath = NULL;
static char * customPathBak = NULL;
-EXPORT char curPathName[STR_LONG_SIZE];
-EXPORT char * curFileName;
-EXPORT char curDirName[STR_LONG_SIZE];
-
EXPORT char * clipBoardN;
-EXPORT wBool_t executableOk = FALSE;
-
static int log_paramFile;
-/**
- * Get the directory from the current file and store it as current directory
- * in a global variable and the preferences
- *
- * \param pathType IN possible enhancement for file type specific directorys
- * \param fileName IN fully qualified filename
- * \return
- *
- * \todo split directory and keep directory part
- */
-
-void SetCurrentPath(
- const char * pathType,
- const char * fileName )
-{
- char *path;
- char *copy;
-
- assert( fileName != NULL );
- assert( pathType != NULL );
-
- copy = strdup( fileName );
- path = strrchr(copy, FILE_SEP_CHAR[ 0 ] );
- if ( path )
- {
- *path = '\0';
- strcpy( curDirName, copy );
- wPrefSetString( "file", "directory", curDirName );
- }
- free( copy );
-}
-
-/**
- * Find the filename/extension piece in a fully qualified path
- *
- * \param path IN the full path
- * \return pointer to the filename part
- */
-
-char *FindName( char *path )
-{
- char *name;
- name = strrchr( path, FILE_SEP_CHAR[0] );
- if (name) {
- name++;
- } else {
- name = path;
- }
- return(name );
-}
-
#ifdef WINDOWS
#define rename( F1, F2 ) Copyfile( F1, F2 )
@@ -197,7 +149,7 @@ RestoreLocale( char * locale )
*/
EXPORT FILE * paramFile = NULL;
-EXPORT char paramFileName[STR_LONG_SIZE];
+char *paramFileName;
EXPORT wIndex_t paramLineNum = 0;
EXPORT char paramLine[STR_LONG_SIZE];
EXPORT char * curContents;
@@ -568,11 +520,9 @@ EXPORT BOOL_T ReadParams(
char *oldLocale = NULL;
if (dirName) {
- strcpy( paramFileName, dirName );
- strcat( paramFileName, FILE_SEP_CHAR );
- strcat( paramFileName, fileName );
+ MakeFullpath(&paramFileName, dirName, fileName, NULL);
} else {
- strcpy( paramFileName, fileName );
+ MakeFullpath(&paramFileName, fileName, NULL);
}
paramLineNum = 0;
curBarScale = -1;
@@ -628,11 +578,9 @@ LOG1( log_paramFile, ("ReadParam( %s )\n", fileName ) )
paramLineNum = oldLineNum;
paramCheckSum = oldCheckSum;
if (dirName) {
- strcpy( paramFileName, dirName );
- strcat( paramFileName, FILE_SEP_CHAR );
- strcat( paramFileName, fileName );
+ MakeFullpath(&paramFileName, dirName, fileName, NULL);
} else {
- strcpy( paramFileName, fileName );
+ MakeFullpath(&paramFileName, fileName);
}
} else if (strncmp( paramLine, "CONTENTS ", 9) == 0 ) {
curContents = MyStrdup( paramLine+9 );
@@ -665,7 +613,8 @@ LOG1( log_paramFile, ("ReadParam( %s )\n", fileName ) )
}
}
if (paramFile)fclose( paramFile );
-
+ free(paramFileName);
+ paramFileName = NULL;
RestoreLocale( oldLocale );
return TRUE;
@@ -675,9 +624,7 @@ LOG1( log_paramFile, ("ReadParam( %s )\n", fileName ) )
static void ReadCustom( void )
{
FILE * f;
- customPath =
- (char*)MyMalloc( strlen(workingDir) + 1 + strlen(sCustomF) + 1 );
- sprintf( customPath, "%s%s%s", workingDir, FILE_SEP_CHAR, sCustomF );
+ MakeFullpath(&customPath, workingDir, sCustomF, NULL);
customPathBak = MyStrdup( customPath );
customPathBak[ strlen(customPathBak)-1 ] = '1';
f = fopen( customPath, "r" );
@@ -741,10 +688,14 @@ EXPORT char * PutTitle( char * cp )
void SetWindowTitle( void )
{
+ char *filename;
+
if ( changed > 2 || inPlayback )
return;
+
+ filename = GetLayoutFilename();
sprintf( message, "%s%s - %s(%s)",
- (curFileName==NULL||curFileName[0]=='\0')?_("Unnamed Trackplan"):curFileName,
+ (filename && filename[0])?filename: _("Unnamed Trackplan"),
changed>0?"*":"", sProdName, sVersion );
wWinSetTitle( mainW, message );
}
@@ -805,7 +756,7 @@ static BOOL_T ReadTrackFile(
}
paramLineNum = 0;
- strcpy( paramFileName, fileName );
+ paramFileName = strdup( fileName );
InfoMessage("0");
count = 0;
@@ -853,13 +804,9 @@ static BOOL_T ReadTrackFile(
if( !(ret = InputError( "unknown command", TRUE )))
break;
} else if (strncmp( paramLine, "TITLE1 ", 7 ) == 0) {
- strncpy( Title1, &paramLine[7], TITLEMAXLEN );
- Title1[ TITLEMAXLEN - 1 ] = '\0';
- /*wStringSetValue( title1PD.control, Title1 );*/
+ SetLayoutTitle(paramLine + 7);
} else if (strncmp( paramLine, "TITLE2 ", 7 ) == 0) {
- strncpy( Title2, &paramLine[7], TITLEMAXLEN );
- Title2[ TITLEMAXLEN - 1 ] = '\0';
- /*wStringSetValue( title2PD.control, Title2 );*/
+ SetLayoutSubtitle(paramLine + 7);
} else if (strncmp( paramLine, "ROOMSIZE", 8 ) == 0) {
if ( ParseRoomSize( paramLine+8, &roomSize ) ) {
SetRoomSize( roomSize );
@@ -895,8 +842,10 @@ static BOOL_T ReadTrackFile(
SetCurrentPath( LAYOUTPATHKEY, fileName );
if (full) {
- strcpy( curPathName, pathName );
- curFileName = &curPathName[fileName-pathName];
+// SetCurrentPath(LAYOUTPATHKEY, pathName);
+ SetLayoutFullPath(pathName);
+ //strcpy(curPathName, pathName);
+ //curFileName = &curPathName[fileName-pathName];
SetWindowTitle();
}
}
@@ -904,6 +853,9 @@ static BOOL_T ReadTrackFile(
RestoreLocale( oldLocale );
paramFile = NULL;
+
+ free(paramFileName);
+ paramFileName = NULL;
InfoMessage( "%d", count );
return ret;
}
@@ -921,9 +873,8 @@ EXPORT int LoadTracks(
assert( fileName != NULL );
assert( cnt == 1 );
- //if (fileName == NULL || cnt == 0 )
- // return TRUE;
+ SetCurrentPath(LAYOUTPATHKEY, fileName[0]);
paramVersion = -1;
wSetCursor( wCursorWait );
Reset();
@@ -935,7 +886,7 @@ EXPORT int LoadTracks(
#ifdef TIME_READTRACKFILE
time0 = wGetTimer();
#endif
- nameOfFile = FindName( fileName[ 0 ] );
+ nameOfFile = FindFilename( fileName[ 0 ] );
if (ReadTrackFile( fileName[ 0 ], nameOfFile, TRUE, FALSE, TRUE )) {
wMenuListAdd( fileList_ml, 0, nameOfFile, MyStrdup(fileName[0]) );
@@ -949,6 +900,7 @@ EXPORT int LoadTracks(
DoChangeNotification( CHANGE_ALL );
DoUpdateTitles();
LoadLayerLists();
+ LayerSetCounts();
}
UndoResume();
Reset();
@@ -997,10 +949,10 @@ static BOOL_T DoSaveTracks(
time(&clock);
rc &= fprintf(f,"#%s Version: %s, Date: %s\n", sProdName, sVersion, ctime(&clock) )>0;
rc &= fprintf(f, "VERSION %d %s\n", iParamVersion, PARAMVERSIONVERSION )>0;
- Stripcr( Title1 );
- Stripcr( Title2 );
- rc &= fprintf(f, "TITLE1 %s\n", Title1 )>0;
- rc &= fprintf(f, "TITLE2 %s\n", Title2 )>0;
+ Stripcr( GetLayoutTitle() );
+ Stripcr( GetLayoutSubtitle() );
+ rc &= fprintf(f, "TITLE1 %s\n", GetLayoutTitle())>0;
+ rc &= fprintf(f, "TITLE2 %s\n", GetLayoutSubtitle())>0;
rc &= fprintf(f, "MAPSCALE %ld\n", (long)mapD.scale )>0;
rc &= fprintf(f, "ROOMSIZE %0.6f x %0.6f\n", mapD.size.x, mapD.size.y )>0;
rc &= fprintf(f, "SCALE %s\n", curScaleName )>0;
@@ -1032,17 +984,15 @@ static int SaveTracks(
assert( fileName != NULL );
assert( cnt == 1 );
- SetCurrentPath( LAYOUTPATHKEY, fileName[ 0 ] );
+ SetCurrentPath(LAYOUTPATHKEY, fileName[0]);
DoSaveTracks( fileName[ 0 ] );
- nameOfFile = FindName( fileName[ 0 ] );
+ nameOfFile = FindFilename( fileName[ 0 ] );
wMenuListAdd( fileList_ml, 0, nameOfFile, MyStrdup(fileName[ 0 ]) );
checkPtMark = changed = 0;
- if (strcmp(curPathName, fileName[ 0 ]))
- strcpy( curPathName, fileName[ 0 ] );
- curFileName = FindName( curPathName );
-
+ SetLayoutFullPath(fileName[0]);
+
if (doAfterSave)
doAfterSave();
doAfterSave = NULL;
@@ -1053,13 +1003,13 @@ static int SaveTracks(
EXPORT void DoSave( doSaveCallBack_p after )
{
doAfterSave = after;
- if (curPathName[0] == '\0') {
+ if (*(GetLayoutFilename()) == '\0') {
if (saveFile_fs == NULL)
saveFile_fs = wFilSelCreate( mainW, FS_SAVE, 0, _("Save Tracks"),
sSourceFilePattern, SaveTracks, NULL );
- wFilSelect( saveFile_fs, curDirName );
+ wFilSelect( saveFile_fs, GetCurrentPath(LAYOUTPATHKEY));
} else {
- char *temp = curPathName;
+ char *temp = GetLayoutFullPath();
SaveTracks( 1, &temp, NULL );
}
SetWindowTitle();
@@ -1071,7 +1021,7 @@ EXPORT void DoSaveAs( doSaveCallBack_p after )
if (saveFile_fs == NULL)
saveFile_fs = wFilSelCreate( mainW, FS_SAVE, 0, _("Save Tracks As"),
sSourceFilePattern, SaveTracks, NULL );
- wFilSelect( saveFile_fs, curDirName );
+ wFilSelect( saveFile_fs, GetCurrentPath(LAYOUTPATHKEY));
SetWindowTitle();
}
@@ -1079,7 +1029,7 @@ EXPORT void DoLoad( void )
{
loadFile_fs = wFilSelCreate( mainW, FS_LOAD, 0, _("Open Tracks"),
sSourceFilePattern, LoadTracks, NULL );
- wFilSelect( loadFile_fs, curDirName );
+ wFilSelect( loadFile_fs, GetCurrentPath(LAYOUTPATHKEY));
}
@@ -1133,45 +1083,16 @@ EXPORT void CleanupFiles( void )
EXPORT int ExistsCheckpoint( void )
{
- int len;
- char *pattern = sCheckPointF;
- char *search;
-
struct stat fileStat;
- len = strlen( workingDir ) + 1 + strlen( sCheckPointF ) + 1;
- checkPtFileName1 = (char*)MyMalloc(len);
- sprintf( checkPtFileName1, "%s%s%s", workingDir, FILE_SEP_CHAR, sCheckPointF );
- checkPtFileName2 = (char*)MyMalloc(len);
- sprintf( checkPtFileName2, "%s%s%s", workingDir, FILE_SEP_CHAR, sCheckPoint1F );
-
- len = strlen( workingDir ) + 1 + strlen( pattern ) + 1;
- search = (char*)MyMalloc(len);
- sprintf( search, "%s%s%s", workingDir, FILE_SEP_CHAR, pattern );
-
- if( !stat( search, &fileStat ) ) {
- MyFree( search );
- return TRUE;
- } else {
- MyFree( search );
- return FALSE;
- }
-
+ MakeFullpath(&checkPtFileName1, workingDir, sCheckPointF, NULL);
+ MakeFullpath(&checkPtFileName2, workingDir, sCheckPoint1F, NULL);
-#ifdef LATER
- DIR *dir;
-
- dir = opendir( search );
- MyFree( search );
-
- if( dir ) {
- closedir( dir );
+ if( !stat( checkPtFileName1, &fileStat ) ) {
return TRUE;
} else {
return FALSE;
}
-#endif
-
}
/**
@@ -1183,16 +1104,12 @@ EXPORT int ExistsCheckpoint( void )
EXPORT int LoadCheckpoint( void )
{
- int len;
char *search;
paramVersion = -1;
wSetCursor( wCursorWait );
- len = strlen( workingDir ) + 1 + strlen( sCheckPointF ) + 1;
- search = (char*)MyMalloc(len);
- sprintf( search, "%s%s%s", workingDir, FILE_SEP_CHAR, sCheckPointF );
-
+ MakeFullpath(&search, workingDir, sCheckPointF, NULL);
UndoSuspend();
if (ReadTrackFile( search, search + strlen(search) - strlen( sCheckPointF ), TRUE, TRUE, TRUE )) {
@@ -1209,11 +1126,10 @@ EXPORT int LoadCheckpoint( void )
wSetCursor( wCursorNormal );
- strcpy( curPathName, "" );
- curFileName = curPathName;
+ SetLayoutFullPath("");
SetWindowTitle();
changed = TRUE;
- MyFree( search );
+ free( search );
return TRUE;
}
@@ -1238,7 +1154,7 @@ static int ImportTracks(
assert( fileName != NULL );
assert( cnt == 1 );
- nameOfFile = FindName(fileName[ 0 ]);
+ nameOfFile = FindFilename(fileName[ 0 ]);
paramVersion = -1;
wSetCursor( wCursorWait );
Reset();
@@ -1265,7 +1181,7 @@ EXPORT void DoImport( void )
importFile_fs = wFilSelCreate( mainW, FS_LOAD, 0, _("Import Tracks"),
sImportFilePattern, ImportTracks, NULL );
- wFilSelect( importFile_fs, curDirName );
+ wFilSelect( importFile_fs, GetCurrentPath(LAYOUTPATHKEY));
}
@@ -1326,7 +1242,7 @@ EXPORT void DoExport( void )
exportFile_fs = wFilSelCreate( mainW, FS_SAVE, 0, _("Export Tracks"),
sImportFilePattern, DoExportTracks, NULL );
- wFilSelect( exportFile_fs, curDirName );
+ wFilSelect( exportFile_fs, GetCurrentPath(LAYOUTPATHKEY));
}
@@ -1412,20 +1328,11 @@ EXPORT BOOL_T EditPaste( void )
EXPORT void FileInit( void )
{
- const char * pref;
-
if ( (libDir = wGetAppLibDir()) == NULL ) {
abort();
}
if ( (workingDir = wGetAppWorkDir()) == NULL )
AbortProg( "wGetAppWorkDir()" );
-
- pref = wPrefGetString( "file", "directory" );
- if (pref != NULL) {
- strcpy( curDirName, pref );
- } else {
- sprintf( curDirName, "%s%sexamples", libDir, FILE_SEP_CHAR );
- }
}
EXPORT BOOL_T ParamFileInit( void )
@@ -1441,10 +1348,8 @@ EXPORT BOOL_T ParamFileInit( void )
ReadCustom();
}
- curPathName[0] = '\0';
-
- clipBoardN = (char*)MyMalloc( strlen(workingDir) + 1 + strlen(sClipboardF) + 1 );
- sprintf( clipBoardN, "%s%s%s", workingDir, FILE_SEP_CHAR, sClipboardF );
+ SetLayoutFullPath("");
+ MakeFullpath(&clipBoardN, workingDir, sClipboardF, NULL);
return TRUE;
}
diff --git a/app/bin/fileio.h b/app/bin/fileio.h
index f574126..4f5aa8d 100644
--- a/app/bin/fileio.h
+++ b/app/bin/fileio.h
@@ -1,5 +1,4 @@
-/*
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/fileio.h,v 1.4 2008-01-15 11:46:03 mni77 Exp $
+/** \file fileio.h
*/
/* XTrkCad - Model Railroad CAD
@@ -23,8 +22,13 @@
#ifndef FILEIO_H
#define FILEIO_H
+#include <stdio.h>
+
+#include "common.h"
+#include "misc.h"
+
FILE * paramFile;
-char paramFileName[STR_LONG_SIZE];
+extern char *paramFileName;
wIndex_t paramLineNum;
char paramLine[STR_LONG_SIZE];
char * curContents;
@@ -37,10 +41,6 @@ typedef BOOL_T (*readParam_t) ( char * );
extern const char * workingDir;
extern const char * libDir;
-extern char curPathName[STR_LONG_SIZE];
-extern char * curFileName;
-extern char curDirName[STR_LONG_SIZE];
-
#define PARAM_CUSTOM (-2)
#define PARAM_LAYOUT (-3)
extern int curParamFileIndex;
@@ -57,6 +57,8 @@ int curDemo;
wMenuList_p fileList_ml;
+#define PARAM_SUBDIR "params"
+
#define LAYOUTPATHKEY "layout"
#define BITMAPPATHKEY "bitmap"
#define DXFPATHKEY "dxf"
@@ -65,8 +67,7 @@ wMenuList_p fileList_ml;
#define PARAMETERPATHKEY "params"
#define IMPORTPATHKEY "import"
#define MACROPATHKEY "macro"
-
-void SetCurrentPath( const char *, const char * );
+#define CUSTOMPATHKEY "custom"
void Stripcr( char * );
char * GetNextLine( void );
diff --git a/app/bin/i18n.c b/app/bin/i18n.c
index ff4e28d..a8ed631 100644
--- a/app/bin/i18n.c
+++ b/app/bin/i18n.c
@@ -19,11 +19,12 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-#include "i18n.h"
-#include "wlib.h"
-
#include <locale.h>
#include <stdio.h>
+#include <stdlib.h>
+
+#include "i18n.h"
+#include "wlib.h"
/**
* Initialize gettext environment. By default, the language files are installed
diff --git a/app/bin/layout.c b/app/bin/layout.c
new file mode 100644
index 0000000..0328474
--- /dev/null
+++ b/app/bin/layout.c
@@ -0,0 +1,375 @@
+/** \file layout.c
+ * Layout data and dialog
+ */
+
+/* XTrkCad - Model Railroad CAD
+ * Copyright (C) 2017 Martin Fischer
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, 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 License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <dynstring.h>
+
+#include "custom.h"
+#include "i18n.h"
+#include "layout.h"
+#include "misc2.h"
+#include "param.h"
+#include "paths.h"
+#include "track.h"
+#include "wlib.h"
+
+#define MINTRACKRADIUSPREFS "minTrackRadius"
+
+struct sLayoutProps {
+ char title1[TITLEMAXLEN];
+ char title2[TITLEMAXLEN];
+ SCALEINX_T curScaleInx;
+ SCALEDESCINX_T curScaleDescInx;
+ GAUGEINX_T curGaugeInx;
+ DIST_T minTrackRadius;
+ DIST_T maxTrackGrade;
+ coOrd roomSize;
+};
+
+struct sDataLayout {
+ struct sLayoutProps props;
+ DynString fullFileName;
+ struct sLayoutProps *copyOfLayoutProps;
+};
+
+struct sDataLayout thisLayout = {
+ { "", "", -1, 0, 0, 0.0, 5.0, {0.0, 0.0} },
+ NaS,
+ NULL,
+};
+
+static paramFloatRange_t r0_90 = { 0, 90 };
+static paramFloatRange_t r1_10000 = { 1, 10000 };
+static paramFloatRange_t r1_9999999 = { 1, 9999999 };
+
+static void LayoutDlgUpdate(paramGroup_p pg, int inx, void * valueP);
+
+/**
+* Update the full file name. Do not do anything if the new filename is identical to the old one.
+*
+* \param filename IN the new filename
+*/
+
+void
+SetLayoutFullPath(const char *fileName)
+{
+ if (DynStringToCStr(&thisLayout.fullFileName) != fileName) {
+ if (isnas(&thisLayout.fullFileName)) {
+ DynStringMalloc(&thisLayout.fullFileName, strlen(fileName) + 1);
+ } else {
+ DynStringClear(&thisLayout.fullFileName);
+ }
+
+ DynStringCatCStr(&thisLayout.fullFileName, fileName);
+ }
+}
+
+/**
+* Set the minimum radius for the selected scale/gauge into the dialog
+*
+* \param scaleName IN name of the scale/gauge eg. HOn3
+* \param defaltValue IN default value will be used if no preference is set
+*/
+
+void
+LoadLayoutMinRadiusPref(char *scaleName, double defaultValue)
+{
+ DynString prefString = { NULL };
+
+ DynStringPrintf(&prefString, MINTRACKRADIUSPREFS "-%s", scaleName);
+ wPrefGetFloat("misc", DynStringToCStr(&prefString),
+ &thisLayout.props.minTrackRadius, defaultValue);
+ DynStringFree(&prefString);
+}
+
+static void
+CopyLayoutTitle(char* dest, char *src)
+{
+ strncpy(dest, src, TITLEMAXLEN);
+ *(dest + TITLEMAXLEN - 1) = '\0';
+}
+
+void
+SetLayoutTitle(char *title)
+{
+ CopyLayoutTitle(thisLayout.props.title1, title);
+}
+
+void
+SetLayoutSubtitle(char *title)
+{
+ CopyLayoutTitle(thisLayout.props.title2, title);
+}
+
+void
+SetLayoutMinTrackRadius(DIST_T radius)
+{
+ thisLayout.props.minTrackRadius = radius;
+}
+
+void
+SetLayoutMaxTrackGrade(ANGLE_T angle)
+{
+ thisLayout.props.maxTrackGrade = angle;
+}
+
+
+void
+SetLayoutRoomSize(coOrd size)
+{
+ thisLayout.props.roomSize = size;
+}
+
+void
+SetLayoutCurScale(SCALEINX_T scale)
+{
+ thisLayout.props.curScaleInx = scale;
+}
+
+void
+SetLayoutCurScaleDesc(SCALEDESCINX_T desc)
+{
+ thisLayout.props.curScaleDescInx = desc;
+}
+
+void
+SetLayoutCurGauge(GAUGEINX_T gauge)
+{
+ thisLayout.props.curGaugeInx = gauge;
+}
+
+/**
+* Return the full filename.
+*
+* \return pointer to the full filename, should not be modified or freed
+*/
+
+char *
+GetLayoutFullPath()
+{
+ return (DynStringToCStr(&thisLayout.fullFileName));
+}
+
+/**
+* Return the filename part of the full path
+*
+* \return pointer to the filename part, NULL is no filename is set
+*/
+
+char *
+GetLayoutFilename()
+{
+ char *string = DynStringToCStr(&thisLayout.fullFileName);
+
+ if (string) {
+ return (FindFilename(string));
+ } else {
+ return (NULL);
+ }
+}
+
+char *
+GetLayoutTitle()
+{
+ return (thisLayout.props.title1);
+}
+
+char *
+GetLayoutSubtitle()
+{
+ return (thisLayout.props.title2);
+}
+
+DIST_T
+GetLayoutMinTrackRadius()
+{
+ return (thisLayout.props.minTrackRadius);
+}
+
+ANGLE_T
+GetLayoutMaxTrackGrade()
+{
+ return (thisLayout.props.maxTrackGrade);
+}
+
+SCALEDESCINX_T
+GetLayoutCurScaleDesc()
+{
+ return (thisLayout.props.curScaleDescInx);
+}
+
+SCALEINX_T
+GetLayoutCurScale()
+{
+ return (thisLayout.props.curScaleInx);
+}
+/****************************************************************************
+*
+* Layout Dialog
+*
+*/
+
+static wWin_p layoutW;
+
+static paramData_t layoutPLs[] = {
+ { PD_FLOAT, &thisLayout.props.roomSize.x, "roomsizeX", PDO_NOPREF | PDO_DIM | PDO_NOPSHUPD | PDO_DRAW, &r1_9999999, N_("Room Width"), 0, (void*)(CHANGE_MAIN | CHANGE_MAP) },
+ { PD_FLOAT, &thisLayout.props.roomSize.y, "roomsizeY", PDO_NOPREF | PDO_DIM | PDO_NOPSHUPD | PDO_DRAW | PDO_DLGHORZ, &r1_9999999, N_(" Height"), 0, (void*)(CHANGE_MAIN | CHANGE_MAP) },
+ { PD_STRING, &thisLayout.props.title1, "title1", PDO_NOPSHUPD, NULL, N_("Layout Title"), 0, (void *)sizeof(thisLayout.props.title1) },
+ { PD_STRING, &thisLayout.props.title2, "title2", PDO_NOPSHUPD, NULL, N_("Subtitle"), 0, (void *)sizeof(thisLayout.props.title2) },
+#define SCALEINX (4)
+ { PD_DROPLIST, &thisLayout.props.curScaleDescInx, "scale", PDO_NOPREF | PDO_NOPSHUPD | PDO_NORECORD | PDO_NOUPDACT, (void *)120, N_("Scale"), 0, (void*)(CHANGE_SCALE) },
+#define GAUGEINX (5)
+ { PD_DROPLIST, &thisLayout.props.curGaugeInx, "gauge", PDO_NOPREF | PDO_NOPSHUPD | PDO_NORECORD | PDO_NOUPDACT | PDO_DLGHORZ, (void *)120, N_(" Gauge"), 0, (void *)(CHANGE_SCALE) },
+#define MINRADIUSENTRY (6)
+ { PD_FLOAT, &thisLayout.props.minTrackRadius, "mintrackradius", PDO_DIM | PDO_NOPSHUPD | PDO_NOPREF, &r1_10000, N_("Min Track Radius"), 0, (void*)(CHANGE_MAIN | CHANGE_LIMITS) },
+ { PD_FLOAT, &thisLayout.props.maxTrackGrade, "maxtrackgrade", PDO_NOPSHUPD | PDO_DLGHORZ, &r0_90, N_(" Max Track Grade (%)"), 0, (void*)(CHANGE_MAIN) }
+};
+
+static paramGroup_t layoutPG = { "layout", PGO_RECORD | PGO_PREFMISC, layoutPLs, sizeof layoutPLs / sizeof layoutPLs[0] };
+
+/**
+* Apply the changes entered to settings
+*
+* \param junk IN unused
+*/
+
+static void LayoutOk(void * junk)
+{
+ long changes;
+
+ changes = GetChanges(&layoutPG);
+
+ /* [mf Nov. 15, 2005] Get the gauge/scale settings */
+ if (changes & CHANGE_SCALE) {
+ SetScaleGauge(thisLayout.props.curScaleDescInx, thisLayout.props.curGaugeInx);
+ }
+
+ /* [mf Nov. 15, 2005] end */
+
+ if (changes & CHANGE_MAP) {
+ SetRoomSize(thisLayout.props.roomSize);
+ }
+
+ DoChangeNotification(changes);
+
+ if (changes & CHANGE_LIMITS) {
+ char prefString[30];
+ // now set the minimum track radius
+ sprintf(prefString, "minTrackRadius-%s", curScaleName);
+ wPrefSetFloat("misc", prefString, thisLayout.props.minTrackRadius);
+ }
+
+ free(thisLayout.copyOfLayoutProps);
+ wHide(layoutW);
+}
+
+/**
+* Discard the changes entered and replace with earlier values
+*
+* \param junk IN unused
+*/
+
+static void LayoutCancel(struct wWin_t *junk)
+{
+ thisLayout.props = *(thisLayout.copyOfLayoutProps);
+ ParamLoadControls(&layoutPG);
+ LayoutOk(junk);
+}
+
+static void LayoutChange(long changes)
+{
+ if (changes & (CHANGE_SCALE | CHANGE_UNITS))
+ if (layoutW != NULL && wWinIsVisible(layoutW)) {
+ ParamLoadControls(&layoutPG);
+ }
+}
+
+void DoLayout(void * junk)
+{
+ thisLayout.props.roomSize = mapD.size;
+
+ if (layoutW == NULL) {
+ layoutW = ParamCreateDialog(&layoutPG, MakeWindowTitle(_("Layout Options")),
+ _("Ok"), LayoutOk, LayoutCancel, TRUE, NULL, 0, LayoutDlgUpdate);
+ LoadScaleList((wList_p)layoutPLs[4].control);
+ }
+
+ LoadGaugeList((wList_p)layoutPLs[5].control,
+ thisLayout.props.curScaleDescInx); /* set correct gauge list here */
+ thisLayout.copyOfLayoutProps = malloc(sizeof(struct sLayoutProps));
+
+ if (!thisLayout.copyOfLayoutProps) {
+ exit(1);
+ }
+
+ *(thisLayout.copyOfLayoutProps) = thisLayout.props;
+
+ ParamLoadControls(&layoutPG);
+ wShow(layoutW);
+}
+
+EXPORT addButtonCallBack_t LayoutInit(void)
+{
+ ParamRegister(&layoutPG);
+ RegisterChangeNotification(LayoutChange);
+ return &DoLayout;
+}
+
+/**
+* Update the dialog when scale was changed. The list of possible gauges for the selected scale is
+* updated and the first entry is selected (usually standard gauge). After this the minimum gauge
+* is set from the preferences.
+*
+* \param pg IN dialog
+* \param inx IN changed entry field
+* \param valueP IN new value
+*/
+
+static void
+LayoutDlgUpdate(
+ paramGroup_p pg,
+ int inx,
+ void * valueP)
+{
+ /* did the scale change ? */
+ if (inx == SCALEINX) {
+ char prefString[100];
+ char scaleDesc[100];
+
+ LoadGaugeList((wList_p)layoutPLs[GAUGEINX].control, *((int *)valueP));
+ // set the first entry as default, usually the standard gauge for a scale
+ wListSetIndex((wList_p)layoutPLs[GAUGEINX].control, 0);
+
+ // get the minimum radius
+ // get the selected scale first
+ wListGetValues((wList_p)layoutPLs[SCALEINX].control, scaleDesc, 99, NULL, NULL);
+ strtok(scaleDesc, " ");
+
+ // now get the minimum track radius
+ sprintf(prefString, "minTrackRadius-%s", scaleDesc);
+ wPrefGetFloat("misc", prefString, &thisLayout.props.minTrackRadius, 0.0);
+
+ // put the scale's minimum value into the dialog
+ wStringSetValue((wString_p)layoutPLs[MINRADIUSENTRY].control,
+ FormatDistance(thisLayout.props.minTrackRadius));
+ }
+}
diff --git a/app/bin/layout.h b/app/bin/layout.h
new file mode 100644
index 0000000..4a581df
--- /dev/null
+++ b/app/bin/layout.h
@@ -0,0 +1,56 @@
+/** \file layout.h
+ * Layout data
+ */
+
+/* XTrkCad - Model Railroad CAD
+ * Copyright (C) 2017 Martin Fischer
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, 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 License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef HAVE_LAYOUT_H
+#define HAVE_LAYOUT_H
+
+#include "common.h"
+#include "misc.h"
+
+
+void SetLayoutFullPath(const char *fileName);
+void LoadLayoutMinRadiusPref(char *scaleName, double defaultValue);
+void SetLayoutTitle(char *title);
+void SetLayoutSubtitle(char *title);
+void SetLayoutMinTrackRadius(DIST_T radius);
+void SetLayoutMaxTrackGrade(ANGLE_T angle);
+void SetLayoutRoomSize(coOrd size);
+void SetLayoutCurScale(SCALEINX_T scale);
+void SetLayoutCurScaleDesc(SCALEDESCINX_T desc);
+void SetLayoutCurGauge(GAUGEINX_T gauge);
+void SetLayoutScaleGauge(SCALEDESCINX_T desc, GAUGEINX_T gauge);
+
+char *GetLayoutFullPath(void);
+char *GetLayoutFilename(void);
+char *GetLayoutTitle(void);
+char *GetLayoutSubtitle(void);
+DIST_T GetLayoutMinTrackRadius(void);
+SCALEINX_T GetLayoutCurScale(void );
+SCALEDESCINX_T GetLayoutCurScaleDesc(void);
+//GAUGEINX_T GetLayoutCurGauge(void);
+
+
+ANGLE_T GetLayoutMaxTrackGrade(void);
+SCALEDESCINX_T GetLayoutCurScaleDesc(void);
+
+void DoLayout(void * junk);
+#endif \ No newline at end of file
diff --git a/app/bin/lprintf.c b/app/bin/lprintf.c
index c0f1c00..217d904 100644
--- a/app/bin/lprintf.c
+++ b/app/bin/lprintf.c
@@ -1,5 +1,5 @@
-/*
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/lprintf.c,v 1.2 2006-05-26 17:31:44 m_fischer Exp $
+/** \file lprintf.c
+ * Logging functions
*/
/* XTrkCad - Model Railroad CAD
@@ -23,6 +23,7 @@
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <errno.h>
#ifndef WINDOWS
#include <time.h>
@@ -30,8 +31,14 @@
#include <time.h>
#include <sys/timeb.h>
#endif
+
+#include "custom.h"
+#include "fileio.h"
+#include "messages.h"
+#include "paths.h"
#include "track.h"
+
/****************************************************************************
*
* LPRINTF
@@ -72,8 +79,7 @@ static void LogDoOpen( void )
{
if ( logFileName == NULL ) {
#ifdef WINDOWS
- logFileName = (char*)MyMalloc( strlen(wGetAppWorkDir()) + 1 + strlen("xtclog.txt") + 1);
- sprintf( logFileName, "%s%s%s", wGetAppWorkDir(), FILE_SEP_CHAR, "xtclog.txt" );
+ MakeFullpath(&logFileName, wGetAppWorkDir(), "xtclog.txt", NULL);
#else
logFile = stdout;
#endif
@@ -115,7 +121,7 @@ EXPORT void LogSet( char * name, int level )
EXPORT int LogFindIndex( char * name )
{
int inx;
- for ( inx=11; inx<logTable_da.cnt; inx++ )
+ for ( inx=0; inx<logTable_da.cnt; inx++ )
if ( strcasecmp( logTable(inx).name, name ) == 0 )
return inx;
return 0;
diff --git a/app/bin/macro.c b/app/bin/macro.c
index 2147eff..1c711ce 100644
--- a/app/bin/macro.c
+++ b/app/bin/macro.c
@@ -20,6 +20,7 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
+#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#ifndef WINDOWS
@@ -47,14 +48,20 @@
#include <stdint.h>
-#include "track.h"
-#include "version.h"
#include "common.h"
-#include "utility.h"
-#include "draw.h"
-#include "misc.h"
#include "compound.h"
+#include "cundo.h"
+#include "custom.h"
+#include "draw.h"
+#include "fileio.h"
#include "i18n.h"
+#include "messages.h"
+#include "misc.h"
+#include "param.h"
+#include "paths.h"
+#include "track.h"
+#include "utility.h"
+#include "version.h"
EXPORT long adjTimer;
static void DemoInitValues( void );
@@ -268,7 +275,7 @@ EXPORT void DoRecord( void * context )
recordFile_fs = wFilSelCreate( mainW, FS_SAVE, 0, title, sRecordFilePattern, StartRecord, NULL );
}
wTextClear( recordT );
- wFilSelect( recordFile_fs, curDirName );
+ wFilSelect( recordFile_fs, GetCurrentPath(MACROPATHKEY ));
}
/*****************************************************************************
@@ -716,11 +723,7 @@ EXPORT void TakeSnapshot( drawCmd_t * d )
wBitMapDelete( d->d );
documentSnapshotNum++;
if (documentCopy && documentFile) {
- cp = strrchr( message, FILE_SEP_CHAR[0] );
- if (cp == 0)
- cp = message;
- else
- cp++;
+ cp = FindFilename(message);
cp[strlen(cp)-4] = 0;
fprintf( documentFile, "\n?G%s\n", cp );
}
@@ -802,6 +805,7 @@ static void Playback( void )
static wBool_t demoWinOnTop = FALSE;
coOrd roomSize;
char * cp, * cq;
+ char *demoFileName = NULL;
useCurrentLayer = FALSE;
inPlayback = TRUE;
@@ -829,13 +833,13 @@ static void Playback( void )
Reset();
if (curDemo < 0 || curDemo >= demoList_da.cnt)
break;
- strcpy( paramFileName, demoList(curDemo).fileName );
- paramFile = fopen( paramFileName, "r" );
+ demoFileName = strdup(demoList(curDemo).fileName );
+ paramFile = fopen( demoFileName, "r" );
if ( paramFile == NULL ) {
- NoticeMessage( MSG_OPEN_FAIL, _("Continue"), NULL, _("Demo"), paramFileName, strerror(errno) );
+ NoticeMessage( MSG_OPEN_FAIL, _("Continue"), NULL, _("Demo"), demoFileName, strerror(errno) );
return;
}
-
+
playbackColor=wDrawColorBlack;
paramLineNum = 0;
wWinSetTitle( demoW, demoList( curDemo ).title );
@@ -848,11 +852,13 @@ static void Playback( void )
DoChangeNotification( CHANGE_ALL );
CompoundClearDemoDefns();
if ( fgets(paramLine, STR_LONG_SIZE, paramFile) == NULL ) {
- NoticeMessage( MSG_CANT_READ_DEMO, _("Continue"), NULL, sProdName, paramFileName );
+ NoticeMessage( MSG_CANT_READ_DEMO, _("Continue"), NULL, sProdName, demoFileName );
fclose( paramFile );
paramFile = NULL;
return;
}
+ free(demoFileName);
+ demoFileName = NULL;
}
if (paramLineNum == 0) {
documentSnapshotNum = 1;
@@ -950,38 +956,15 @@ static void Playback( void )
tempD.orig = mainD.orig;
tempD.size = mainD.size;
tempD.scale = mainD.scale;
-#ifdef LATER
- ResolveIndex();
- RecomputeElevations();
-#endif
+
DoRedraw();
if (playbackD != NULL && playbackBm != NULL)
MacroDrawBitMap( playbackD, playbackBm, playbackX, playbackY, wDrawColorBlack );
-#ifdef LATER
- } else if (strncmp( paramLine, "POSITION ", 9 ) == 0) {
- if ( !GetArgs( paramLine+9, "ff", &x, &y ) )
- continue;
- MovePlaybackCursor( &mainD, x, y );
-#endif
+
} else if (strncmp( paramLine, "PAUSE ", 6 ) == 0) {
paramTogglePlaybackHilite = TRUE;
didPause = TRUE;
-#ifdef DOPAUSE
- if (lastCmd == mouseCmd) {
- thisCmd = pauseCmd;
- } else {
- if ( !GetArgs( paramLine+6, "l", &timeout ) )
- continue;
-#ifdef LATER
- wFlush();
- wAlarm( timeout*playbackDelay/100, playback );
- return;
-#else
- if (playbackTimer == 0)
- wPause( timeout*playbackDelay/100 );
-#endif
- }
-#endif
+
if ( !GetArgs( paramLine+6, "l", &timeout ) )
continue;
if (timeout > 10000)
@@ -996,18 +979,13 @@ static void Playback( void )
} else if (strncmp( paramLine, "BIGPAUSE ", 6 ) == 0) {
paramTogglePlaybackHilite = TRUE;
didPause = FALSE;
-#ifdef LATER
- wFlush();
- wAlarm( bigPause*playbackDelay/100, playback );
- return;
-#else
+
if (playbackTimer == 0) {
timeout = bigPause*playbackDelay/100;
if (timeout <= dragTimeout)
timeout = dragTimeout+1;
wPause( timeout );
}
-#endif
} else if (strncmp( paramLine, "KEYSTATE ", 9 ) == 0 ) {
playbackKeyState = atoi( paramLine+9 );
} else if (strncmp( paramLine, "TIMESTART", 9 ) == 0 ) {
@@ -1059,49 +1037,9 @@ static void Playback( void )
DemoInitValues();
} else {
if (strncmp( paramLine, "MOUSE ", 6 ) == 0) {
-#ifdef LATER
- if ( GetArgs( paramLine+6, "dff", &rc, &pos.x, &pos.y) ) {
- pos.x = pos.x / mainD.scale - mainD.orig.x;
- pos.y = pos.y / mainD.scale - mainD.orig.y;
-#ifdef DOPAUSE
- if (lastCmd == pauseCmd) {
-#endif
- d = sqrt( (pos.x-mainPos.x)*(pos.x-mainPos.x) +
- (pos.y-mainPos.y)*(pos.y-mainPos.y) );
- d *= mainD.dpi;
- timeout = (long)(MSEC_PER_PIXEL * d);
- if (timeout > 2)
- if (playbackTimer == 0)
- wPause( timeout );
-#ifdef DOPAUSE
- }
-#endif
- mainPos = pos;
- }
-#endif
thisCmd = mouseCmd;
}
if (strncmp( paramLine, "MAP ", 6 ) == 0) {
-#ifdef LATER
- if ( GetArgs( paramLine+6, "dff", &rc, &pos.x, &pos.y ) ) {
- pos.x = pos.x / mapD.scale - mapD.orig.x;
- pos.y = pos.y / mapD.scale - mapD.orig.y;
-#ifdef DOPAUSE
- if (lastCmd == pauseCmd) {
-#endif
- d = sqrt( (pos.x-mapPos.y)*(pos.x-mapPos.x) +
- (pos.y-mapPos.y)*(pos.y-mapPos.y) );
- d *= mapD.dpi;
- timeout = (long)(MSEC_PER_PIXEL * d);
- if (timeout > 2)
- if (playbackTimer == 0)
- wPause( timeout );
-#ifdef DOPAUSE
- }
-#endif
- mapPos = pos;
- }
-#endif
thisCmd = mouseCmd;
}
for ( inx=0; inx<playbackProc_da.cnt; inx++ ) {
@@ -1241,7 +1179,7 @@ EXPORT void DoPlayBack( void * context )
if (demoW == NULL)
CreateDemoW();
wButtonSetLabel( demoNext, _("Save") );
- wFilSelect( playbackFile_fs, curDirName );
+ wFilSelect( playbackFile_fs, GetCurrentPath(MACROPATHKEY));
}
@@ -1380,6 +1318,7 @@ static BOOL_T ReadDemo(
static wMenu_p m;
char * cp;
char *oldLocale = NULL;
+ char *path;
if ( m == NULL )
m = demoM;
@@ -1405,10 +1344,8 @@ static BOOL_T ReadDemo(
if (userLocale)
oldLocale = SaveLocale(userLocale);
demoList( demoList_da.cnt-1 ).title = MyStrdup( _(line+6) );
- demoList( demoList_da.cnt-1 ).fileName =
- (char*)MyMalloc( strlen(libDir) + 1 + 5 + 1 + strlen(cp) + 1 );
- sprintf( demoList( demoList_da.cnt-1 ).fileName, "%s%s%s%s%s",
- libDir, FILE_SEP_CHAR, "demos", FILE_SEP_CHAR, cp );
+ MakeFullpath(&path, libDir, "demos", cp, NULL);
+ demoList(demoList_da.cnt - 1).fileName = path;
wMenuPushCreate( m, NULL, _(line+6), 0, DoDemo, (void*)(intptr_t)(demoList_da.cnt-1) );
if (oldLocale)
RestoreLocale(oldLocale);
diff --git a/app/bin/misc.c b/app/bin/misc.c
index b506f98..0422631 100644
--- a/app/bin/misc.c
+++ b/app/bin/misc.c
@@ -45,20 +45,27 @@
#else
#include <sys/stat.h>
#endif
+#include <locale.h>
#include <stdarg.h>
-
#include <stdint.h>
-#include "track.h"
+#include "cjoin.h"
#include "common.h"
-#include "utility.h"
+#include "compound.h"
+#include "cselect.h"
+#include "cundo.h"
+#include "custom.h"
#include "draw.h"
+#include "fileio.h"
+#include "i18n.h"
+#include "layout.h"
+#include "messages.h"
#include "misc.h"
-#include "cjoin.h"
-#include "compound.h"
+#include "param.h"
+#include "paths.h"
#include "smalldlg.h"
-#include "i18n.h"
-#include <locale.h>
+#include "track.h"
+#include "utility.h"
#define DEFAULT_SCALE ("N")
@@ -89,7 +96,7 @@ EXPORT wWin_p mainW;
EXPORT wIndex_t changed = 0;
-EXPORT char FAR message[STR_LONG_SIZE];
+EXPORT char message[STR_LONG_SIZE];
static char message2[STR_LONG_SIZE];
EXPORT REGION_T curRegion = 0;
@@ -107,6 +114,7 @@ EXPORT wButton_p redoB;
EXPORT wButton_p zoomUpB;
EXPORT wButton_p zoomDownB;
+wButton_p mapShowB;
EXPORT wIndex_t checkPtMark = 0;
@@ -286,6 +294,95 @@ EXPORT char * MyStrdup( const char * str )
return ret;
}
+/*
+ * Convert Text into the equivalent form that can be written to a file or put in a text box by adding escape characters
+ *
+ * The following special characters are produced -
+ * \n for LineFeed 0x0A
+ * \t for Tab 0x09
+ * "" for " This is so that a CSV conformant type program can interpret the file output
+ *
+ */
+EXPORT char * ConvertToEscapedText(const char * text) {
+ int text_i=0;
+ int add = 0; //extra chars for escape
+ while(text[text_i]) {
+ switch (text[text_i]) {
+ case '\n': add++; break;
+ case '\t': add++; break;
+ case '\\': add++; break;
+ case '\"': add++; break;
+ }
+ text_i++;
+ }
+ char * cout = MyMalloc(strlen(text)+1+add);
+ int cout_i = 0;
+ text_i = 0;
+ while(text[text_i]) {
+ char c = text[text_i];
+ switch (c) {
+ case '\n': cout[cout_i] = '\\'; cout_i++; cout[cout_i] = 'n'; cout_i++; break; // Line Feed
+ case '\t': cout[cout_i] = '\\'; cout_i++; cout[cout_i] = 't'; cout_i++; break; // Tab
+ case '\\': cout[cout_i] = '\\'; cout_i++; cout[cout_i] = '\\'; cout_i++; break; // BackSlash
+ case '\"': cout[cout_i] = '\"'; cout_i++; cout[cout_i] = '\"'; cout_i++; break; // Double Quotes
+ default: cout[cout_i] = c; cout_i++;
+ }
+ text_i++;
+ }
+ cout[cout_i] = '\0';
+ return cout;
+}
+
+/*
+ * Convert Text that has embedded escape characters into the equivalent form that can be shown on the screen
+ *
+ * The following special characters are supported -
+ * \n = LineFeed 0x0A
+ * \t = Tab 0x09
+ * \\ = \ The way to still produce backslash
+ * "" = " Take out quotes included so that other (CSV-like) programs could read the files
+ *
+ */
+EXPORT char * ConvertFromEscapedText(const char * text) {
+ enum { CHARACTER, ESCAPE, QUOTE } state = CHARACTER;
+ char * cout = MyMalloc(strlen(text)+1); //always equal to or shorter than
+ int text_i = 0;
+ int cout_i = 0;
+ int c;
+ while (text[text_i]) {
+ c = text[text_i];
+ switch (state) {
+ case CHARACTER:
+ if (c == '\\') {
+ state = ESCAPE;
+ } else if (c == '\"') {
+ state = QUOTE;
+ } else {
+ cout[cout_i] = c;
+ cout_i++;
+ }
+ break;
+
+ case ESCAPE:
+ switch (c) {
+ case '\\': cout[cout_i] = '\\'; cout_i++; break; // "\\" = "\"
+ case 'n': cout[cout_i] = '\n'; cout_i++; break; // LF
+ case 't': cout[cout_i] = '\t'; cout_i++; break; // TAB
+ }
+ state = CHARACTER;
+ break;
+ case QUOTE:
+ switch(c) {
+ case '\"': cout[cout_i] = c; cout_i++; break; //One quote = NULL, Two quotes = 1 quote
+ }
+ state = CHARACTER;
+ }
+ text_i++;
+ }
+ cout[cout_i] = '\0';
+ return cout;
+}
+
EXPORT void AbortProg(
char * msg,
@@ -482,7 +579,8 @@ static void ChkRevert( void )
_("&Revert"), _("&Cancel") );
if( rc ) {
/* load the file */
- LoadTracks( 1, &curFileName, NULL );
+ char *filename = GetLayoutFullPath();
+ LoadTracks( 1, &filename, NULL );
}
}
}
@@ -518,7 +616,7 @@ EXPORT void SaveState( void )
RememberParamFiles();
ParamUpdatePrefs();
- wPrefSetString( "misc", "lastlayout", curPathName );
+ wPrefSetString( "misc", "lastlayout", GetLayoutFullPath());
if ( fileList_ml ) {
strcpy( file, "file" );
@@ -537,17 +635,14 @@ EXPORT void SaveState( void )
}
/*
- * Clean up befor quitting
+ * Clean up before quitting
*/
-static int quitting;
static void DoQuitAfter( void )
{
changed = 0;
SaveState();
CleanupFiles();
-
- quitting = TRUE;
}
/**
* Process shutdown request. This function is called when the user requests
@@ -557,28 +652,26 @@ static void DoQuitAfter( void )
void DoQuit( void )
{
Confirm(_("Quit"), DoQuitAfter );
- if ( quitting ) {
#ifdef CHECK_UNUSED_BALLOONHELP
- ShowUnusedBalloonHelp();
+ ShowUnusedBalloonHelp();
#endif
- LogClose();
- wExit(0);
- }
+ LogClose();
+ wExit(0);
}
static void DoClearAfter( void )
{
+
ClearTracks();
/* set all layers to their default properties and set current layer to 0 */
DefaultLayerProperties();
-
+ DoLayout(NULL);
checkPtMark = 0;
Reset();
DoChangeNotification( CHANGE_MAIN|CHANGE_MAP );
EnableCommands();
- curPathName[0] = '\0';
- curFileName = curPathName;
+ SetLayoutFullPath("");
SetWindowTitle();
}
@@ -591,24 +684,29 @@ static void DoClear( void )
* Toggle visibility state of map window.
*/
-void MapWindowToggleShow( void )
+void MapWindowToggleShow(void)
{
- MapWindowShow( !mapVisible );
+ MapWindowShow(!mapVisible);
}
/**
* Set visibility state of map window.
+ *
+ * \param state IN TRUE if visible, FALSE if hidden
*/
-void MapWindowShow( int state )
+void MapWindowShow(int state)
{
- mapVisible = state;
- wPrefSetInteger( "misc", "mapVisible", mapVisible );
- wMenuToggleSet( mapShowMI, mapVisible );
- if( mapVisible )
- DoChangeNotification( CHANGE_MAP );
+ mapVisible = state;
+ wPrefSetInteger("misc", "mapVisible", mapVisible);
+ wMenuToggleSet(mapShowMI, mapVisible);
- wWinShow( mapW, mapVisible );
+ if (mapVisible) {
+ DoChangeNotification(CHANGE_MAP);
+ }
+
+ wWinShow(mapW, mapVisible);
+ wButtonSetBusy(mapShowB, (wBool_t)mapVisible);
}
static void DoShowWindow(
@@ -619,6 +717,7 @@ static void DoShowWindow(
if (data == mapW) {
if (mapVisible == FALSE) {
MapWindowShow( TRUE );
+ return;
}
}
wWinShow( (wWin_p)data, TRUE );
@@ -717,25 +816,6 @@ EXPORT void SelectFont( void )
#define BUTTON_MAX (170)
#define NUM_CMDMENUS (4)
-#ifdef LATER
-static struct {
- addButtonCallBack_t actionProc;
- procCommand_t cmdProc;
- char * helpStr;
- wControl_p control;
- char * labelStr;
- int reqLevel;
- wBool_t enabled;
- wPos_t x, y;
- long options;
- long stickyMask;
- int group;
- long acclKey;
- wMenuPush_p menu[NUM_CMDMENUS];
- void * context;
- } commandList[COMMAND_MAX];
-#endif
-
static struct {
wControl_p control;
wBool_t enabled;
@@ -873,6 +953,7 @@ LOG( log_command, 2, ( "COMMAND CANCEL %s\n", commandList[curCommand].helpKey )
checkPtMark = changed;
}
MainRedraw();
+ MapRedraw();
EnableCommands();
ResetMouseState();
LOG( log_command, 1, ( "COMMAND RESET %s\n", commandList[curCommand].helpKey ) )
@@ -1011,7 +1092,10 @@ LOG( log_command, 4, ( " COMMAND returns %d\n", rc ) )
(commandList[curCommand].stickyMask & stickySet) ) {
tempSegs_da.cnt = 0;
UpdateAllElevations();
- MainRedraw();
+ if (action != C_REDRAW) {
+ MainRedraw();
+ MapRedraw();
+ }
if (commandList[curCommand].options & IC_NORESTART) {
return C_CONTINUE;
}
@@ -1197,7 +1281,7 @@ EXPORT void LayoutSetPos(
lastGroup = 0;
wWinGetSize( mainW, &width, &h );
gap = 5;
- toolbarWidth = width+5;
+ toolbarWidth = width-20+5;
layerButtCnt = 0;
toolbarHeight = 0;
}
@@ -1224,7 +1308,7 @@ EXPORT void LayoutSetPos(
h = wControlGetHeight( buttonList[inx].control );
if ( inx<buttonCnt-1 && (buttonList[inx+1].options&IC_ABUT) )
w += wControlGetWidth( buttonList[inx+1].control );
- if (toolbarWidth+w>width) {
+ if (toolbarWidth+w>width-20) {
toolbarWidth = 0;
toolbarHeight += h + 5;
}
@@ -1405,74 +1489,6 @@ EXPORT void ButtonGroupEnd( void )
}
-#ifdef LATER
-EXPORT wIndex_t AddCommandControl(
- procCommand_t command,
- char * helpKey,
- char * nameStr,
- wControl_p control,
- int reqLevel,
- long options,
- long acclKey,
- void * context )
-{
- wIndex_t buttInx = -1;
- wIndex_t cmdInx;
- BOOL_T newButtonGroup = FALSE;
- wMenu_p tm, p1m, p2m;
- static wIcon_p openbuttIcon = NULL;
- static wMenu_p commandsSubmenu;
- static wMenu_p popup1Submenu;
- static wMenu_p popup2Submenu;
-
- AddToolbarControl( control, options );
-
- buttonList[buttInx].cmdInx = commandCnt;
- cmdInx = AddCommand( command, helpKey, nameStr, NULL, reqLevel, options, acclKey, context );
- commandList[cmdInx].buttInx = buttInx;
- if (nameStr[0] == '\0')
- return cmdInx;
- if (commandList[cmdInx].options&IC_STICKY) {
- if ( buttonGroupPopupM==NULL || newButtonGroup ) {
- if ( stickyCnt > 32 )
- AbortProg( "stickyCnt>32" );
- stickyCnt++;
- }
- if ( buttonGroupPopupM==NULL) {
- stickyLabels[stickyCnt-1] = nameStr;
- } else {
- stickyLabels[stickyCnt-1] = buttonGroupStickyLabel;
- }
- stickyLabels[stickyCnt] = NULL;
- commandList[cmdInx].stickyMask = 1L<<(stickyCnt-1);
- }
- if ( buttonGroupPopupM ) {
- commandList[cmdInx].menu[0] =
- wMenuPushCreate( buttonGroupPopupM, helpKey, GetBalloonHelpStr(helpKey), 0, DoCommandB, (void*)cmdInx );
- tm = commandsSubmenu;
- p1m = popup1Submenu;
- p2m = popup2Submenu;
- } else {
- tm = commandsM;
- p1m = (options&IC_POPUP2)?popup1aM:popup1M;
- p2m = (options&IC_POPUP2)?popup2aM:popup2M;
- }
- commandList[cmdInx].menu[1] =
- wMenuPushCreate( tm, helpKey, nameStr, acclKey, DoCommandB, (void*)cmdInx );
- if ( (options & (IC_POPUP|IC_POPUP2)) ) {
- if ( !(options & IC_SELECTED) ) {
- commandList[cmdInx].menu[2] =
- wMenuPushCreate( p1m, helpKey, nameStr, 0, DoCommandB, (void*)cmdInx );
- }
- commandList[cmdInx].menu[3] =
- wMenuPushCreate( p2m, helpKey, nameStr, 0, DoCommandB, (void*)cmdInx );
- }
-
- return cmdInx;
-}
-#endif
-
-
EXPORT wIndex_t AddMenuButton(
wMenu_p menu,
procCommand_t command,
@@ -1730,7 +1746,7 @@ static char *AllToolbarLabels[] = {
N_("Create Track Buttons"),
N_("Layout Control Elements"),
N_("Modify Track Buttons"),
- N_("Describe/Select"),
+ N_("Properties/Select"),
N_("Track Group Buttons"),
N_("Train Group Buttons"),
N_("Create Misc Buttons"),
@@ -1826,15 +1842,26 @@ static void ShowAddElevations( void )
/*--------------------------------------------------------------------*/
static wWin_p rotateW;
+static wWin_p moveW;
static long rotateValue;
+static coOrd moveValue;
static rotateDialogCallBack_t rotateDialogCallBack;
+static moveDialogCallBack_t moveDialogCallBack;
static void RotateEnterOk( void * );
+
static paramIntegerRange_t rn360_360 = { -360, 360, 80 };
static paramData_t rotatePLs[] = {
{ PD_LONG, &rotateValue, "rotate", PDO_ANGLE, &rn360_360, N_("Angle:") } };
static paramGroup_t rotatePG = { "rotate", 0, rotatePLs, sizeof rotatePLs/sizeof rotatePLs[0] };
+static paramFloatRange_t r_1000_1000 = { -1000.0, 1000.0, 80 };
+static void MoveEnterOk( void * );
+static paramData_t movePLs[] = {
+ { PD_FLOAT, &moveValue.x, "moveX", PDO_DIM, &r_1000_1000, N_("Move X:") },
+ { PD_FLOAT, &moveValue.y, "moveY", PDO_DIM, &r_1000_1000, N_("Move Y:") } };
+static paramGroup_t movePG = { "move", 0, movePLs, sizeof movePLs/sizeof movePLs[0] };
+
EXPORT void StartRotateDialog( rotateDialogCallBack_t func )
{
@@ -1845,6 +1872,22 @@ EXPORT void StartRotateDialog( rotateDialogCallBack_t func )
wShow( rotateW );
}
+EXPORT void StartMoveDialog( moveDialogCallBack_t func )
+{
+ if ( moveW == NULL )
+ moveW = ParamCreateDialog( &movePG, MakeWindowTitle(_("Move")), _("Ok"), MoveEnterOk, wHide, FALSE, NULL, 0, NULL );
+ ParamLoadControls( &movePG );
+ moveDialogCallBack = func;
+ moveValue = zero;
+ wShow( moveW );
+}
+
+static void MoveEnterOk( void * junk )
+{
+ ParamLoadData( &movePG );
+ moveDialogCallBack( (void*) &moveValue );
+ wHide( moveW );
+}
static void RotateEnterOk( void * junk )
{
@@ -1862,6 +1905,17 @@ static void RotateDialogInit( void )
ParamRegister( &rotatePG );
}
+static void MoveDialogInit (void)
+{
+ ParamRegister( &movePG );
+}
+
+
+EXPORT void AddMoveMenu(
+ wMenu_p m,
+ moveDialogCallBack_t func ) {
+ wMenuPushCreate( m, "", _("Enter Move ..."), 0, (wMenuCallBack_p)StartMoveDialog, (void*)func );
+}
EXPORT void AddRotateMenu(
wMenu_p m,
@@ -1904,6 +1958,7 @@ static void CreateDebugW( void )
debugPG.paramCnt = debugCnt;
ParamRegister( &debugPG );
debugW = ParamCreateDialog( &debugPG, MakeWindowTitle(_("Debug")), _("Ok"), DebugOk, NULL, FALSE, NULL, 0, NULL );
+ wHide(debugW);
}
@@ -1974,7 +2029,9 @@ static char * accelKeyNames[] = {
"F9",
"F10",
"F11",
- "F12" };
+ "F12",
+ "NumpadAdd",
+ "NumpadSub"};
static void SetAccelKey(
char * prefName,
@@ -2020,12 +2077,12 @@ static void SetAccelKey(
#include "bitmaps/document-save.xpm"
#include "bitmaps/document-open.xpm"
#include "bitmaps/document-print.xpm"
+#include "bitmaps/map.xpm"
static void CreateMenus( void )
{
wMenu_p fileM, editM, viewM, optionM, windowM, macroM, helpM, toolbarM, messageListM, manageM, addM, changeM, drawM;
wMenu_p zoomM, zoomSubM;
-// wIcon_p bm_p;
wMenuPush_p zoomInM, zoomOutM;
@@ -2055,10 +2112,13 @@ static void CreateMenus( void )
wMenuPushCreate( popup2M, "cmdZoomOut", _("Zoom Out"), 0, (wMenuCallBack_p)DoZoomDown, (void*)1 );
MiscMenuItemCreate( popup1M, popup2M, "cmdGridEnable", _("SnapGrid Enable"), 0, (void*)(wMenuCallBack_p)SnapGridEnable, 0, (void *)0 );
MiscMenuItemCreate( popup1M, popup2M, "cmdGridShow", _("SnapGrid Show"), 0, (void*)(wMenuCallBack_p)SnapGridShow, 0, (void *)0 );
+ MiscMenuItemCreate( popup1M, popup2M, "cmdMapShow", _("Show/Hide Map"), 0, (void*)(wMenuCallBack_p)MapWindowToggleShow, 0, (void *)0);
wMenuSeparatorCreate( popup1M );
wMenuSeparatorCreate( popup2M );
MiscMenuItemCreate( popup2M, NULL, "cmdCopy", _("Copy"), 0, (void*)(wMenuCallBack_p)EditCopy, 0, (void *)0 );
MiscMenuItemCreate( popup1M, popup2M, "cmdPaste", _("Paste"), 0, (void*)(wMenuCallBack_p)EditPaste, 0, (void *)0 );
+ MiscMenuItemCreate( popup1M, popup2M, "cmdSelectAll", _("Select All"), 0, (void*)(wMenuCallBack_p)SetAllTrackSelect, 0, (void *)1 );
+ MiscMenuItemCreate( popup1M, popup2M, "cmdSelectCurrentLayer", _("Select Current Layer"), 0, (void*)(wMenuCallBack_p)SelectCurrentLayer, 0, (void *)0 );
MiscMenuItemCreate( popup2M, NULL, "cmdDeselectAll", _("Deselect All"), 0, (void*)(wMenuCallBack_p)SetAllTrackSelect, 0, (void *)0 );
wMenuPushCreate( popup2M, "cmdMove", _("Move"), 0, (wMenuCallBack_p)DoCommandBIndirect, &moveCmdInx );
wMenuPushCreate( popup2M, "cmdRotate", _("Rotate"), 0, (wMenuCallBack_p)DoCommandBIndirect, &rotateCmdInx );
@@ -2074,7 +2134,6 @@ static void CreateMenus( void )
AddToolbarButton( "menuFile-clear", wIconCreatePixMap(document_new), IC_MODETRAIN_TOO, (addButtonCallBack_t)DoClear, NULL );
AddToolbarButton( "menuFile-load", wIconCreatePixMap(document_open), IC_MODETRAIN_TOO, (addButtonCallBack_t)ChkLoad, NULL );
AddToolbarButton( "menuFile-save", wIconCreatePixMap(document_save), IC_MODETRAIN_TOO, (addButtonCallBack_t)DoSave, NULL );
-// AddToolbarButton( "menuFile-print", wIconCreatePixMap(document_print_xpm), IC_MODETRAIN_TOO, (addButtonCallBack_t)DoPrint, NULL );
cmdGroup = BG_ZOOM;
zoomUpB = AddToolbarButton( "cmdZoomIn", wIconCreatePixMap(zoomin_xpm), IC_MODETRAIN_TOO,
@@ -2097,7 +2156,7 @@ static void CreateMenus( void )
/*
* FILE MENU
*/
- MiscMenuItemCreate( fileM, NULL, "menuFile-clear", _("&New"), ACCL_NEW, (void*)(wMenuCallBack_p)DoClear, 0, (void *)0 );
+ MiscMenuItemCreate( fileM, NULL, "menuFile-clear", _("&New ..."), ACCL_NEW, (void*)(wMenuCallBack_p)DoClear, 0, (void *)0 );
wMenuPushCreate( fileM, "menuFile-load", _("&Open ..."), ACCL_OPEN, (wMenuCallBack_p)ChkLoad, NULL );
wMenuSeparatorCreate( fileM );
@@ -2179,8 +2238,10 @@ static void CreateMenus( void )
// visibility toggle for map window
// get the start value
- wPrefGetInteger( "misc", "mapVisible", (long *)&mapVisible, 1 );
- mapShowMI = wMenuToggleCreate( viewM, "cmdMapShow", _("Show Map"), ACCL_MAPSHOW,
+ long mapVisible_long;
+ wPrefGetInteger( "misc", "mapVisible", (long *)&mapVisible_long, 1 );
+ mapVisible = mapVisible_long?TRUE:FALSE;
+ mapShowMI = wMenuToggleCreate( viewM, "cmdMapShow", _("Show/Hide Map"), ACCL_MAPSHOW,
mapVisible, (wMenuToggleCallBack_p)MapWindowToggleShow, NULL );
wMenuSeparatorCreate( viewM );
@@ -2193,6 +2254,10 @@ static void CreateMenus( void )
cmdGroup = BG_SNAP;
InitSnapGridButtons();
+ mapShowB = AddToolbarButton("cmdMapShow", wIconCreatePixMap(map_xpm), IC_MODETRAIN_TOO,
+ (addButtonCallBack_t)MapWindowToggleShow, NULL);
+ wControlLinkedSet((wControl_p)mapShowMI, (wControl_p)mapShowB);
+ wButtonSetBusy(mapShowB, (wBool_t)mapVisible);
/*
* ADD MENU
@@ -2221,6 +2286,7 @@ static void CreateMenus( void )
cmdGroup = BG_SELECT;
InitCmdDescribe( changeM );
InitCmdSelect( changeM );
+ InitCmdPan( changeM );
wMenuSeparatorCreate( changeM );
cmdGroup = BG_TRKGRP;
@@ -2324,7 +2390,7 @@ static void CreateMenus( void )
InitNewTurn( wMenuMenuCreate( manageM, "cmdTurnoutNew", _("Tur&nout Designer...") ) );
- MiscMenuItemCreate( manageM, NULL, "smdContmgm", _("Layout &Control Elements"), ACCL_CONTMGM,(void*)ControlMgrInit(),0,(void*) 0);
+ MiscMenuItemCreate( manageM, NULL, "cmdContmgm", _("Layout &Control Elements"), ACCL_CONTMGM,(void*)ControlMgrInit(),0,(void*) 0);
MiscMenuItemCreate( manageM, NULL, "cmdGroup", _("&Group"), ACCL_GROUP, (void*)(wMenuCallBack_p)DoGroup, IC_SELECTED, (void *)0 );
MiscMenuItemCreate( manageM, NULL, "cmdUngroup", _("&Ungroup"), ACCL_UNGROUP, (void*)(wMenuCallBack_p)DoUngroup, IC_SELECTED, (void *)0 );
@@ -2361,13 +2427,15 @@ static void CreateMenus( void )
#endif
SetAccelKey( "zoomUp", wAccelKey_Pgdn, 0, (wAccelKeyCallBack_p)DoZoomUp, (void*)1 );
SetAccelKey( "zoomDown", wAccelKey_Pgup, 0, (wAccelKeyCallBack_p)DoZoomDown, (void*)1 );
- SetAccelKey( "redraw", wAccelKey_F5, 0, (wAccelKeyCallBack_p)MainRedraw, (void*)1 );
+ SetAccelKey( "redraw", wAccelKey_F5, 0, (wAccelKeyCallBack_p)MainRedraw, (void*)1 );
SetAccelKey( "delete", wAccelKey_Del, 0, (wAccelKeyCallBack_p)SelectDelete, (void*)1 );
SetAccelKey( "copy", wAccelKey_Ins, WKEY_CTRL, (wAccelKeyCallBack_p)EditCopy, 0 );
SetAccelKey( "paste", wAccelKey_Ins, WKEY_SHIFT, (wAccelKeyCallBack_p)EditPaste, 0 );
SetAccelKey( "undo", wAccelKey_Back, WKEY_SHIFT, (wAccelKeyCallBack_p)UndoUndo, 0 );
SetAccelKey( "cut", wAccelKey_Del, WKEY_SHIFT, (wAccelKeyCallBack_p)EditCut, 0 );
SetAccelKey( "nextWindow", wAccelKey_F6, 0, (wAccelKeyCallBack_p)NextWindow, 0 );
+ SetAccelKey( "zoomUp", wAccelKey_Numpad_Add, WKEY_CTRL, (wAccelKeyCallBack_p)DoZoomUp, (void*)1 );
+ SetAccelKey( "zoomDown", wAccelKey_Numpad_Subtract, WKEY_CTRL, (wAccelKeyCallBack_p)DoZoomDown, (void*)1 );
InitBenchDialog();
}
@@ -2386,9 +2454,9 @@ static void LoadFileList( void )
if (!cp)
continue;
pathName = MyStrdup(cp);
- fileName = strrchr( pathName, FILE_SEP_CHAR[0] );
+ fileName = FindFilename((char *)pathName);
if (fileName)
- wMenuListAdd( fileList_ml, 0, fileName+1, pathName );
+ wMenuListAdd( fileList_ml, 0, fileName, pathName );
}
}
@@ -2408,13 +2476,13 @@ EXPORT void InitCmdExport( void )
* the option to load the checkpoint file to continue working after a crash.
*
* \param none
- * \return none
+ * \return TRUE for resume, FALSE otherwise
*
*/
-static void OfferCheckpoint( void )
+static int OfferCheckpoint( void )
{
- int ret;
+ int ret = FALSE;
/* sProdName */
ret = wNoticeEx( NT_INFORMATION,
@@ -2423,8 +2491,9 @@ static void OfferCheckpoint( void )
if( ret ) {
/* load the checkpoint file */
LoadCheckpoint();
+ ret = TRUE;
}
-
+ return ret;
}
@@ -2433,6 +2502,7 @@ EXPORT wWin_p wMain(
char * argv[] )
{
int c;
+ int resumeWork;
char * logFileName = NULL;
int log_init = 0;
int initialZoom = 0;
@@ -2445,6 +2515,8 @@ EXPORT wWin_p wMain(
char *oldLocale = NULL;
char buffer[ STR_SIZE ];
unsigned int i;
+ wPos_t displayWidth;
+ wPos_t displayHeight;
strcpy( buffer, sProdNameLower );
@@ -2527,15 +2599,19 @@ LOG1( log_init, ( "initCustom\n" ) )
* MAIN WINDOW
*/
LOG1( log_init, ( "create main window\n" ) )
- strcpy( Title1, sProdName );
+ SetLayoutTitle( sProdName );
sprintf( message, _("Unnamed Trackplan - %s(%s)"), sProdName, sVersion );
wSetBalloonHelp( balloonHelp );
- mainW = wWinMainCreate( buffer, 600, 350, "xtrkcadW", message, "main",
+
+ wGetDisplaySize(&displayWidth, &displayHeight);
+ mainW = wWinMainCreate( buffer, (displayWidth*2)/3, (displayHeight*2)/3, "xtrkcadW", message, "main",
F_RESIZE|F_MENUBAR|F_NOTAB|F_RECALLPOS|F_HIDE,
MainProc, NULL );
if ( mainW == NULL )
return NULL;
+ InitAppDefaults();
+
drawColorBlack = wDrawFindColor( wRGB( 0, 0, 0) );
drawColorWhite = wDrawFindColor( wRGB(255,255,255) );
drawColorRed = wDrawFindColor( wRGB(255, 0, 0) );
@@ -2593,6 +2669,7 @@ LOG1( log_init, ( "misc2Init\n" ) )
Misc2Init();
RotateDialogInit();
+ MoveDialogInit();
wSetSplashInfo( _("Initializing commands") );
LOG1( log_init, ( "paramInit\n" ) )
@@ -2687,19 +2764,21 @@ LOG1( log_init, ( "Initialization complete\n" ) )
ShowTip(SHOWTIP_NEXTTIP);
- /* if work is to be resumed and no filename was given on startup, load last layout */
- if( (onStartup == 0) && (!initialFile || !strlen(initialFile))) {
- initialFile = (char*)wPrefGetString( "misc", "lastlayout" );
- }
-
- if (initialFile && strlen(initialFile)) {
- DoFileList( 0, NULL, initialFile );
- }
-
/* check for existing checkpoint file */
+ resumeWork = FALSE;
if (ExistsCheckpoint())
- OfferCheckpoint();
+ resumeWork = OfferCheckpoint();
+ if (!resumeWork) {
+ /* if work is to be resumed and no filename was given on startup, load last layout */
+ if ((onStartup == 0) && (!initialFile || !strlen(initialFile))) {
+ initialFile = (char*)wPrefGetString("misc", "lastlayout");
+ }
+
+ if (initialFile && strlen(initialFile)) {
+ DoFileList(0, NULL, initialFile);
+ }
+ }
inMainW = FALSE;
return mainW;
}
diff --git a/app/bin/misc.h b/app/bin/misc.h
index 1747189..2a26b0c 100644
--- a/app/bin/misc.h
+++ b/app/bin/misc.h
@@ -25,12 +25,15 @@
#define EXPORT
+#include <stdio.h>
+
#include "acclkeys.h"
+#include "common.h"
+#include "draw.h"
+#include "wlib.h"
typedef void (*addButtonCallBack_t)(void*);
-#include "custom.h"
-
#ifdef WINDOWS
/* suppress warning from *.bmp about conversion of int to char */
#pragma warning( disable : 4305)
@@ -72,8 +75,7 @@ extern DIST_T connectDistance;
extern ANGLE_T connectAngle;
extern long twoRailScale;
extern long mapScale;
-extern char Title1[40];
-extern char Title2[40];
+extern long zoomCorner;
extern long checkPtInterval;
extern long liveMap;
extern long preSelect;
@@ -84,9 +86,6 @@ extern long descriptionFontSize;
extern long units;
extern long onStartup;
extern long angleSystem;
-extern SCALEINX_T curScaleInx;
-extern GAUGEINX_T curGaugeInx;
-extern SCALEDESCINX_T curScaleDescInx;
extern DIST_T trackGauge;
extern DIST_T curScaleRatio;
extern char * curScaleName;
@@ -181,7 +180,7 @@ extern wPos_t DlgSepFrmBottom;
extern wWin_p mainW;
extern wPos_t toolbarHeight;
extern wIndex_t changed;
-extern char FAR message[STR_LONG_SIZE];
+extern char message[STR_LONG_SIZE];
extern REGION_T curRegion;
extern long paramVersion;
extern coOrd zero;
@@ -199,8 +198,11 @@ extern wMenu_p popup1M, popup2M;
#define wControlBeside( B ) (wControlGetPosX((wControl_p)(B))+wControlGetWidth((wControl_p)(B)))
typedef void (*rotateDialogCallBack_t) ( void * );
+typedef void (*moveDialogCallBack_t) (void *);
extern void AddRotateMenu( wMenu_p, rotateDialogCallBack_t );
+extern void AddMoveMenu( wMenu_p, moveDialogCallBack_t );
extern void StartRotateDialog( rotateDialogCallBack_t );
+extern void StartMoveDialog(moveDialogCallBack_t );
/*
* Safe Memory etc
*/
@@ -219,6 +221,9 @@ int NoticeMessage( char *, char*, char *, ... );
int NoticeMessage2( int, char *, char*, char *, ... );
void DoQuit( void );
+char * ConvertFromEscapedText(const char * text);
+char * ConvertToEscapedText(const char * text);
+
void wShow( wWin_p );
void wHide( wWin_p );
void CloseDemoWindows( void );
@@ -282,10 +287,6 @@ typedef void (*changeNotificationCallBack_t)( long );
void RegisterChangeNotification( changeNotificationCallBack_t );
void DoChangeNotification( long );
-#include "param.h"
-#include "misc2.h"
-#include "fileio.h"
-
/* foreign externs */
extern drawCmd_t mapD;
extern STATUS_T CmdEnumerate( wAction_t, coOrd );
@@ -386,6 +387,11 @@ BOOL_T CarCustomSave(FILE*);
typedef int (*contMgmCallBack_p) (int, void *);
void ContMgmLoad (wIcon_p,contMgmCallBack_p,void *);
+/* dlayer.c */
+void LayerSetCounts();
+void DecrementLayerObjects(unsigned int index);
+void IncrementLayerObjects(unsigned int index);
+
/* doption.c */
long GetDistanceFormat( void );
diff --git a/app/bin/misc2.c b/app/bin/misc2.c
index 2c7644b..0dbb57d 100644
--- a/app/bin/misc2.c
+++ b/app/bin/misc2.c
@@ -36,27 +36,32 @@
#include <stdint.h>
-#include "track.h"
-#include "common.h"
-#include "utility.h"
-#include "draw.h"
-#include "misc.h"
#include "cjoin.h"
+#include "common.h"
#include "compound.h"
+#include "custom.h"
+#include "draw.h"
+#include "fileio.h"
#include "i18n.h"
+#include "layout.h"
+#include "messages.h"
+#include "misc.h"
+#include "param.h"
+#include "track.h"
+#include "utility.h"
+
-EXPORT long units = 0; /**< measurement units: 0 = English, 1 = metric */
+EXPORT long units = 0; /**< measurement units: 0 = English, 1 = metric */
EXPORT long checkPtInterval = 10;
EXPORT DIST_T curScaleRatio;
EXPORT char * curScaleName;
EXPORT DIST_T trackGauge;
-EXPORT char Title1[TITLEMAXLEN] = "";
-EXPORT char Title2[TITLEMAXLEN] = "Title line 2";
EXPORT long labelScale = 8;
EXPORT long labelEnable = ((1<<0)|LABELENABLE_LENGTHS|LABELENABLE_ENDPT_ELEV|LABELENABLE_CARS);
EXPORT long labelWhen = 2;
EXPORT long colorLayers = 0;
+EXPORT long zoomCorner = 0;
EXPORT long hideSelectionWindow = 0;
EXPORT long angleSystem = 0;
EXPORT DIST_T minLength = 0.1;
@@ -65,13 +70,15 @@ EXPORT ANGLE_T connectAngle = 1.0;
EXPORT long twoRailScale = 16;
EXPORT long mapScale = 64;
EXPORT long liveMap = 0;
-EXPORT long preSelect = 0;
+EXPORT long preSelect = 0; /**< default command 0 = Describe 1 = Select */
EXPORT long listLabels = 7;
EXPORT long layoutLabels = 1;
EXPORT long descriptionFontSize = 72;
EXPORT long enableListPrices = 1;
EXPORT void ScaleLengthEnd(void);
-static char minTrackRadiusPrefS[STR_SHORT_SIZE] = "minTrackRadius";
+
+static BOOL_T SetScaleDescGauge(SCALEINX_T scaleInx);
+
/****************************************************************************
*
@@ -173,7 +180,7 @@ static tieData_t tieData_demo = {
16.0/160.0,
32.0/160.0 };
-EXPORT SCALEINX_T curScaleInx = -1;
+//EXPORT SCALEINX_T curScaleInx = -1;
static scaleInfo_p curScale;
EXPORT long includeSameGaugeTurnouts = FALSE;
static SCALEINX_T demoScaleInx = -1;
@@ -188,8 +195,6 @@ typedef struct {
EXPORT typedef gaugeInfo_t * gaugeInfo_p;
-EXPORT GAUGEINX_T curGaugeInx = 0;
-
/** this struct holds a scale description */
typedef struct {
char *scaleDesc; /** ptr to textual description eg. 'HO' */
@@ -202,7 +207,6 @@ EXPORT typedef scaleDesc_t *scaleDesc_p;
static dynArr_t scaleDesc_da;
#define scaleDesc(N) DYNARR_N( scaleDesc_t, scaleDesc_da, N )
-EXPORT SCALEDESCINX_T curScaleDescInx;
/**
* Get the ratio from a scale description. Each member in the list of scale descriptions is
@@ -307,6 +311,48 @@ EXPORT char *GetGaugeDesc( SCALEDESCINX_T scaleInx, GAUGEINX_T gaugeInx )
return g->gauge;
}
+void
+SetScaleGauge(SCALEDESCINX_T desc, GAUGEINX_T gauge)
+{
+ dynArr_t gauges_da;
+
+ gauges_da = (scaleDesc(desc)).gauges_da;
+ SetLayoutCurScale(((gaugeInfo_p)gauges_da.ptr)[gauge].scale);
+}
+
+static BOOL_T
+SetScaleDescGauge(SCALEINX_T scaleInx)
+{
+ int i, j;
+ char *scaleName = GetScaleName(scaleInx);
+ DIST_T scaleRatio = GetScaleRatio(scaleInx);
+ dynArr_t gauges_da;
+
+ for (i = 0; i < scaleDesc_da.cnt; i++) {
+ char *t = strchr(scaleDesc(i).scaleDesc, ' ');
+ /* are the first characters (which describe the scale) identical? */
+ if (!strncmp(scaleDesc(i).scaleDesc, scaleName, t - scaleDesc(i).scaleDesc)) {
+ /* if yes, are we talking about the same ratio */
+ if (scaleInfo(scaleDesc(i).scale).ratio == scaleRatio) {
+ /* yes, we found the right scale descriptor, so now look for the gauge */
+ SetLayoutCurScaleDesc( i );
+ gauges_da = scaleDesc(i).gauges_da;
+ SetLayoutCurGauge(0);
+ for (j = 0; j < gauges_da.cnt; j++) {
+ gaugeInfo_p ptr = &(DYNARR_N(gaugeInfo_t, gauges_da, j));
+ if (scaleInfo(ptr->scale).gauge == GetScaleTrackGauge(scaleInx)) {
+ SetLayoutCurGauge( j );
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
EXPORT SCALEINX_T LookupScale( const char * name )
{
wIndex_t si;
@@ -411,21 +457,20 @@ SetScale( SCALEINX_T newScaleInx )
NoticeMessage( MSG_BAD_SCALE_INDEX, _("Ok"), NULL, (int)newScaleInx );
return;
}
- curScaleInx = (SCALEINX_T)newScaleInx;
- curScale = &scaleInfo(curScaleInx);
+ SetLayoutCurScale((SCALEINX_T)newScaleInx );
+ curScale = &scaleInfo(newScaleInx);
trackGauge = curScale->gauge;
curScaleRatio = curScale->ratio;
curScaleName = curScale->scale;
- curScaleDescInx = 0;
+ SetLayoutCurScaleDesc( 0 );
- GetScaleGauge( curScaleInx, &curScaleDescInx, &curGaugeInx );
+ SetScaleDescGauge((SCALEINX_T)newScaleInx);
wPrefSetString( "misc", "scale", curScaleName );
// now load the minimum radius for the newly selected scale
- sprintf( minTrackRadiusPrefS, "minTrackRadius-%s", curScaleName );
- wPrefGetFloat( "misc", minTrackRadiusPrefS, &minTrackRadius, curScale->R[0] );
+ LoadLayoutMinRadiusPref(curScaleName, curScale->R[0]);
}
/**
@@ -449,7 +494,7 @@ EXPORT BOOL_T DoSetScale(
while (isspace((unsigned char)*newScale)) newScale++;
for (scale = 0; scale<scaleInfo_da.cnt; scale++) {
if (strcasecmp( scaleInfo(scale).scale, newScale ) == 0) {
- curScaleInx = scale;
+ SetLayoutCurScale(scale);
found = TRUE;
break;
}
@@ -543,15 +588,6 @@ EXPORT BOOL_T DoSetScaleDesc( void )
return( TRUE );
}
-void
-SetScaleGauge( SCALEDESCINX_T scaleDesc, GAUGEINX_T gauge )
-{
- dynArr_t gauges_da;
-
- gauges_da = (scaleDesc(scaleDesc)).gauges_da;
- curScaleInx = ((gaugeInfo_p)gauges_da.ptr)[ gauge ].scale;
-}
-
static BOOL_T AddScale(
char * line )
{
@@ -674,7 +710,7 @@ EXPORT void LoadGaugeList( wList_p gaugeList, SCALEDESCINX_T scale )
static void ScaleChange( long changes )
{
if (changes & CHANGE_SCALE) {
- SetScale( curScaleInx );
+ SetScale( GetLayoutCurScale() );
}
}
diff --git a/app/bin/misc2.h b/app/bin/misc2.h
index 86b466c..d616ad8 100644
--- a/app/bin/misc2.h
+++ b/app/bin/misc2.h
@@ -1,5 +1,5 @@
-/*
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/misc2.h,v 1.7 2008-01-04 02:12:33 tshead Exp $
+/** \file misc2.h
+ *
*/
/* XTrkCad - Model Railroad CAD
@@ -23,9 +23,9 @@
#ifndef MISC2_H
#define MISC2_H
-#ifdef WINDOWS
-#include <time.h>
-#endif
+#include "common.h"
+#include "misc.h"
+#include "time.h"
#define LABEL_MANUF (1<<0)
#define LABEL_PARTNO (1<<1)
@@ -76,27 +76,25 @@ void GetScaleEasementValues( DIST_T *, DIST_T * );
tieData_p GetScaleTieData( SCALEINX_T );
SCALEINX_T LookupScale( const char * );
BOOL_T GetScaleGauge( SCALEINX_T scaleInx, SCALEDESCINX_T *scaleDescInx, GAUGEINX_T *gaugeInx);
-
+void SetScaleGauge(SCALEDESCINX_T desc, GAUGEINX_T gauge);
BOOL_T DoSetScale( char * );
-static void SetScale( SCALEINX_T );
-void SetScaleGauge( SCALEDESCINX_T, GAUGEINX_T );
void ScaleLengthIncrement( SCALEINX_T, DIST_T );
void LoadScaleList( wList_p );
void LoadGaugeList( wList_p, SCALEDESCINX_T );
BOOL_T CompatibleScale( BOOL_T, SCALEINX_T, SCALEINX_T );
BOOL_T DoSetScaleDesc( void );
-typedef int LAYER_T;
-LAYER_T curLayer;
+
+unsigned int curLayer;
long layerCount;
-wDrawColor GetLayerColor( LAYER_T );
-BOOL_T GetLayerVisible( LAYER_T );
-BOOL_T GetLayerFrozen( LAYER_T );
-BOOL_T GetLayerOnMap( LAYER_T );
-char * GetLayerName( LAYER_T );
+wDrawColor GetLayerColor( unsigned int );
+BOOL_T GetLayerVisible( unsigned int );
+BOOL_T GetLayerFrozen( unsigned int );
+BOOL_T GetLayerOnMap( unsigned int );
+char * GetLayerName( unsigned int );
BOOL_T ReadLayers( char * );
BOOL_T WriteLayers( FILE * );
-
+char * FormatLayerName(unsigned int layerNumber);
/* dlayers.c */
void UpdateLayerLists( void );
void DefaultLayerProperties(void);
diff --git a/app/bin/param.c b/app/bin/param.c
index 27c558f..e513bc3 100644
--- a/app/bin/param.c
+++ b/app/bin/param.c
@@ -21,6 +21,7 @@
*/
#include <stdlib.h>
+#include <stdint.h>
#include <stdio.h>
#ifndef WINDOWS
#include <unistd.h>
@@ -45,12 +46,17 @@
#include <stdarg.h>
#include <locale.h>
#include <wlib.h>
-#include "track.h"
+
#include "common.h"
-#include "utility.h"
-#include "misc.h"
#include "compound.h"
+#include "custom.h"
+#include "fileio.h"
#include "i18n.h"
+#include "messages.h"
+#include "misc.h"
+#include "param.h"
+#include "track.h"
+#include "utility.h"
/* Bogus reg vars */
@@ -65,10 +71,6 @@ EXPORT char *PREFSECT = "DialogItem";
static int paramCheckErrorCount = 0;
static BOOL_T paramCheckShowErrors = FALSE;
-static int log_hotspot;
-static int hotspotOffsetX = 5;
-static int hotspotOffsetY = 19;
-
static int log_paramLayout;
@@ -168,88 +170,119 @@ static int GetNumberStr( char ** cpp, FLOAT_T * numP, BOOL_T * hasFract )
}
//extern wIndex_t distanceFormatInx; // distanceFormatInx
-static BOOL_T GetDistance( char ** cpp, FLOAT_T * distP )
+static BOOL_T GetDistance(char ** cpp, FLOAT_T * distP)
{
- FLOAT_T n1, n2;
- BOOL_T neg = FALSE;
- BOOL_T hasFract;
- BOOL_T expectInch = FALSE;
- long distanceFormat;
-
- while ( isspace((unsigned char)**cpp) ) (*cpp)++;
- if ( (*cpp)[0] == '\0' ) {
- *distP = 0.0;
- return TRUE;
- }
- if ( (*cpp)[0] == '-' ) {
- neg = TRUE;
- (*cpp)++;
- }
- if ( !GetNumberStr( cpp, &n1, &hasFract ) ) return FALSE;
-
- distanceFormat = GetDistanceFormat();
-
-
- if ( (*cpp)[0] == '\0' ) { /* EOL */
- if ( units==UNITS_METRIC )
- {
- n1 = n1/2.54;
- if ((distanceFormat & DISTFMT_FMT) == DISTFMT_FMT_MM)
- n1 /= 10;
- if ((distanceFormat & DISTFMT_FMT) == DISTFMT_FMT_M)
- n1 *= 100;
- } else {
- if (((distanceFormat & DISTFMT_FMT) == DISTFMT_FMT_SHRT) || ((distanceFormat & DISTFMT_FMT) == DISTFMT_FMT_LONG))
- n1 *= 12;
- }
- if ( neg )
- n1 = -n1;
- *distP = n1;
- return TRUE;
- }
- if ( (*cpp)[0] == '\'' ) {
- n1 *= 12.0;
- (*cpp) += 1;
- expectInch = !hasFract;
- } else if ( tolower((unsigned char)(*cpp)[0]) == 'f' && tolower((unsigned char)(*cpp)[1]) == 't' ) {
- n1 *= 12.0;
- (*cpp) += 2;
- expectInch = !hasFract;
- } else if ( tolower((unsigned char)(*cpp)[0]) == 'c' && tolower((unsigned char)(*cpp)[1]) == 'm' ) {
- n1 /= 2.54;
- (*cpp) += 2;
- } else if ( tolower((unsigned char)(*cpp)[0]) == 'm' && tolower((unsigned char)(*cpp)[1]) == 'm' ) {
- n1 /= 25.4;
- (*cpp) += 2;
- } else if ( tolower((unsigned char)(*cpp)[0]) == 'm' ) {
- n1 *= 100.0/2.54;
- (*cpp) += 1;
- } else if ( (*cpp)[0] == '"' ) {
- (*cpp) += 1;
- } else if ( tolower((unsigned char)(*cpp)[0]) == 'i' && tolower((unsigned char)(*cpp)[1]) == 'n' ) {
- (*cpp) += 2;
- } else {
- getNumberError = N_("Invalid Units Indicator");
- return FALSE;
- }
- while ( isspace((unsigned char)**cpp) ) (*cpp)++;
- if ( expectInch && isdigit( (unsigned char)**cpp ) ) {
- if ( !GetNumberStr( cpp, &n2, &hasFract ) ) return FALSE;
- n1 += n2;
- if ( (*cpp)[0] == '"' )
- (*cpp) += 1;
- else if ( tolower((unsigned char)(*cpp)[0]) == 'i' && tolower((unsigned char)(*cpp)[1]) == 'n' )
- (*cpp) += 2;
- while ( isspace((unsigned char)**cpp) ) (*cpp)++;
- }
- if ( **cpp ) {
- getNumberError = N_("Expected End Of String");
- return FALSE;
- }
- if ( neg )
- n1 = -n1;
- *distP = n1;
- return TRUE;
+ FLOAT_T n1, n2;
+ BOOL_T neg = FALSE;
+ BOOL_T hasFract;
+ BOOL_T expectInch = FALSE;
+ long distanceFormat;
+
+ while (isspace((unsigned char)**cpp)) {
+ (*cpp)++;
+ }
+
+ if ((*cpp)[0] == '\0') {
+ *distP = 0.0;
+ return TRUE;
+ }
+
+ if ((*cpp)[0] == '-') {
+ neg = TRUE;
+ (*cpp)++;
+ }
+
+ if (!GetNumberStr(cpp, &n1, &hasFract)) {
+ return FALSE;
+ }
+
+ distanceFormat = GetDistanceFormat();
+
+ if ((*cpp)[0] == '\0') { /* EOL */
+ if (units==UNITS_METRIC) {
+ n1 = n1/2.54;
+
+ if ((distanceFormat & DISTFMT_FMT) == DISTFMT_FMT_MM) {
+ n1 /= 10;
+ }
+
+ if ((distanceFormat & DISTFMT_FMT) == DISTFMT_FMT_M) {
+ n1 *= 100;
+ }
+ }
+
+ if (neg) {
+ n1 = -n1;
+ }
+
+ *distP = n1;
+ return TRUE;
+ }
+
+ if ((*cpp)[0] == '\'') {
+ n1 *= 12.0;
+ (*cpp) += 1;
+ expectInch = !hasFract;
+ } else if (tolower((unsigned char)(*cpp)[0]) == 'f' &&
+ tolower((unsigned char)(*cpp)[1]) == 't') {
+ n1 *= 12.0;
+ (*cpp) += 2;
+ expectInch = !hasFract;
+ } else if (tolower((unsigned char)(*cpp)[0]) == 'c' &&
+ tolower((unsigned char)(*cpp)[1]) == 'm') {
+ n1 /= 2.54;
+ (*cpp) += 2;
+ } else if (tolower((unsigned char)(*cpp)[0]) == 'm' &&
+ tolower((unsigned char)(*cpp)[1]) == 'm') {
+ n1 /= 25.4;
+ (*cpp) += 2;
+ } else if (tolower((unsigned char)(*cpp)[0]) == 'm') {
+ n1 *= 100.0/2.54;
+ (*cpp) += 1;
+ } else if ((*cpp)[0] == '"') {
+ (*cpp) += 1;
+ } else if (tolower((unsigned char)(*cpp)[0]) == 'i' &&
+ tolower((unsigned char)(*cpp)[1]) == 'n') {
+ (*cpp) += 2;
+ } else {
+ getNumberError = N_("Invalid Units Indicator");
+ return FALSE;
+ }
+
+ while (isspace((unsigned char)**cpp)) {
+ (*cpp)++;
+ }
+
+ if (expectInch && isdigit((unsigned char)**cpp)) {
+ if (!GetNumberStr(cpp, &n2, &hasFract)) {
+ return FALSE;
+ }
+
+ n1 += n2;
+
+ if ((*cpp)[0] == '"') {
+ (*cpp) += 1;
+ } else if (tolower((unsigned char)(*cpp)[0]) == 'i' &&
+ tolower((unsigned char)(*cpp)[1]) == 'n') {
+ (*cpp) += 2;
+ }
+
+ while (isspace((unsigned char)**cpp)) {
+ (*cpp)++;
+ }
+ }
+
+ if (**cpp) {
+ getNumberError = N_("Expected End Of String");
+ return FALSE;
+ }
+
+ if (neg) {
+ n1 = -n1;
+ }
+
+ *distP = n1;
+ return TRUE;
}
@@ -279,46 +312,57 @@ EXPORT FLOAT_T DecodeFloat(
}
-EXPORT FLOAT_T DecodeDistance(
- wString_p strCtrl,
- BOOL_T * validP )
+FLOAT_T DecodeDistance(
+ wString_p strCtrl,
+ BOOL_T * validP)
{
- FLOAT_T valF;
- char *cp0, *cp1, *cpN, c1;
-
- cp0 = cp1 = cpN = CAST_AWAY_CONST wStringGetValue( strCtrl );
- cpN += strlen(cpN)-1;
- while (cpN > cp1 && isspace((unsigned char)*cpN) ) cpN--;
- c1 = *cpN;
- switch ( c1 ) {
- case '=':
- case 's':
- case 'S':
- case 'p':
- case 'P':
- *cpN = '\0';
- break;
- default:
- cpN = NULL;
- }
- *validP = ( GetDistance( &cp1, &valF ) );
- if ( cpN )
- *cpN = c1;
- if ( *validP ) {
-/*fprintf( stderr, "gd=%0.6f\n", valF );*/
- if ( c1 == 's' || c1 == 'S' )
- valF *= curScaleRatio;
- else if ( c1 == 'p' || c1 == 'P' )
- valF /= curScaleRatio;
- if ( cpN )
- wStringSetValue( strCtrl, FormatDistance( valF ) );
- } else {
-/*fprintf( stderr, "Gd( @%s ) error=%s\n", cp1, getNumberError );*/
- sprintf( decodeErrorStr, "%s @ %s", _(getNumberError), *cp1?cp1:_("End Of String") );
- /*wStringSetHilight( strCtrl, cp1-cp0, -1 ); */
- valF = 0.0;
- }
- return valF;
+ FLOAT_T valF;
+ char *cp0, *cp1, *cpN, c1;
+ cp0 = cp1 = cpN = CAST_AWAY_CONST wStringGetValue(strCtrl);
+ cpN += strlen(cpN)-1;
+
+ while (cpN > cp1 && isspace((unsigned char)*cpN)) {
+ cpN--;
+ }
+
+ c1 = *cpN;
+
+ switch (c1) {
+ case '=':
+ case 's':
+ case 'S':
+ case 'p':
+ case 'P':
+ *cpN = '\0';
+ break;
+
+ default:
+ cpN = NULL;
+ }
+
+ *validP = (GetDistance(&cp1, &valF));
+
+ if (cpN) {
+ *cpN = c1;
+ }
+
+ if (*validP) {
+ if (c1 == 's' || c1 == 'S') {
+ valF *= curScaleRatio;
+ } else if (c1 == 'p' || c1 == 'P') {
+ valF /= curScaleRatio;
+ }
+
+ if (cpN) {
+ wStringSetValue(strCtrl, FormatDistance(valF));
+ }
+ } else {
+ sprintf(decodeErrorStr, "%s @ %s", _(getNumberError),
+ *cp1?cp1:_("End Of String"));
+ valF = 0.0;
+ }
+
+ return valF;
}
@@ -542,10 +586,18 @@ EXPORT void ParamLoadControl(
p->oldD.f = tmpR;
break;
case PD_STRING:
- wStringSetValue( (wString_p)p->control, (char*)p->valueP );
if (p->oldD.s)
MyFree( p->oldD.s );
- p->oldD.s = MyStrdup( (char*)p->valueP );
+ if (p->context) {
+ p->oldD.s = MyMalloc((intptr_t)p->context);
+ strncpy(p->oldD.s, (char*)p->valueP, (intptr_t)p->context);
+ *(p->oldD.s + (intptr_t)p->context - 1) = '\0';
+ wStringSetValue((wString_p)p->control, (char*)p->oldD.s);
+ }
+ else {
+ p->oldD.s = MyStrdup((char *)p->valueP);
+ wStringSetValue((wString_p)p->control, (char*)p->valueP);
+ }
break;
case PD_MESSAGE:
wMessageSetValue( (wMessage_p)p->control, _((char*)p->valueP) );
@@ -558,6 +610,7 @@ EXPORT void ParamLoadControl(
case PD_DRAW:
case PD_MENU:
case PD_MENUITEM:
+ case PD_BITMAP:
break;
}
}
@@ -677,8 +730,20 @@ EXPORT long ParamUpdate(
if (p->oldD.s)
MyFree( p->oldD.s );
p->oldD.s = MyStrdup( stringV );
- if ( /*(p->option&PDO_NOUPDUPD)==0 &&*/ p->valueP)
- strcpy( (char*)p->valueP, stringV );
+ if ( /*(p->option&PDO_NOUPDUPD)==0 &&*/ p->valueP) {
+ if (p->context) {
+ strncpy((char*)p->valueP, stringV, (intptr_t)p->context);
+ ((char *)p->valueP)[(intptr_t)p->context - 1] = '\0';
+ if (strlen(stringV) > (uintptr_t)p->context) {
+ NoticeMessage2(0, MSG_ENTERED_STRING_TRUNCATED, _("Ok"), NULL, (intptr_t)p->context);
+ }
+
+ }
+ else {
+ strcpy((char*)p->valueP, stringV);
+ }
+ }
+
if ( (p->option&PDO_NOUPDACT)==0 && pg->changeProc)
pg->changeProc( pg, inx, CAST_AWAY_CONST stringV );
change |= (1L<<inx);
@@ -690,6 +755,7 @@ EXPORT long ParamUpdate(
case PD_TEXT:
case PD_MENU:
case PD_MENUITEM:
+ case PD_BITMAP:
break;
}
}
@@ -754,6 +820,7 @@ EXPORT void ParamLoadData(
case PD_TEXT:
case PD_MENU:
case PD_MENUITEM:
+ case PD_BITMAP:
break;
}
}
@@ -862,6 +929,7 @@ static long ParamIntRestore(
case PD_TEXT:
case PD_MENU:
case PD_MENUITEM:
+ case PD_BITMAP:
break;
}
}
@@ -911,6 +979,7 @@ static void ParamIntSave(
case PD_TEXT:
case PD_MENU:
case PD_MENUITEM:
+ case PD_BITMAP:
break;
}
}
@@ -1006,7 +1075,7 @@ EXPORT void ParamRegister( paramGroup_p pg )
case PD_COMBOLIST:
if ( (p->option&PDO_LISTINDEX) ) {
if (!wPrefGetInteger( PREFSECT, prefName1, &valL, *(wIndex_t*)p->valueP ))
- wPrefGetInteger( prefSect2, prefName2, &valL, *(wIndex_t*)p->valueP );
+ wPrefGetInteger( prefSect2, prefName2, &valL, valL );
if ( p->control )
wListSetIndex( (wList_p)p->control, (wIndex_t)valL );
*(wIndex_t*)p->valueP = (wIndex_t)valL;
@@ -1185,6 +1254,7 @@ EXPORT void ParamGroupRecord(
case PD_TEXT:
case PD_MENU:
case PD_MENUITEM:
+ case PD_BITMAP:
break;
}
}
@@ -1517,7 +1587,7 @@ EXPORT void ParamChange( paramData_p p )
case PD_DROPLIST:
case PD_COMBOLIST:
if (recordF && (p->option&PDO_NORECORD)==0 && p->group->nameStr && p->nameStr) {
- fprintf( recordF, "PARAMETER %s %s %d %s\n", p->group->nameStr, p->nameStr, *(wIndex_t*)p->valueP, ??? );
+ fprintf( recordF, "PARAMETER %s %s %d %s\n", p->group->nameStr, p->nameStr, *(wIndex_t*)p->valueP, "???" );
}
#ifdef LATER
if ( p->control && (p->option&PDO_NOCONTUPD) == 0 )
@@ -1776,6 +1846,7 @@ static void ParamPlayback( char * line )
case PD_MESSAGE:
case PD_TEXT:
case PD_MENU:
+ case PD_BITMAP:
break;
case PD_MENUITEM:
if (p->valueP) {
@@ -1926,6 +1997,7 @@ static void ParamCheck( char * line )
case PD_TEXT:
case PD_MENU:
case PD_MENUITEM:
+ case PD_BITMAP:
break;
}
if ( hasError ) {
@@ -1965,7 +2037,6 @@ static void ParamCreateControl(
paramTextData_t * textDataP;
paramListData_t * listDataP;
wIcon_p iconP;
- wDrawColor color = wDrawColorBlack;
wWin_p win;
wPos_t w;
@@ -2049,8 +2120,8 @@ static void ParamCreateControl(
listDataP->height = wControlGetHeight( pd->control );
break;
case PD_COLORLIST:
- pd->control = (wControl_p)wColorSelectButtonCreate( win, xx, yy, helpStr, _(pd->winLabel), pd->winOption, 0, &color, ParamColorSelectPush, pd );
- break;
+ pd->control = (wControl_p)wColorSelectButtonCreate( win, xx, yy, helpStr, _(pd->winLabel), pd->winOption, 0, NULL, ParamColorSelectPush, pd );
+ break;
case PD_MESSAGE:
if ( pd->winData != 0 )
w = (wPos_t)(long)pd->winData;
@@ -2312,10 +2383,6 @@ static void LayoutControls(
char * cp;
strcpy( message, pd->nameStr );
for ( cp=message; *cp; cp++ ) if ( *cp == '-' ) *cp = '_';
- LOG( log_hotspot, 1, ( "popup %d %d %d %d _%s_%s\n",
- controlK.orig.x+hotspotOffsetX, controlK.orig.y+hotspotOffsetY,
- controlSize_x, controlSize_y,
- group->nameStr, message ) )
}
/*
* Set column term
@@ -2437,9 +2504,21 @@ static void ParamDlgProc(
}
}
+/**
+ * Create a dialog box from data definition.
+ *
+ * \param IN group data definition for the dialog
+ * \param IN title title of the new dialog
+ * \param IN okLabel text for the affirmative button
+ * \param IN okProc subroutine to call when ok is pressed
+ * \param IN cancelProc if not NULL a subroutine for Cancel event. If NULL no cancel button is created
+ * \param IN needHelpButton if TRUE a help button is created
+ * \param IN layoutProc ???
+ * \param IN winOption ???
+ * \param IN changeProc ???
+ */
-
-EXPORT wWin_p ParamCreateDialog(
+wWin_p ParamCreateDialog(
paramGroup_p group,
char * title,
char * okLabel,
@@ -2452,7 +2531,6 @@ EXPORT wWin_p ParamCreateDialog(
{
char helpStr[STR_SHORT_SIZE];
wPos_t w0, h0;
- wButton_p lastB = NULL;
char * cancelLabel = (winOption&PD_F_ALT_CANCELLABEL?_("Close"):_("Cancel"));
winOption &= ~PD_F_ALT_CANCELLABEL;
@@ -2465,34 +2543,22 @@ EXPORT wWin_p ParamCreateDialog(
if ( (winOption&F_RESIZE) != 0 )
winOption |= F_RECALLSIZE;
- sprintf( helpStr, "cmd%s", group->nameStr );
- helpStr[3] = toupper((unsigned char)helpStr[3]);
-
group->win = wWinPopupCreate( mainW, DlgSepRight, DlgSepFrmBottom, helpStr, title, group->nameStr, F_AUTOSIZE|winOption, ParamDlgProc, group );
if ( okLabel && okProc ) {
sprintf( helpStr, "%s-ok", group->nameStr );
- lastB = group->okB = wButtonCreate( group->win, 0, 0, helpStr, okLabel, BB_DEFAULT, 0, (wButtonCallBack_p)ParamButtonOk, group );
+ group->okB = wButtonCreate( group->win, 0, 0, helpStr, okLabel, BB_DEFAULT, 0, (wButtonCallBack_p)ParamButtonOk, group );
}
if ( group->cancelProc ) {
- lastB = group->cancelB = wButtonCreate( group->win, 0, 0, NULL, cancelLabel, BB_CANCEL, 0, (wButtonCallBack_p)ParamButtonCancel, group );
+ group->cancelB = wButtonCreate( group->win, 0, 0, NULL, cancelLabel, BB_CANCEL, 0, (wButtonCallBack_p)ParamButtonCancel, group );
}
if ( needHelpButton ) {
sprintf( helpStr, "cmd%s", group->nameStr );
helpStr[3] = toupper((unsigned char)helpStr[3]);
- lastB = group->helpB = wButtonCreate( group->win, 0, 0, NULL, _("Help"), BB_HELP, 0, (wButtonCallBack_p)wHelp, MyStrdup(helpStr) );
+ group->helpB = wButtonCreate( group->win, 0, 0, NULL, _("Help"), BB_HELP, 0, (wButtonCallBack_p)wHelp, MyStrdup(helpStr) );
}
- LOG( log_hotspot, 1, ( "mkshg ${PNG2DIR}/%s.png ${SHGDIR}/%s.shg << EOF\n", group->nameStr, group->nameStr ) )
LayoutControls( group, ParamCreateControl, &group->origW, &group->origH );
- if ( group->okB )
- LOG( log_hotspot, 1, ( "popup %d %d %d %d _%s_%s\n",
- wControlGetPosX((wControl_p)(group->okB))+hotspotOffsetX,
- wControlGetPosY((wControl_p)(group->okB))+hotspotOffsetY,
- wControlGetWidth((wControl_p)(group->okB)),
- wControlGetHeight((wControl_p)(group->okB)),
- group->nameStr, "ok" ) )
- LOG( log_hotspot, 1, ( "EOF\n" ) )
group->origW += DlgSepRight;
group->origH += DlgSepBottom;
@@ -2502,11 +2568,9 @@ EXPORT wWin_p ParamCreateDialog(
group->origH != h0 ) {
LayoutControls( group, ParamPositionControl, NULL, NULL );
}
- } else if ( group->origW > w0 || group->origH > h0 ) {
- if ( group->origW > w0 )
- w0 = group->origW;
- if ( group->origH > h0 )
- h0 = group->origH;
+ } else {
+ w0 = max(group->origW, w0);
+ h0 = max(group->origH, h0);
wWinSetSize( group->win, w0, h0 );
}
@@ -2566,6 +2630,5 @@ EXPORT void ParamInit( void )
{
AddPlaybackProc( "PARAMETER", ParamPlayback, NULL );
AddPlaybackProc( "PARAMCHECK", ParamCheck, NULL );
- log_hotspot = LogFindIndex( "hotspot" );
log_paramLayout = LogFindIndex( "paramlayout" );
}
diff --git a/app/bin/param.h b/app/bin/param.h
index 02d259c..355f3e7 100644
--- a/app/bin/param.h
+++ b/app/bin/param.h
@@ -1,5 +1,5 @@
-/*
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/param.h,v 1.6 2009-09-21 18:24:33 m_fischer Exp $
+/** \file param.h
+ * Definitions for parameter dialog handling
*/
/* XTrkCad - Model Railroad CAD
@@ -23,6 +23,10 @@
#ifndef PARAM_H
#define PARAM_H
+#include "common.h"
+#include "wlib.h"
+#include "draw.h"
+
typedef struct turnoutInfo_t * turnoutInfo_p;
typedef enum {
@@ -214,12 +218,15 @@ extern int paramLen;
extern unsigned long paramKey;
extern BOOL_T paramTogglePlaybackHilite;
+long GetChanges(paramGroup_p pg);
+
+
#define ParamMenuPushCreate( PD, M, HS, NS, AK, FUNC ) \
wMenuPushCreate( M, HS, NS, AK, paramMenuPush, &PD ); \
(PD).valueP = FUNC; \
if ( HS ) GetBalloonHelpStr(HS);
-#define PD_F_ALT_CANCELLABEL (1L<<30)
+#define PD_F_ALT_CANCELLABEL (1L<<30) /**<use Close or Cancel for the discard button */
wWin_p ParamCreateDialog( paramGroup_p, char *, char *, paramActionOkProc, paramActionCancelProc, BOOL_T, paramLayoutProc, long, paramChangeProc );
void ParamCreateControls( paramGroup_p, paramChangeProc );
void ParamLayoutDialog( paramGroup_p );
diff --git a/app/bin/paths.c b/app/bin/paths.c
new file mode 100644
index 0000000..cbd9b38
--- /dev/null
+++ b/app/bin/paths.c
@@ -0,0 +1,220 @@
+/** \file paths.c
+* Path and file name handling functions.
+*/
+
+/* XTrkCad - Model Railroad CAD
+ * Copyright (C) 2017 Martin Fischer
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, 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 License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdarg.h>
+
+#ifdef WINDOWS
+#include <windows.h>
+#endif
+
+#include <wlib.h>
+#include <dynstring.h>
+#include "track.h"
+#include "common.h"
+#include "utility.h"
+#include "misc.h"
+#include "i18n.h"
+#include "uthash.h"
+#include "paths.h"
+
+struct pathTable {
+ char type[ PATH_TYPE_SIZE]; /**< type of path */
+ DynString path; /**< path */
+ UT_hash_handle hh; /**< makes this structure hashable */
+};
+
+static struct pathTable *paths;
+
+static void AddPath(const char *type, char*path);
+static struct pathTable *FindPath(const char *type);
+
+#define PATHS_SECTION "paths"
+
+/**
+* Find the path for a given type in the hash table
+*
+* \param type IN the searched type
+* \param entry OUT the table entry
+* \return
+*/
+
+static struct pathTable *
+FindPath(const char *type)
+{
+ struct pathTable *entry;
+ HASH_FIND_STR(paths, type, entry);
+ return (entry);
+}
+
+/**
+ * Add a path to the table. If it already exists, the value ist updated.
+ *
+ * \param type IN type of path
+ * \param path IN path
+ */
+static void
+AddPath(const char *type, char*path)
+{
+ struct pathTable *tableEntry;
+ tableEntry = FindPath(type);
+
+ if (tableEntry) {
+ DynStringClear(&(tableEntry->path));
+ } else {
+ tableEntry = malloc(sizeof(struct pathTable));
+ DynStringMalloc(&tableEntry->path, 16);
+ strcpy(tableEntry->type, type);
+ HASH_ADD_STR(paths, type, tableEntry);
+ }
+
+ DynStringCatCStr(&(tableEntry->path), path);
+}
+
+/**
+ * Update the current directory for a specific filetype. Get the directory part from the current file.
+ * XTrackCAD keeps seprate directories for different filetypes.(for trackplans, bitmaps and DXF files).
+ * The paths are stored in a hash table using the file type as the hash.
+ *
+ * \param pathType IN file type
+ * \param fileName IN fully qualified filename
+ * \return
+ *
+ */
+
+void SetCurrentPath(
+ const char * pathType,
+ const char * fileName)
+{
+ char *path;
+ char *copy;
+ assert(fileName != NULL);
+ assert(pathType != NULL);
+ copy = strdup(fileName);
+ path = strrchr(copy, FILE_SEP_CHAR[0]);
+
+ if (path) {
+ *path = '\0';
+ AddPath(pathType, copy);
+ wPrefSetString(PATHS_SECTION, pathType, copy);
+ }
+
+ free(copy);
+}
+
+/**
+ * Get the current path for a given file type. Following options are searched:
+ * 1. the hash array
+ * 2. the preferences file for this type
+ * 3. the older general setting (for backwards compatibility)
+ * 4. the user's home directory as default
+ * Search is finished when one of the options returns a valid path
+ *
+ * \param IN pathType the type
+ * \return pointer to path of NULL if not existing
+ */
+
+char *GetCurrentPath(
+ const char *pathType)
+{
+ struct pathTable *currentPath;
+ const char *path;
+ assert(pathType != NULL);
+ currentPath = FindPath(pathType);
+
+ if (currentPath) {
+ return (DynStringToCStr(&(currentPath->path)));
+ }
+
+ path = wPrefGetString(PATHS_SECTION, pathType);
+
+ if (!path) {
+ path = wPrefGetString("file", "directory");
+ }
+
+ if (!path) {
+ path = wGetUserHomeDir();
+ }
+
+ AddPath(pathType, (char *)path);
+ return ((char *)path);
+}
+
+/**
+* Find the filename/extension piece in a fully qualified path
+*
+* \param path IN the full path
+* \return pointer to the filename part
+*/
+
+char *FindFilename(char *path)
+{
+ char *name;
+ name = strrchr(path, FILE_SEP_CHAR[0]);
+
+ if (name) {
+ name++;
+ } else {
+ name = path;
+ }
+
+ return (name);
+}
+
+/**
+* Make a full path definition from directorys and filenames. The individual pieces are
+* concatinated. Where necessary a path delimiter is added. A pointer to the resulting
+* string is returned. This memory should be free'd when no longer needed.
+* Windows: to construct an absolute path, a leading backslash has to be included after
+* the drive delimiter ':' or at the beginning of the first directory name.
+*
+* \param str OUT pointer to the complete path
+* \param ... IN one or more parts of the path, terminate with NULL
+*/
+
+void
+MakeFullpath(char **str, ...)
+{
+ va_list valist;
+ const char *part;
+ char *separator = FILE_SEP_CHAR;
+ char lastchar = '\0';
+ DynString path;
+ DynStringMalloc(&path, 0);
+ va_start(valist, str);
+
+ while ((part = va_arg(valist, const char *))) {
+ if (part[0] !=separator[0] && lastchar && lastchar != separator[0] &&
+ lastchar != ':') {
+ DynStringNCatCStr(&path, 1, separator);
+ }
+
+ DynStringCatCStr(&path, part);
+ lastchar = part[strlen(part) - 1];
+ }
+
+ *str = strdup(DynStringToCStr(&path));
+ DynStringFree(&path);
+ va_end(valist);
+}
diff --git a/app/bin/paths.h b/app/bin/paths.h
new file mode 100644
index 0000000..1e3c98a
--- /dev/null
+++ b/app/bin/paths.h
@@ -0,0 +1,32 @@
+/** \file paths.h
+* Path and file name handling functions.
+*/
+
+/* XTrkCad - Model Railroad CAD
+ * Copyright (C) 2017 Martin Fischer
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, 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 License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef HAVE_PATHS_H
+#define HAVE_PATHS_H
+
+#define PATH_TYPE_SIZE 10
+
+void SetCurrentPath( const char * pathType, const char * fileName );
+char *GetCurrentPath(const char *pathType);
+char *FindFilename(char *path);
+void MakeFullpath(char **str, ...);
+#endif
diff --git a/app/bin/shrtpath.c b/app/bin/shrtpath.c
index fa48408..b8fbe1e 100644
--- a/app/bin/shrtpath.c
+++ b/app/bin/shrtpath.c
@@ -20,8 +20,10 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-#include "track.h"
+#include <string.h>
+
#include "shrtpath.h"
+#include "track.h"
EXPORT int log_shortPath;
static int log_shortPathInitted;
diff --git a/app/bin/shrtpath.h b/app/bin/shrtpath.h
index a8236e6..165717f 100644
--- a/app/bin/shrtpath.h
+++ b/app/bin/shrtpath.h
@@ -1,4 +1,6 @@
-/* $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/shrtpath.h,v 1.1 2005-12-07 15:46:54 rc-flyer Exp $ */
+/** \file shrtpath.h
+ *
+ */
/* XTrkCad - Model Railroad CAD
* Copyright (C) 2005 Dave Bullis
@@ -17,6 +19,11 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
+#ifndef HAVE_SHRTPATH_H
+#define HAVE_SHRTPATH_H
+
+#include "common.h"
+#include "track.h"
typedef enum {
SPTC_MATCH, /* trk:ep is end of path? */
@@ -31,3 +38,4 @@ typedef int (*shortestPathFunc_p)( SPTF_CMD cmd, track_p, EPINX_T, EPINX_T, DIST
int FindShortestPath( track_p, EPINX_T, BOOL_T, shortestPathFunc_p, void * );
extern int log_shortPath;
+#endif //HAVE_SHRTPATH_H \ No newline at end of file
diff --git a/app/bin/smalldlg.c b/app/bin/smalldlg.c
index e4213a5..7828912 100644
--- a/app/bin/smalldlg.c
+++ b/app/bin/smalldlg.c
@@ -1,7 +1,5 @@
/** \file smalldlg.c
* Several simple and smaller dialogs.
- *
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/smalldlg.c,v 1.6 2009-09-21 18:24:33 m_fischer Exp $
*/
/* XTrkCad - Model Railroad CAD
@@ -38,23 +36,22 @@
#ifdef WINDOWS
#include <io.h>
#include <windows.h>
-#if _MSC_VER >1300
- #define strdup _strdup
-#endif
#else
#include <sys/stat.h>
#endif
-#include "wlib.h"
#include "common.h"
+#include "custom.h"
#include "draw.h"
+#include "fileio.h"
+#include "i18n.h"
#include "misc.h"
-#include "custom.h"
+#include "paths.h"
#include "param.h"
-
#include "smalldlg.h"
-#include "i18n.h"
+#include "wlib.h"
+extern char *sTipF;
wWin_p aboutW;
static wWin_p tipW; /**< window handle for tip dialog */
@@ -86,13 +83,14 @@ static void CreateTipW( void )
{
FILE * tipF;
char buff[4096];
+ char *filename;
char * cp;
tipW = ParamCreateDialog( &tipPG, MakeWindowTitle(_("Tip of the Day")), _("Ok"), (paramActionOkProc)wHide, NULL, FALSE, NULL, F_CENTER, NULL );
/* open the tip file */
- sprintf( buff, "%s%s%s.tip", libDir, FILE_SEP_CHAR, sProdNameLower );
- tipF = fopen( buff, "r" );
+ MakeFullpath(&filename, libDir, sTipF, NULL);
+ tipF = fopen( filename, "r" );
/* if tip file could not be opened, the only tip is an error message for the situation */
if (tipF == NULL) {
@@ -146,6 +144,7 @@ static void CreateTipW( void )
tips(tips_da.cnt-1) = strdup( buff );
}
}
+ free(filename);
}
/**
@@ -221,11 +220,12 @@ void CreateAboutW( void *ptr )
aboutW = ParamCreateDialog( &aboutPG, MakeWindowTitle(_("About")), _("Ok"), (paramActionOkProc)wHide, NULL, FALSE, NULL, F_TOP|F_CENTER, NULL );
ParamLoadMessage( &aboutPG, I_ABOUTVERSION, sAboutProd );
wTextAppend( COPYRIGHT_T, DESCRIPTION );
- wTextAppend( COPYRIGHT_T, "\n\nXTrackCAD is Copyright 2003 by Sillub Technology and 2007 by Martin Fischer and Bob Blackwell." );
- wTextAppend( COPYRIGHT_T, "\n\nIcons by: Tango Desktop Project (http://tango.freedesktop.org)");
- wTextAppend( COPYRIGHT_T, "\n\nContributions by: Robert Heller, Mikko Nissinen, Timothy M. Shead, Daniel Luis Spagnol" );
- wTextAppend( COPYRIGHT_T, "\n\nParameter Files by: Ralph Boyd, Dwayne Ward" );
- wTextAppend( COPYRIGHT_T, "\n\nuthash Copyright notice:" );
+ wTextAppend( COPYRIGHT_T, "\n\nXTrackCAD is Copyright 2003 by Sillub Technology and 2017 by Bob Blackwell, Martin Fischer and Adam Richards." );
+ wTextAppend( COPYRIGHT_T, "\nIcons by: Tango Desktop Project (http://tango.freedesktop.org)");
+ wTextAppend( COPYRIGHT_T, "\nContributions by: Robert Heller, Mikko Nissinen, Timothy M. Shead, Daniel Luis Spagnol" );
+ wTextAppend( COPYRIGHT_T, "\nParameter Files by: Ralph Boyd, Dwayne Ward" );
+ wTextAppend( COPYRIGHT_T, "\nCornu Algorithm and Implementation by: Raph Levien");
+ wTextAppend( COPYRIGHT_T, "\nuthash Copyright notice:" );
wTextAppend( COPYRIGHT_T, "\nCopyright (c) 2005-2015, Troy D. Hanson http://troydhanson.github.com/uthash/");
wTextAppend( COPYRIGHT_T, "\nAll rights reserved.");
}
diff --git a/app/bin/smalldlg.h b/app/bin/smalldlg.h
index 2bcb3bc..df54063 100644
--- a/app/bin/smalldlg.h
+++ b/app/bin/smalldlg.h
@@ -1,7 +1,5 @@
/** \file smalldlg.h
* Definitions and declarations for the small dialog box functions.
- *
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/smalldlg.h,v 1.2 2009-09-21 18:24:33 m_fischer Exp $
*/
/* XTrkCad - Model Railroad CAD
@@ -25,6 +23,8 @@
#ifndef SMALLDLG_H
#define SMALLDLG_H
+#include "wlib.h"
+
#define SHOWTIP_NEXTTIP (0L)
#define SHOWTIP_PREVTIP (1L)
#define SHOWTIP_FORCESHOW (2L)
diff --git a/app/bin/tbezier.c b/app/bin/tbezier.c
new file mode 100644
index 0000000..8d7ca6c
--- /dev/null
+++ b/app/bin/tbezier.c
@@ -0,0 +1,1629 @@
+/** \file tbezier.c
+ * XTrkCad - Model Railroad CAD
+ * Copyright (C) 2005 Dave Bullis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, 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 License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *****************************************************************************
+ * BEZIER TRACK (and LINE)
+ *
+ * tbezier.c has all the Track functions (for both T_BEZIER and T_BEZLIN) but all the heavy-math-lifting is delegated to cbezier.c
+ *
+ * Both Bezier Tracks and Lines are defined with two end points and two control points. Each end and control point pair is joined by a control arm.
+ * The angle between the control and end point (arm angle) determines the angle at the end point.
+ * The way the curve moves in between the ends is driven by the relative lengths of the two control arms.
+ * In general, lengthening one arm while keeping the other arm length fixed results in a curve that changes more slowly from the lengthened end and more swiftly from the other.
+ * Very un-prototypical track curves are easy to draw with Bezier, so beware!
+ *
+ * Another large user of tbezier.c is the Cornu function by way of its support for Bezier segments, which are used to approximate Cornu curves.
+ *
+ * In XTrkcad, Bezier curves are rendered into a set of Curved and Straight segments for display. This set is also saved, although the code recalculates a fresh set upon reload.
+ *
+ */
+
+
+#include "track.h"
+#include "draw.h"
+#include "tbezier.h"
+#include "cbezier.h"
+#include "ccurve.h"
+#include "cstraigh.h"
+#include "cjoin.h"
+#include "utility.h"
+#include "i18n.h"
+#include "param.h"
+#include "math.h"
+#include "string.h"
+#include "cundo.h"
+#include "layout.h"
+#include "fileio.h"
+#include "assert.h"
+
+EXPORT TRKTYP_T T_BEZIER = -1;
+EXPORT TRKTYP_T T_BZRLIN = -1;
+
+
+struct extraData {
+ BezierData_t bezierData;
+ };
+
+static int log_bezier = 0;
+
+static DIST_T GetLengthBezier( track_p );
+
+/****************************************
+ *
+ * UTILITIES
+ *
+ */
+
+/*
+ * Run after any changes to the Bezier points
+ */
+EXPORT void FixUpBezier(coOrd pos[4], struct extraData* xx, BOOL_T track) {
+ xx->bezierData.a0 = NormalizeAngle(FindAngle(pos[1], pos[0]));
+ xx->bezierData.a1 = NormalizeAngle(FindAngle(pos[2], pos[3]));
+
+ ConvertToArcs(pos, &xx->bezierData.arcSegs, track, xx->bezierData.segsColor,
+ xx->bezierData.segsWidth);
+ xx->bezierData.minCurveRadius = BezierMinRadius(pos,
+ xx->bezierData.arcSegs);
+ xx->bezierData.length = BezierLength(pos, xx->bezierData.arcSegs);
+}
+
+/*
+ * Run after any changes to the Bezier points for a Segment
+ */
+EXPORT void FixUpBezierSeg(coOrd pos[4], trkSeg_p p, BOOL_T track) {
+ p->u.b.angle0 = NormalizeAngle(FindAngle(pos[1], pos[0]));
+ p->u.b.angle3 = NormalizeAngle(FindAngle(pos[2], pos[3]));
+ ConvertToArcs(pos, &p->bezSegs, track, p->color,
+ p->width);
+ p->u.b.minRadius = BezierMinRadius(pos,
+ p->bezSegs);
+ p->u.b.length = BezierLength(pos, p->bezSegs);
+}
+
+EXPORT void FixUpBezierSegs(trkSeg_p p,int segCnt) {
+ for (int i=0;i<segCnt;i++,p++) {
+ if (p->type == SEG_BEZTRK || p->type == SEG_BEZLIN) {
+ FixUpBezierSeg(p->u.b.pos,p,p->type == SEG_BEZTRK);
+ }
+ }
+}
+
+
+static void GetBezierAngles( ANGLE_T *a0, ANGLE_T *a1, track_p trk )
+{
+ assert( trk != NULL );
+
+ *a0 = NormalizeAngle( GetTrkEndAngle(trk,0) );
+ *a1 = NormalizeAngle( GetTrkEndAngle(trk,1) );
+
+ LOG( log_bezier, 4, ( "getBezierAngles: = %0.3f %0.3f\n", *a0, *a1 ) )
+}
+
+
+static void ComputeBezierBoundingBox( track_p trk, struct extraData * xx )
+{
+ coOrd hi, lo;
+ hi.x = lo.x = xx->bezierData.pos[0].x;
+ hi.y = lo.y = xx->bezierData.pos[0].y;
+
+ for (int i=1; i<=3;i++) {
+ hi.x = hi.x < xx->bezierData.pos[i].x ? xx->bezierData.pos[i].x : hi.x;
+ hi.y = hi.y < xx->bezierData.pos[i].y ? xx->bezierData.pos[i].y : hi.y;
+ lo.x = lo.x > xx->bezierData.pos[i].x ? xx->bezierData.pos[i].x : lo.x;
+ lo.y = lo.y > xx->bezierData.pos[i].y ? xx->bezierData.pos[i].y : lo.y;
+ }
+ SetBoundingBox( trk, hi, lo );
+}
+
+
+DIST_T BezierDescriptionDistance(
+ coOrd pos,
+ track_p trk )
+{
+ struct extraData *xx = GetTrkExtraData(trk);
+ coOrd p1;
+
+ if ( GetTrkType( trk ) != T_BEZIER || ( GetTrkBits( trk ) & TB_HIDEDESC ) != 0 )
+ return 100000;
+
+ p1.x = xx->bezierData.pos[0].x + ((xx->bezierData.pos[3].x-xx->bezierData.pos[0].x)/2) + xx->bezierData.descriptionOff.x;
+ p1.y = xx->bezierData.pos[0].y + ((xx->bezierData.pos[3].y-xx->bezierData.pos[0].y)/2) + xx->bezierData.descriptionOff.y;
+
+ return FindDistance( p1, pos );
+}
+
+
+static void DrawBezierDescription(
+ track_p trk,
+ drawCmd_p d,
+ wDrawColor color )
+{
+ struct extraData *xx = GetTrkExtraData(trk);
+ wFont_p fp;
+ coOrd pos;
+
+ if (layoutLabels == 0)
+ return;
+ if ((labelEnable&LABELENABLE_TRKDESC)==0)
+ return;
+ pos.x = xx->bezierData.pos[0].x + ((xx->bezierData.pos[3].x - xx->bezierData.pos[0].x)/2);
+ pos.y = xx->bezierData.pos[0].y + ((xx->bezierData.pos[3].y - xx->bezierData.pos[0].y)/2);
+ pos.x += xx->bezierData.descriptionOff.x;
+ pos.y += xx->bezierData.descriptionOff.y;
+ fp = wStandardFont( F_TIMES, FALSE, FALSE );
+ sprintf( message, _("Bezier Curve: length=%s min radius=%s"),
+ FormatDistance(xx->bezierData.length), FormatDistance(xx->bezierData.minCurveRadius));
+ DrawBoxedString( BOX_BOX, d, pos, message, fp, (wFontSize_t)descriptionFontSize, color, 0.0 );
+}
+
+
+STATUS_T BezierDescriptionMove(
+ track_p trk,
+ wAction_t action,
+ coOrd pos )
+{
+ struct extraData *xx = GetTrkExtraData(trk);
+ static coOrd p0,p1;
+ static BOOL_T editState;
+ wDrawColor color;
+ if (GetTrkType(trk) != T_BEZIER) return C_TERMINATE;
+ p0.x = xx->bezierData.pos[0].x + ((xx->bezierData.pos[3].x - xx->bezierData.pos[0].x)/2);
+ p0.y = xx->bezierData.pos[0].y + ((xx->bezierData.pos[3].y - xx->bezierData.pos[0].y)/2);
+ switch (action) {
+ case C_DOWN:
+ case C_MOVE:
+ case C_UP:
+ editState = TRUE;
+ p1 = pos;
+ color = GetTrkColor( trk, &mainD );
+ DrawLine( &mainD, p0, pos, 0, wDrawColorBlack );
+ xx->bezierData.descriptionOff.x = pos.x - p0.x;
+ xx->bezierData.descriptionOff.y = pos.y - p0.y;
+ if (action == C_UP) {
+ editState = FALSE;
+ }
+ MainRedraw();
+ MapRedraw();
+ return action==C_UP?C_TERMINATE:C_CONTINUE;
+ case C_REDRAW:
+ if (editState)
+ DrawLine( &mainD, p1, p0, 0, wDrawColorBlack );
+ break;
+
+
+ }
+ return C_CONTINUE;
+}
+
+/****************************************
+ *
+ * GENERIC FUNCTIONS
+ *
+ */
+
+static struct {
+ coOrd pos[4];
+ FLOAT_T elev[2];
+ FLOAT_T length;
+ DIST_T minRadius;
+ FLOAT_T grade;
+ unsigned int layerNumber;
+ ANGLE_T angle[2];
+ DIST_T radius[2];
+ coOrd center[2];
+ dynArr_t segs;
+ long width;
+ wDrawColor color;
+ } bezData;
+typedef enum { P0, A0, R0, C0, Z0, CP1, CP2, P1, A1, R1, C1, Z1, RA, LN, GR, LY, WI, CO } crvDesc_e;
+static descData_t bezDesc[] = {
+/*P0*/ { DESC_POS, N_("End Pt 1: X,Y"), &bezData.pos[0] },
+/*A0*/ { DESC_ANGLE, N_("End Angle"), &bezData.angle[0] },
+/*R0*/ { DESC_DIM, N_("Radius"), &bezData.radius[0] },
+/*C0*/ { DESC_POS, N_("Center X,Y"), &bezData.center[0]},
+/*Z0*/ { DESC_DIM, N_("Z1"), &bezData.elev[0] },
+/*CP1*/ { DESC_POS, N_("Ctl Pt 1: X,Y"), &bezData.pos[1] },
+/*CP2*/ { DESC_POS, N_("Ctl Pt 2: X,Y"), &bezData.pos[2] },
+/*P1*/ { DESC_POS, N_("End Pt 2: X,Y"), &bezData.pos[3] },
+/*A1*/ { DESC_ANGLE, N_("End Angle"), &bezData.angle[1] },
+/*R1*/ { DESC_DIM, N_("Radius"), &bezData.radius[1] },
+/*C1*/ { DESC_POS, N_("Center X,Y"), &bezData.center[1]},
+/*Z1*/ { DESC_DIM, N_("Z2"), &bezData.elev[1] },
+/*RA*/ { DESC_DIM, N_("MinRadius"), &bezData.radius },
+/*LN*/ { DESC_DIM, N_("Length"), &bezData.length },
+/*GR*/ { DESC_FLOAT, N_("Grade"), &bezData.grade },
+/*LY*/ { DESC_LAYER, N_("Layer"), &bezData.layerNumber },
+/*WI*/ { DESC_LONG, N_("Line Width"), &bezData.width},
+/*CO*/ { DESC_COLOR, N_("Line Color"), &bezData.color},
+ { DESC_NULL } };
+
+static void UpdateBezier( track_p trk, int inx, descData_p descUpd, BOOL_T final )
+{
+ struct extraData *xx = GetTrkExtraData(trk);
+ BOOL_T updateEndPts;
+ EPINX_T ep;
+ ANGLE_T angle1, angle2;
+
+ if ( inx == -1 )
+ return;
+ updateEndPts = FALSE;
+ switch ( inx ) {
+ case P0:
+ if (GetTrkEndTrk(trk,0)) break;
+ updateEndPts = TRUE;
+ xx->bezierData.pos[0] = bezData.pos[0];
+ bezDesc[P0].mode |= DESC_CHANGE;
+ /* no break */
+ case P1:
+ if (GetTrkEndTrk(trk,0) && GetTrkEndTrk(trk,1)) break;
+ updateEndPts = TRUE;
+ xx->bezierData.pos[3]= bezData.pos[3];
+ bezDesc[P1].mode |= DESC_CHANGE;
+ break;
+ case A0:
+ case A1:
+ break;
+ case CP1:
+ if (GetTrkEndTrk(trk,0)) {
+ angle1 = NormalizeAngle(GetTrkEndAngle(trk,0));
+ angle2 = NormalizeAngle(FindAngle(bezData.pos[1], xx->bezierData.pos[0])-angle1);
+ if (angle2 > 90.0 && angle2 < 270.0)
+ Translate( &bezData.pos[1], xx->bezierData.pos[0], angle1, -FindDistance( xx->bezierData.pos[0], bezData.pos[1] )*cos(D2R(angle2)));
+ }
+ xx->bezierData.pos[1] = bezData.pos[1];
+ bezDesc[CP1].mode |= DESC_CHANGE;
+ updateEndPts = TRUE;
+ break;
+ case CP2:
+ if (GetTrkEndTrk(trk,1)) {
+ angle1 = NormalizeAngle(GetTrkEndAngle(trk,1));
+ angle2 = NormalizeAngle(FindAngle(bezData.pos[2], xx->bezierData.pos[3])-angle1);
+ if (angle2 > 90.0 && angle2 < 270.0)
+ Translate( &bezData.pos[2], xx->bezierData.pos[3], angle1, -FindDistance( xx->bezierData.pos[3], bezData.pos[0] )*cos(D2R(angle2)));
+ }
+ xx->bezierData.pos[2] = bezData.pos[2];
+ bezDesc[CP2].mode |= DESC_CHANGE;
+ updateEndPts = TRUE;
+ break;
+ case Z0:
+ case Z1:
+ ep = (inx==Z0?0:1);
+ UpdateTrkEndElev( trk, ep, GetTrkEndElevUnmaskedMode(trk,ep), bezData.elev[ep], NULL );
+ ComputeElev( trk, 1-ep, FALSE, &bezData.elev[1-ep], NULL );
+ if ( bezData.length > minLength )
+ bezData.grade = fabs( (bezData.elev[0]-bezData.elev[1])/bezData.length )*100.0;
+ else
+ bezData.grade = 0.0;
+ bezDesc[GR].mode |= DESC_CHANGE;
+ bezDesc[inx==Z0?Z1:Z0].mode |= DESC_CHANGE;
+ return;
+ case LY:
+ SetTrkLayer( trk, bezData.layerNumber);
+ break;
+ case WI:
+ xx->bezierData.segsWidth = bezData.width/mainD.dpi;
+ break;
+ case CO:
+ xx->bezierData.segsColor = bezData.color;
+ break;
+ default:
+ AbortProg( "updateBezier: Bad inx %d", inx );
+ }
+ ConvertToArcs(xx->bezierData.pos, &xx->bezierData.arcSegs, IsTrack(trk)?TRUE:FALSE, xx->bezierData.segsColor, xx->bezierData.segsWidth);
+ trackParams_t params;
+ for (int i=0;i<2;i++) {
+ GetTrackParams(0,trk,xx->bezierData.pos[i],&params);
+ bezData.radius[i] = params.arcR;
+ bezData.center[i] = params.arcP;
+ }
+ if (updateEndPts) {
+ if ( GetTrkEndTrk(trk,0) == NULL ) {
+ SetTrkEndPoint( trk, 0, bezData.pos[0], NormalizeAngle( FindAngle(bezData.pos[0], bezData.pos[1]) ) );
+ bezData.angle[0] = GetTrkEndAngle(trk,0);
+ bezDesc[A0].mode |= DESC_CHANGE;
+ GetTrackParams(PARAMS_CORNU,trk,xx->bezierData.pos[0],&params);
+ bezData.radius[0] = params.arcR;
+ bezData.center[0] = params.arcP;
+ }
+ if ( GetTrkEndTrk(trk,1) == NULL ) {
+ SetTrkEndPoint( trk, 1, bezData.pos[3], NormalizeAngle( FindAngle(bezData.pos[2], bezData.pos[3]) ) );
+ bezData.angle[1] = GetTrkEndAngle(trk,1);
+ bezDesc[A1].mode |= DESC_CHANGE;
+ GetTrackParams(PARAMS_CORNU,trk,xx->bezierData.pos[1],&params);
+ bezData.radius[1] = params.arcR;
+ bezData.center[1] = params.arcP;
+ }
+ }
+
+ FixUpBezier(xx->bezierData.pos, xx, IsTrack(trk));
+ ComputeBezierBoundingBox(trk, xx);
+ DrawNewTrack( trk );
+}
+
+static void DescribeBezier( track_p trk, char * str, CSIZE_T len )
+{
+ struct extraData *xx = GetTrkExtraData(trk);
+ DIST_T d;
+ int fix0, fix1 = 0;
+
+ d = xx->bezierData.length;
+ sprintf( str, _("Bezier %s(%d): Layer=%u MinRadius=%s Length=%s EP=[%0.3f,%0.3f] [%0.3f,%0.3f] CP1=[%0.3f,%0.3f] CP2=[%0.3f, %0.3f]"),
+ GetTrkType(trk)==T_BEZIER?"Track":"Line",
+ GetTrkIndex(trk),
+ GetTrkLayer(trk)+1,
+ FormatDistance(xx->bezierData.minCurveRadius),
+ FormatDistance(d),
+ PutDim(xx->bezierData.pos[0].x),PutDim(xx->bezierData.pos[0].y),
+ PutDim(xx->bezierData.pos[3].x),PutDim(xx->bezierData.pos[3].y),
+ PutDim(xx->bezierData.pos[1].x),PutDim(xx->bezierData.pos[1].y),
+ PutDim(xx->bezierData.pos[2].x),PutDim(xx->bezierData.pos[2].y));
+
+ if (GetTrkType(trk) == T_BEZIER) {
+ fix0 = GetTrkEndTrk(trk,0)!=NULL;
+ fix1 = GetTrkEndTrk(trk,1)!=NULL;
+ }
+
+ bezData.length = GetLengthBezier(trk);
+ bezData.minRadius = xx->bezierData.minCurveRadius;
+ if (bezData.minRadius >= 100000.00) bezData.minRadius = 0;
+ bezData.layerNumber = GetTrkLayer(trk);
+ bezData.pos[0] = xx->bezierData.pos[0];
+ bezData.pos[1] = xx->bezierData.pos[1];
+ bezData.pos[2] = xx->bezierData.pos[2];
+ bezData.pos[3] = xx->bezierData.pos[3];
+ bezData.angle[0] = xx->bezierData.a0;
+ bezData.angle[1] = xx->bezierData.a1;
+ trackParams_t params;
+ GetTrackParams(PARAMS_CORNU,trk,xx->bezierData.pos[0],&params);
+ bezData.radius[0] = params.arcR;
+ bezData.center[0] = params.arcP;
+ GetTrackParams(PARAMS_CORNU,trk,xx->bezierData.pos[3],&params);
+ bezData.radius[1] = params.arcR;
+ bezData.center[1] = params.arcP;
+
+ if (GetTrkType(trk) == T_BEZIER) {
+ ComputeElev( trk, 0, FALSE, &bezData.elev[0], NULL );
+ ComputeElev( trk, 1, FALSE, &bezData.elev[1], NULL );
+
+ if ( bezData.length > minLength )
+ bezData.grade = fabs( (bezData.elev[0]-bezData.elev[1])/bezData.length )*100.0;
+ else
+ bezData.grade = 0.0;
+ }
+
+ bezDesc[P0].mode = fix0?DESC_RO:0;
+ bezDesc[P1].mode = fix1?DESC_RO:0;
+ bezDesc[LN].mode = DESC_RO;
+ bezDesc[CP1].mode = 0;
+ bezDesc[CP2].mode = 0;
+ if (GetTrkType(trk) == T_BEZIER) {
+ bezDesc[Z0].mode = EndPtIsDefinedElev(trk,0)?0:DESC_RO;
+ bezDesc[Z1].mode = EndPtIsDefinedElev(trk,1)?0:DESC_RO;
+ }
+ else
+ bezDesc[Z0].mode = bezDesc[Z1].mode = DESC_IGNORE;
+ bezDesc[A0].mode = DESC_RO;
+ bezDesc[A1].mode = DESC_RO;
+ bezDesc[C0].mode = DESC_RO;
+ bezDesc[C1].mode = DESC_RO;
+ bezDesc[R0].mode = DESC_RO;
+ bezDesc[R1].mode = DESC_RO;
+ bezDesc[GR].mode = DESC_RO;
+ bezDesc[RA].mode = DESC_RO;
+ bezDesc[LY].mode = DESC_NOREDRAW;
+ bezData.width = (long)floor(xx->bezierData.segsWidth*mainD.dpi+0.5);
+ bezDesc[WI].mode = GetTrkType(trk) == T_BEZIER?DESC_IGNORE:0;
+ bezData.color = xx->bezierData.segsColor;
+ bezDesc[CO].mode = GetTrkType(trk) == T_BEZIER?DESC_IGNORE:0;
+
+ if (GetTrkType(trk) == T_BEZIER)
+ DoDescribe( _("Bezier Track"), trk, bezDesc, UpdateBezier );
+ else
+ DoDescribe( _("Bezier Line"), trk, bezDesc, UpdateBezier );
+
+}
+
+static DIST_T DistanceBezier( track_p t, coOrd * p )
+{
+ struct extraData *xx = GetTrkExtraData(t);
+
+ DIST_T d = 100000.0;
+ coOrd p2 = xx->bezierData.pos[0]; //Set initial point
+ segProcData_t segProcData;
+ for (int i = 0;i<xx->bezierData.arcSegs.cnt;i++) {
+ segProcData.distance.pos1 = * p;
+ SegProc(SEGPROC_DISTANCE,&DYNARR_N(trkSeg_t,xx->bezierData.arcSegs,i),&segProcData);
+ if (segProcData.distance.dd<d) {
+ d = segProcData.distance.dd;
+ p2 = segProcData.distance.pos1;
+ }
+ }
+ * p = p2;
+ return d;
+}
+
+static void DrawBezier( track_p t, drawCmd_p d, wDrawColor color )
+{
+ struct extraData *xx = GetTrkExtraData(t);
+ long widthOptions = DTS_LEFT|DTS_RIGHT;
+
+
+ if (GetTrkType(t) == T_BZRLIN) {
+ DrawSegsO(d,t,zero,0.0,xx->bezierData.arcSegs.ptr,xx->bezierData.arcSegs.cnt, 0.0, color, 0);
+ return;
+ }
+
+ if (GetTrkWidth(t) == 2)
+ widthOptions |= DTS_THICK2;
+ if (GetTrkWidth(t) == 3)
+ widthOptions |= DTS_THICK3;
+
+
+ if ( ((d->funcs->options&wDrawOptTemp)==0) &&
+ (labelWhen == 2 || (labelWhen == 1 && (d->options&DC_PRINT))) &&
+ labelScale >= d->scale &&
+ ( GetTrkBits( t ) & TB_HIDEDESC ) == 0 ) {
+ DrawBezierDescription( t, d, color );
+ }
+ DIST_T scale2rail = (d->options&DC_PRINT)?(twoRailScale*2+1):twoRailScale;
+ if ( tieDrawMode!=TIEDRAWMODE_NONE &&
+ d!=&mapD &&
+ (d->options&DC_TIES)!=0 &&
+ d->scale<scale2rail/2 )
+ DrawSegsO(d,t,zero,0.0,xx->bezierData.arcSegs.ptr,xx->bezierData.arcSegs.cnt, GetTrkGauge(t), color, widthOptions|DTS_TIES);
+ DrawSegsO(d,t,zero,0.0,xx->bezierData.arcSegs.ptr,xx->bezierData.arcSegs.cnt, GetTrkGauge(t), color, widthOptions);
+ if ( (d->funcs->options & wDrawOptTemp) == 0 &&
+ (d->options&DC_QUICK) == 0 ) {
+ DrawEndPt( d, t, 0, color );
+ DrawEndPt( d, t, 1, color );
+ }
+}
+
+static void DeleteBezier( track_p t )
+{
+ struct extraData *xx = GetTrkExtraData(t);
+
+ for (int i=0;i<xx->bezierData.arcSegs.cnt;i++) {
+ trkSeg_t s = DYNARR_N(trkSeg_t,xx->bezierData.arcSegs,i);
+ if (s.type == SEG_BEZTRK || s.type == SEG_BEZLIN) {
+ if (s.bezSegs.ptr) MyFree(s.bezSegs.ptr);
+ s.bezSegs.max = 0;
+ s.bezSegs.cnt = 0;
+ }
+ }
+ if (xx->bezierData.arcSegs.ptr && !xx->bezierData.arcSegs.max)
+ MyFree(xx->bezierData.arcSegs.ptr);
+ xx->bezierData.arcSegs.max = 0;
+ xx->bezierData.arcSegs.cnt = 0;
+ xx->bezierData.arcSegs.ptr = NULL;
+}
+
+static BOOL_T WriteBezier( track_p t, FILE * f )
+{
+ struct extraData *xx = GetTrkExtraData(t);
+ long options;
+ BOOL_T rc = TRUE;
+ BOOL_T track =(GetTrkType(t)==T_BEZIER);
+ options = GetTrkWidth(t) & 0x0F;
+ if ( ( GetTrkBits(t) & TB_HIDEDESC ) == 0 ) options |= 0x80;
+ rc &= fprintf(f, "%s %d %u %ld %ld %0.6f %s %d %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f 0 %0.6f %0.6f \n",
+ track?"BEZIER":"BZRLIN",GetTrkIndex(t), GetTrkLayer(t), (long)options, wDrawGetRGB(xx->bezierData.segsColor), xx->bezierData.segsWidth,
+ GetTrkScaleName(t), GetTrkVisible(t),
+ xx->bezierData.pos[0].x, xx->bezierData.pos[0].y,
+ xx->bezierData.pos[1].x, xx->bezierData.pos[1].y,
+ xx->bezierData.pos[2].x, xx->bezierData.pos[2].y,
+ xx->bezierData.pos[3].x, xx->bezierData.pos[3].y,
+ xx->bezierData.descriptionOff.x, xx->bezierData.descriptionOff.y )>0;
+ if (track) {
+ rc &= WriteEndPt( f, t, 0 );
+ rc &= WriteEndPt( f, t, 1 );
+ }
+ rc &= WriteSegs( f, xx->bezierData.arcSegs.cnt, xx->bezierData.arcSegs.ptr );
+ return rc;
+}
+
+static void ReadBezier( char * line )
+{
+ struct extraData *xx;
+ track_p t;
+ wIndex_t index;
+ BOOL_T visible;
+ coOrd p0, c1, c2, p1, dp;
+ char scale[10];
+ wIndex_t layer;
+ long options;
+ char * cp = NULL;
+ unsigned long rgb;
+ DIST_T width;
+
+ if (!GetArgs( line+6, "dLluwsdpppp0p",
+ &index, &layer, &options, &rgb, &width, scale, &visible, &p0, &c1, &c2, &p1, &dp ) ) {
+ return;
+ }
+ if (strncmp(line,"BEZIER",6)==0)
+ t = NewTrack( index, T_BEZIER, 0, sizeof *xx );
+ else
+ t = NewTrack( index, T_BZRLIN, 0, sizeof *xx );
+ xx = GetTrkExtraData(t);
+ SetTrkVisible(t, visible);
+ SetTrkScale(t, LookupScale(scale));
+ SetTrkLayer(t, layer );
+ SetTrkWidth(t, (int)(options&0x0F));
+ if ( ( options & 0x80 ) == 0 ) SetTrkBits(t,TB_HIDEDESC);
+ xx->bezierData.pos[0] = p0;
+ xx->bezierData.pos[1] = c1;
+ xx->bezierData.pos[2] = c2;
+ xx->bezierData.pos[3] = p1;
+ xx->bezierData.descriptionOff = dp;
+ xx->bezierData.segsWidth = width;
+ xx->bezierData.segsColor = wDrawFindColor( rgb );
+ ReadSegs();
+ FixUpBezier(xx->bezierData.pos,xx,GetTrkType(t) == T_BEZIER);
+ ComputeBezierBoundingBox(t,xx);
+ if (GetTrkType(t) == T_BEZIER) {
+ SetEndPts(t,2);
+ }
+}
+
+static void MoveBezier( track_p trk, coOrd orig )
+{
+ struct extraData *xx = GetTrkExtraData(trk);
+ UndoModify(trk);
+ for (int i=0;i<4;i++) {
+ xx->bezierData.pos[i].x += orig.x;
+ xx->bezierData.pos[i].y += orig.y;
+ }
+ FixUpBezier(xx->bezierData.pos,xx,IsTrack(trk));
+ ComputeBezierBoundingBox(trk,xx);
+
+}
+
+static void RotateBezier( track_p trk, coOrd orig, ANGLE_T angle )
+{
+ struct extraData *xx = GetTrkExtraData(trk);
+ UndoModify(trk);
+ for (int i=0;i<5;i++) {
+ Rotate( &xx->bezierData.pos[i], orig, angle );
+ }
+ FixUpBezier(xx->bezierData.pos,xx,IsTrack(trk));
+ ComputeBezierBoundingBox(trk,xx);
+
+}
+
+static void RescaleBezier( track_p trk, FLOAT_T ratio )
+{
+ struct extraData *xx = GetTrkExtraData(trk);
+ UndoModify(trk);
+ xx->bezierData.pos[0].x *= ratio;
+ xx->bezierData.pos[0].y *= ratio;
+ xx->bezierData.pos[1].x *= ratio;
+ xx->bezierData.pos[1].y *= ratio;
+ xx->bezierData.pos[2].x *= ratio;
+ xx->bezierData.pos[2].y *= ratio;
+ xx->bezierData.pos[3].x *= ratio;
+ xx->bezierData.pos[3].y *= ratio;
+ FixUpBezier(xx->bezierData.pos,xx,IsTrack(trk));
+ ComputeBezierBoundingBox(trk,xx);
+
+}
+
+EXPORT void AdjustBezierEndPt( track_p trk, EPINX_T inx, coOrd pos ) {
+ struct extraData *xx = GetTrkExtraData(trk);
+ UndoModify(trk);
+ if (inx ==0 ) {
+ xx->bezierData.pos[1].x += -xx->bezierData.pos[0].x+pos.x;
+ xx->bezierData.pos[1].y += -xx->bezierData.pos[0].y+pos.y;
+ xx->bezierData.pos[0] = pos;
+ }
+ else {
+ xx->bezierData.pos[2].x += -xx->bezierData.pos[3].x+pos.x;
+ xx->bezierData.pos[2].y += -xx->bezierData.pos[3].y+pos.y;
+ xx->bezierData.pos[3] = pos;
+ }
+ FixUpBezier(xx->bezierData.pos, xx, IsTrack(trk));
+ ComputeBezierBoundingBox(trk,xx);
+ SetTrkEndPoint( trk, inx, pos, inx==0?xx->bezierData.a0:xx->bezierData.a1);
+}
+
+
+/**
+ * Split the Track at approximately the point pos.
+ */
+static BOOL_T SplitBezier( track_p trk, coOrd pos, EPINX_T ep, track_p *leftover, EPINX_T * ep0, EPINX_T * ep1 )
+{
+ struct extraData *xx = GetTrkExtraData(trk);
+ track_p trk1;
+ double t;
+ BOOL_T track;
+ track = IsTrack(trk);
+
+ coOrd current[4], newl[4], newr[4];
+
+ double dd = DistanceBezier(trk, &pos);
+ if (dd>minLength) return FALSE;
+
+ BezierMathDistance(&pos, xx->bezierData.pos, 500, &t); //Find t value
+
+ for (int i=0;i<4;i++) {
+ current[i] = xx->bezierData.pos[i];
+ }
+
+ BezierSplit(current, newl, newr, t);
+
+ if (track) {
+ trk1 = NewBezierTrack(ep?newr:newl,NULL,0);
+ } else
+ trk1 = NewBezierLine(ep?newr:newl,NULL,0, xx->bezierData.segsColor,xx->bezierData.segsWidth);
+ UndoModify(trk);
+ for (int i=0;i<4;i++) {
+ xx->bezierData.pos[i] = ep?newl[i]:newr[i];
+ }
+ FixUpBezier(xx->bezierData.pos,xx,track);
+ ComputeBezierBoundingBox(trk,xx);
+ SetTrkEndPoint( trk, ep, xx->bezierData.pos[ep?3:0], ep?xx->bezierData.a1:xx->bezierData.a0);
+
+ *leftover = trk1;
+ *ep0 = 1-ep;
+ *ep1 = ep;
+
+ return TRUE;
+}
+
+static int log_traverseBezier = 0;
+static int log_bezierSegments = 0;
+/*
+ * TraverseBezier is used to position a train/car.
+ * We find a new position and angle given a current pos, angle and a distance to travel.
+ *
+ * The output can be TRUE -> we have moved the point to a new point or to the start/end of the next track
+ * FALSE -> we have not found that point because pos was not on/near the track
+ *
+ * If true we supply the remaining distance to go (always positive).
+ * We detect the movement direction by comparing the current angle to the angle of the track at the point.
+ *
+ * Each segment may be processed forwards or in reverse (this really only applies to curved segments).
+ * So for each segment we call traverse1 to get the direction and extra distance to go to get to the current point
+ * and then use that for traverse2 to actually move to the new point
+ *
+ * If we exceed the current point's segment we move on to the next until the end of this track or we have found the spot.
+ *
+ */
+static BOOL_T TraverseBezier( traverseTrack_p trvTrk, DIST_T * distR )
+{
+ track_p trk = trvTrk->trk;
+ struct extraData *xx = GetTrkExtraData(trk);
+ DIST_T dist = *distR;
+ segProcData_t segProcData;
+ BOOL_T segs_backwards= FALSE;
+ DIST_T d = 10000;
+ coOrd pos2 = trvTrk->pos;
+ ANGLE_T a1,a2;
+ int inx,segInx = 0;
+ EPINX_T ep;
+ BOOL_T back,neg;
+ trkSeg_p segPtr = (trkSeg_p)xx->bezierData.arcSegs.ptr;
+
+ a2 = GetAngleSegs( //Find correct Segment and nearest point in it
+ xx->bezierData.arcSegs.cnt,segPtr,
+ &pos2, &segInx, &d , &back, NULL, &neg ); //d = how far pos2 from old pos2 = trvTrk->pos
+
+ if ( d > 10 ) {
+ ErrorMessage( "traverseBezier: Position is not near track: %0.3f", d );
+ return FALSE; //This means the input pos is not on or close to the track.
+ }
+ if (back) a2 = NormalizeAngle(a2+180); //GetAngleSegs has reversed angle for backwards
+ a1 = NormalizeAngle(a2-trvTrk->angle); //Establish if we are going fwds or backwards globally
+ if (a1 <270 && a1>90) { //Must add 180 if the seg is reversed or inverted (but not both)
+ segs_backwards = TRUE;
+ ep = 0;
+ } else {
+ segs_backwards = FALSE;
+ ep = 1;
+ }
+ if ( neg ) {
+ segs_backwards = !segs_backwards; //neg implies all the segs are reversed
+ ep = 1-ep; //other end
+ }
+
+ segProcData.traverse1.pos = pos2; //actual point on curve
+ segProcData.traverse1.angle = trvTrk->angle; //direction car is going for Traverse 1 has to be reversed...
+LOG( log_traverseBezier, 1, ( " TraverseBezier [%0.3f %0.3f] D%0.3f A%0.3f SB%d \n", trvTrk->pos.x, trvTrk->pos.y, dist, trvTrk->angle, segs_backwards ) )
+ inx = segInx;
+ while (inx >=0 && inx<xx->bezierData.arcSegs.cnt) {
+ segPtr = (trkSeg_p)xx->bezierData.arcSegs.ptr+inx; //move in to the identified segment
+ SegProc( SEGPROC_TRAVERSE1, segPtr, &segProcData ); //Backwards or forwards for THIS segment - note that this can differ from segs_backward!!
+ BOOL_T backwards = segProcData.traverse1.backwards; //Are we going to EP0?
+ BOOL_T reverse_seg = segProcData.traverse1.reverse_seg; //is it a backwards segment (we don't actually care as Traverse1 takes care of it)
+
+ dist += segProcData.traverse1.dist;
+ segProcData.traverse2.dist = dist;
+ segProcData.traverse2.segDir = backwards;
+LOG( log_traverseBezier, 2, ( " TraverseBezierT1 D%0.3f B%d RS%d \n", dist, backwards, reverse_seg ) )
+ SegProc( SEGPROC_TRAVERSE2, segPtr, &segProcData ); //Angle at pos2
+ if ( segProcData.traverse2.dist <= 0 ) { //-ve or zero distance left over so stop there
+ *distR = 0;
+ trvTrk->pos = segProcData.traverse2.pos;
+ trvTrk->angle = segProcData.traverse2.angle;
+LOG( log_traverseBezier, 1, ( " -> [%0.3f %0.3f] A%0.3f D%0.3f\n", trvTrk->pos.x, trvTrk->pos.y, trvTrk->angle, *distR ) )
+ return TRUE;
+ } //NOTE Traverse1 and Traverse2 are overlays so get all out before storing
+ dist = segProcData.traverse2.dist; //How far left?
+ coOrd pos = segProcData.traverse2.pos; //Will be at seg end
+ ANGLE_T angle = segProcData.traverse2.angle; //Angle of end
+ segProcData.traverse1.angle = angle; //Reverse to suit Traverse1
+ segProcData.traverse1.pos = pos;
+ inx = segs_backwards?inx-1:inx+1; //Here's where the global segment direction comes in
+LOG( log_traverseBezier, 2, ( " TraverseBezierL D%0.3f A%0.3f\n", dist, angle ) )
+ }
+ *distR = dist; //Tell caller what is left
+ //Must be at one end or another
+ trvTrk->pos = GetTrkEndPos(trk,ep);
+ trvTrk->angle = NormalizeAngle(GetTrkEndAngle(trk, ep)+(segs_backwards?180:0));
+ trvTrk->trk = GetTrkEndTrk(trk,ep); //go to next track
+ if (trvTrk->trk==NULL) {
+ trvTrk->pos = pos2;
+ return TRUE;
+ }
+
+LOG( log_traverseBezier, 1, ( " -> [%0.3f %0.3f] A%0.3f D%0.3f\n", trvTrk->pos.x, trvTrk->pos.y, trvTrk->angle, *distR ) )
+ return TRUE;
+
+}
+
+
+static BOOL_T MergeBezier(
+ track_p trk0,
+ EPINX_T ep0,
+ track_p trk1,
+ EPINX_T ep1 )
+{
+ struct extraData *xx0 = GetTrkExtraData(trk0);
+ struct extraData *xx1 = GetTrkExtraData(trk1);
+ track_p trk2 = NULL;
+ EPINX_T ep2=-1;
+ BOOL_T tracks = FALSE;
+
+ if (IsTrack(trk0) && IsTrack(trk1) ) tracks = TRUE;
+ if (GetTrkType(trk0) != GetTrkType(trk1)) return FALSE;
+
+ if (ep0 == ep1)
+ return FALSE;
+
+ UndoStart( _("Merge Bezier"), "MergeBezier( T%d[%d] T%d[%d] )", GetTrkIndex(trk0), ep0, GetTrkIndex(trk1), ep1 );
+ UndoModify( trk0 );
+ UndrawNewTrack( trk0 );
+ if (tracks) {
+ trk2 = GetTrkEndTrk( trk1, 1-ep1 );
+ if (trk2) {
+ ep2 = GetEndPtConnectedToMe( trk2, trk1 );
+ DisconnectTracks( trk1, 1-ep1, trk2, ep2 );
+ }
+ }
+ if (ep0 == 0) {
+ xx0->bezierData.pos[3] = xx1->bezierData.pos[3];
+ xx0->bezierData.pos[2] = xx1->bezierData.pos[2];
+ } else {
+ xx0->bezierData.pos[0] = xx1->bezierData.pos[0];
+ xx0->bezierData.pos[1] = xx1->bezierData.pos[1];
+ }
+ FixUpBezier(xx0->bezierData.pos,xx0,tracks);
+ ComputeBezierBoundingBox(trk0,xx0);
+ DeleteTrack( trk1, FALSE );
+ if (tracks && trk2) {
+ if (ep0 == 1)
+ SetTrkEndPoint( trk2, 1, xx0->bezierData.pos[0], xx0->bezierData.a0);
+ else
+ SetTrkEndPoint( trk2, 2, xx0->bezierData.pos[3], xx0->bezierData.a1);
+ ConnectTracks( trk0, ep0, trk2, ep2 );
+ }
+ DrawNewTrack( trk0 );
+
+
+ return TRUE;
+}
+
+
+static BOOL_T EnumerateBezier( track_p trk )
+{
+
+ if (trk != NULL) {
+ DIST_T d;
+ struct extraData *xx = GetTrkExtraData(trk);
+ d = xx->bezierData.length;
+ ScaleLengthIncrement( GetTrkScale(trk), d );
+ }
+ return TRUE;
+}
+
+static DIST_T GetLengthBezier( track_p trk )
+{
+ struct extraData *xx = GetTrkExtraData(trk);
+ DIST_T length = 0.0;
+ segProcData_t segProcData;
+ for(int i=0;i<xx->bezierData.arcSegs.cnt;i++) {
+ SegProc(SEGPROC_LENGTH,&(DYNARR_N(trkSeg_t,xx->bezierData.arcSegs,i)), &segProcData);
+ length += segProcData.length.length;
+ }
+ return length;
+}
+
+
+static BOOL_T GetParamsBezier( int inx, track_p trk, coOrd pos, trackParams_t * params )
+{
+ int segInx;
+ BOOL_T back,negative;
+ DIST_T d;
+
+ params->type = curveTypeBezier;
+ struct extraData *xx = GetTrkExtraData(trk);
+ for (int i=0;i<4;i++) params->bezierPoints[i] = xx->bezierData.pos[i];
+ params->len = xx->bezierData.length;
+ params->track_angle = GetAngleSegs( //Find correct Segment and nearest point in it
+ xx->bezierData.arcSegs.cnt,xx->bezierData.arcSegs.ptr,
+ &pos, &segInx, &d , &back, NULL, &negative );
+ if ( negative != back ) params->track_angle = NormalizeAngle(params->track_angle+180); //Bezier is in reverse
+ trkSeg_p segPtr = &DYNARR_N(trkSeg_t,xx->bezierData.arcSegs,segInx);
+ if (segPtr->type == SEG_STRLIN) {
+ params->arcR = 0.0;
+ } else {
+ params->arcR = fabs(segPtr->u.c.radius);
+ params->arcP = segPtr->u.c.center;
+ params->arcA0 = segPtr->u.c.a0;
+ params->arcA1 = segPtr->u.c.a1;
+ }
+ if ( inx == PARAMS_PARALLEL ) {
+ params->ep = 0;
+ } else if (inx == PARAMS_CORNU ){
+ params->ep = PickEndPoint( pos, trk);
+ } else {
+ params->ep = PickUnconnectedEndPointSilent( pos, trk);
+ }
+ if (params->ep>=0)
+ params->angle = GetTrkEndAngle(trk, params->ep);
+ return TRUE;
+
+}
+
+static BOOL_T TrimBezier( track_p trk, EPINX_T ep, DIST_T dist ) {
+ DeleteTrack(trk, TRUE);
+ return TRUE;
+}
+
+
+
+static BOOL_T QueryBezier( track_p trk, int query )
+{
+ struct extraData * xx = GetTrkExtraData(trk);
+ switch ( query ) {
+ case Q_CAN_GROUP:
+ return FALSE;
+ break;
+ case Q_FLIP_ENDPTS:
+ case Q_HAS_DESC:
+ return TRUE;
+ break;
+ case Q_EXCEPTION:
+ return GetTrkType(trk) == T_BEZIER?xx->bezierData.minCurveRadius < (GetLayoutMinTrackRadius()-EPSILON):FALSE;
+ break;
+ case Q_CAN_MODIFY_CONTROL_POINTS:
+ return TRUE;
+ break;
+ case Q_CANNOT_PLACE_TURNOUT:
+ return FALSE;
+ break;
+ case Q_ISTRACK:
+ return GetTrkType(trk) == T_BEZIER?TRUE:FALSE;
+ break;
+ case Q_CAN_PARALLEL:
+ return (GetTrkType(trk) == T_BEZIER);
+ break;
+ case Q_MODIFY_CAN_SPLIT:
+ case Q_CORNU_CAN_MODIFY:
+ return (GetTrkType(trk) == T_BEZIER);
+ default:
+ return FALSE;
+ }
+}
+
+
+static void FlipBezier(
+ track_p trk,
+ coOrd orig,
+ ANGLE_T angle )
+{
+ struct extraData * xx = GetTrkExtraData(trk);
+ FlipPoint( &xx->bezierData.pos[0], orig, angle );
+ FlipPoint( &xx->bezierData.pos[1], orig, angle );
+ FlipPoint( &xx->bezierData.pos[2], orig, angle );
+ FlipPoint( &xx->bezierData.pos[3], orig, angle );
+ FixUpBezier(xx->bezierData.pos,xx,IsTrack(trk));
+ ComputeBezierBoundingBox(trk,xx);
+
+}
+
+static ANGLE_T GetAngleBezier(
+ track_p trk,
+ coOrd pos,
+ EPINX_T * ep0,
+ EPINX_T * ep1 )
+{
+ struct extraData * xx = GetTrkExtraData(trk);
+ ANGLE_T angle;
+ BOOL_T back, neg;
+ int indx;
+ angle = GetAngleSegs( xx->bezierData.arcSegs.cnt, (trkSeg_p)xx->bezierData.arcSegs.ptr, &pos, &indx, NULL, &back, NULL, &neg );
+ if (!back) angle = NormalizeAngle(angle+180); //Make CCW
+ if ( ep0 ) *ep0 = neg?1:0;
+ if ( ep1 ) *ep1 = neg?0:1;
+ return angle;
+}
+
+BOOL_T GetBezierSegmentFromTrack(track_p trk, trkSeg_p seg_p) {
+ struct extraData * xx = GetTrkExtraData(trk);
+
+ seg_p->type = IsTrack(trk)?SEG_BEZTRK:SEG_BEZLIN;
+ for (int i=0;i<4;i++) seg_p->u.b.pos[i] = xx->bezierData.pos[i];
+ seg_p->color = xx->bezierData.segsColor;
+ seg_p->bezSegs.cnt = 0;
+ if (seg_p->bezSegs.ptr) MyFree(seg_p->bezSegs.ptr);
+ seg_p->bezSegs.max = 0;
+ FixUpBezierSeg(seg_p->u.b.pos,seg_p,seg_p->type == SEG_BEZTRK);
+ return TRUE;
+
+}
+
+
+static BOOL_T MakeParallelBezier(
+ track_p trk,
+ coOrd pos,
+ DIST_T sep,
+ track_p * newTrkR,
+ coOrd * p0R,
+ coOrd * p1R )
+{
+ struct extraData * xx = GetTrkExtraData(trk);
+ coOrd np[4], p;
+ ANGLE_T a,a2;
+
+ //Produce bezier that is translated parallel to the existing Bezier
+ // - not a precise result if the bezier end angles are not in the same general direction.
+ // The expectation is that the user will have to adjust it - unless and until we produce
+ // a new algo to adjust the control points to be parallel to the endpoints.
+
+ a = FindAngle(xx->bezierData.pos[0],xx->bezierData.pos[3]);
+ p = pos;
+ DistanceBezier(trk, &p);
+ a2 = NormalizeAngle(FindAngle(pos,p)-a);
+ //find parallel move x and y for points
+ for (int i =0; i<4;i++) {
+ np[i] = xx->bezierData.pos[i];
+ }
+
+ if ( a2 > 180 ) {
+ Translate(&np[0],np[0],a+90,sep);
+ Translate(&np[1],np[1],a+90,sep);
+ Translate(&np[2],np[2],a+90,sep);
+ Translate(&np[3],np[3],a+90,sep);
+ } else {
+ Translate(&np[0],np[0],a-90,sep);
+ Translate(&np[1],np[1],a-90,sep);
+ Translate(&np[2],np[2],a-90,sep);
+ Translate(&np[3],np[3],a-90,sep);
+ }
+
+ if ( newTrkR ) {
+ *newTrkR = NewBezierTrack( np, NULL, 0);
+ } else {
+ DYNARR_SET( trkSeg_t, tempSegs_da, 1 );
+ tempSegs(0).color = wDrawColorBlack;
+ tempSegs(0).width = 0;
+ tempSegs_da.cnt = 1;
+ tempSegs(0).type = SEG_BEZTRK;
+ if (tempSegs(0).bezSegs.ptr) MyFree(tempSegs(0).bezSegs.ptr);
+ tempSegs(0).bezSegs.max = 0;
+ tempSegs(0).bezSegs.cnt = 0;
+ for (int i=0;i<4;i++) tempSegs(0).u.b.pos[i] = np[i];
+ FixUpBezierSeg(tempSegs(0).u.b.pos,&tempSegs(0),TRUE);
+ }
+ if ( p0R ) *p0R = np[0];
+ if ( p1R ) *p1R = np[1];
+ return TRUE;
+}
+
+/*
+ * When an undo is run, the array of segs is missing - they are not saved to the Undo log. So Undo calls this routine to
+ * ensure
+ * - that the Segs are restored and
+ * - other fields reset.
+ */
+BOOL_T RebuildBezier (track_p trk)
+{
+ struct extraData *xx;
+ xx = GetTrkExtraData(trk);
+ xx->bezierData.arcSegs.cnt = 0;
+ FixUpBezier(xx->bezierData.pos,xx,IsTrack(trk));
+ ComputeBezierBoundingBox(trk, xx);
+ return TRUE;
+}
+
+BOOL_T MoveBezierEndPt ( track_p *trk, EPINX_T *ep, coOrd pos, DIST_T d0 ) {
+ track_p trk2;
+ struct extraData *xx;
+ if (SplitTrack(*trk,pos,*ep,&trk2,TRUE)) {
+ if (trk2) DeleteTrack(trk2,TRUE);
+ xx = GetTrkExtraData(*trk);
+ SetTrkEndPoint( *trk, *ep, *ep?xx->bezierData.pos[3]:xx->bezierData.pos[0], *ep?xx->bezierData.a1:xx->bezierData.a0 );
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static trackCmd_t bezlinCmds = {
+ "BZRLIN",
+ DrawBezier,
+ DistanceBezier,
+ DescribeBezier,
+ DeleteBezier,
+ WriteBezier,
+ ReadBezier,
+ MoveBezier,
+ RotateBezier,
+ RescaleBezier,
+ NULL,
+ GetAngleBezier,
+ SplitBezier,
+ NULL,
+ NULL,
+ NULL, /* redraw */
+ NULL, /* trim */
+ MergeBezier,
+ NULL, /* modify */
+ GetLengthBezier,
+ GetParamsBezier,
+ NULL, /* Move EndPt */
+ QueryBezier,
+ NULL, /* ungroup */
+ FlipBezier,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ RebuildBezier
+ };
+
+static trackCmd_t bezierCmds = {
+ "BEZIER",
+ DrawBezier,
+ DistanceBezier,
+ DescribeBezier,
+ DeleteBezier,
+ WriteBezier,
+ ReadBezier,
+ MoveBezier,
+ RotateBezier,
+ RescaleBezier,
+ NULL,
+ GetAngleBezier,
+ SplitBezier,
+ TraverseBezier,
+ EnumerateBezier,
+ NULL, /* redraw */
+ TrimBezier, /* trim */
+ MergeBezier,
+ NULL, /* modify */
+ GetLengthBezier,
+ GetParamsBezier,
+ MoveBezierEndPt, /* Move EndPt */
+ QueryBezier,
+ NULL, /* ungroup */
+ FlipBezier,
+ NULL,
+ NULL,
+ NULL,
+ MakeParallelBezier,
+ NULL,
+ RebuildBezier
+ };
+
+
+EXPORT void BezierSegProc(
+ segProc_e cmd,
+ trkSeg_p segPtr,
+ segProcData_p data )
+{
+ ANGLE_T a1, a2;
+ DIST_T d, dd;
+ coOrd p0,p2 ;
+ segProcData_t segProcData;
+ trkSeg_p subSegsPtr;
+ coOrd temp0,temp1,temp2,temp3;
+ int inx,segInx;
+ BOOL_T back, segs_backwards, neg;
+#define bezSegs(N) DYNARR_N( trkSeg_t, segPtr->bezSegs, N )
+
+ switch (cmd) {
+
+ case SEGPROC_TRAVERSE1: //Work out how much extra dist and what direction
+ if (segPtr->type != SEG_BEZTRK) {
+ data->traverse1.dist = 0;
+ return;
+ }
+ d = data->traverse1.dist;
+ p0 = data->traverse1.pos;
+LOG( log_bezierSegments, 1, ( " BezTr1-Enter P[%0.3f %0.3f] A%0.3f\n", p0.x, p0.y, data->traverse1.angle ))
+ a2 = GetAngleSegs(segPtr->bezSegs.cnt,segPtr->bezSegs.ptr,&p0,&segInx,&d,&back, NULL, &neg); //Find right seg and pos
+ inx = segInx;
+ data->traverse1.BezSegInx = segInx;
+ data->traverse1.reverse_seg = FALSE;
+ data->traverse1.backwards = FALSE;
+ if (d>10) {
+ data->traverse1.dist = 0;
+ return;
+ }
+
+ if (back) a2 = NormalizeAngle(a2+180);
+ a1 = NormalizeAngle(a2-data->traverse1.angle); //Establish if we are going fwds or backwards globally
+ if (a1<270 && a1>90) { //Must add 180 if the seg is reversed or inverted (but not both)
+ segs_backwards = TRUE;
+ } else {
+ segs_backwards = FALSE;
+ }
+ if ( neg ) {
+ segs_backwards = !segs_backwards; //neg implies all the segs are reversed
+ }
+ segProcData.traverse1.pos = data->traverse1.pos = p0; //actual point on curve
+ segProcData.traverse1.angle = data->traverse1.angle; //Angle of car
+ LOG( log_bezierSegments, 1, ( " BezTr1-GSA I%d P[%0.3f %0.3f] N%d SB%d\n", segInx, p0.x, p0.y, neg, segs_backwards ))
+ subSegsPtr = (trkSeg_p)segPtr->bezSegs.ptr+inx;
+ SegProc( SEGPROC_TRAVERSE1, subSegsPtr, &segProcData );
+ data->traverse1.reverse_seg = segProcData.traverse1.reverse_seg; //which way is curve (info)
+ data->traverse1.backwards = segProcData.traverse1.backwards; //Pass through Train direction
+ data->traverse1.dist = segProcData.traverse1.dist; //Get last seg partial dist
+ data->traverse1.segs_backwards = segs_backwards; //Get last
+ data->traverse1.negative = segProcData.traverse1.negative; //Is curve flipped (info)
+ data->traverse1.BezSegInx = inx; //Copy up Index
+LOG( log_bezierSegments, 1, ( " BezTr1-Exit -> A%0.3f B%d R%d N%d D%0.3f\n", a2, segProcData.traverse1.backwards, segProcData.traverse1.reverse_seg, segProcData.traverse1.negative, segProcData.traverse1.dist ))
+ break;
+
+ case SEGPROC_TRAVERSE2:
+ if (segPtr->type != SEG_BEZTRK) return; //Not SEG_BEZLIN
+LOG( log_bezierSegments, 1, ( " BezTr2-Enter D%0.3f SD%d SI%d SB%d\n", data->traverse2.dist, data->traverse2.segDir, data->traverse2.BezSegInx, data->traverse2.segs_backwards))
+ if (data->traverse2.dist <= segPtr->u.b.length) {
+
+ segProcData.traverse2.pos = data->traverse2.pos;
+ DIST_T dist = data->traverse2.dist;
+ segProcData.traverse2.dist = data->traverse2.dist;
+ segProcData.traverse2.angle = data->traverse2.angle;
+ segProcData.traverse2.segDir = data->traverse2.segDir;
+ segs_backwards = data->traverse2.segs_backwards;
+ BOOL_T backwards = data->traverse2.segDir;
+ inx = data->traverse2.BezSegInx; //Special from Traverse1
+ while (inx>=0 && inx<segPtr->bezSegs.cnt) {
+ subSegsPtr = (trkSeg_p)segPtr->bezSegs.ptr+inx;
+ SegProc(SEGPROC_TRAVERSE2, subSegsPtr, &segProcData);
+ if (segProcData.traverse2.dist<=0) { //Done
+ data->traverse2.angle = segProcData.traverse2.angle;
+ data->traverse2.dist = 0;
+ data->traverse2.pos = segProcData.traverse2.pos;
+LOG( log_bezierSegments, 1, ( " BezTr2-Exit1 -> A%0.3f P[%0.3f %0.3f] \n", data->traverse2.angle, data->traverse2.pos.x, data->traverse2.pos.y ))
+ return;
+ } else dist = segProcData.traverse2.dist;
+ p2 = segProcData.traverse2.pos;
+ a2 = segProcData.traverse2.angle;
+LOG( log_bezierSegments, 2, ( " BezTr2-Tr2 D%0.3f P[%0.3f %0.3f] A%0.3f\n", dist, p2.x, p2.y, a2 ))
+
+ segProcData.traverse1.pos = p2;
+ segProcData.traverse1.angle = a2;
+ inx = segs_backwards?inx-1:inx+1;
+ if (inx<0 || inx>=segPtr->bezSegs.cnt) break;
+ subSegsPtr = (trkSeg_p)segPtr->bezSegs.ptr+inx;
+ SegProc(SEGPROC_TRAVERSE1, subSegsPtr, &segProcData);
+ BOOL_T reverse_seg = segProcData.traverse1.reverse_seg; //For Info only
+ backwards = segProcData.traverse1.backwards;
+ dist += segProcData.traverse1.dist; //Add extra if needed - this is if we have to go from the other end of this seg
+ segProcData.traverse2.dist = dist; //distance left
+ segProcData.traverse2.segDir = backwards; //which way
+ segProcData.traverse2.pos = p2;
+ segProcData.traverse2.angle = a2;
+LOG( log_bezierSegments, 2, ( " BezTr2-Loop A%0.3f P[%0.3f %0.3f] D%0.3f SI%d B%d RS%d\n", a2, p2.x, p2.y, dist, inx, backwards, reverse_seg ))
+ }
+ data->traverse2.dist = dist;
+ } else data->traverse2.dist -= segPtr->u.b.length; //we got here because the desired point is not inside the segment
+ if (segs_backwards) {
+ data->traverse2.pos = segPtr->u.b.pos[0]; // Backwards so point 0
+ data->traverse2.angle = segPtr->u.b.angle0;
+ } else {
+ data->traverse2.pos = segPtr->u.b.pos[3]; // Forwards so point 3
+ data->traverse2.angle = segPtr->u.b.angle3;
+ }
+LOG( log_bezierSegments, 1, ( " BezTr-Exit2 --> SI%d A%0.3f P[%0.3f %0.3f] D%0.3f\n", inx, data->traverse2.angle, data->traverse2.pos.x, data->traverse2.pos.y, data->traverse2.dist))
+ break;
+
+ case SEGPROC_DRAWROADBEDSIDE:
+ //TODO - needs parallel bezier problem solved...
+ break;
+
+ case SEGPROC_DISTANCE:
+
+ dd = 100000.00; //Just find one distance
+ p0 = data->distance.pos1;
+
+ //initialize p2
+ p2 = segPtr->u.b.pos[0];
+ for(int i=0;i<segPtr->bezSegs.cnt;i++) {
+ segProcData.distance.pos1 = p0;
+ SegProc(SEGPROC_DISTANCE,&(DYNARR_N(trkSeg_t,segPtr->bezSegs,i)),&segProcData);
+ d = segProcData.distance.dd;
+ if (d<dd) {
+ dd = d;
+ p2 = segProcData.distance.pos1;
+ }
+ }
+ data->distance.dd = dd;
+ data->distance.pos1 = p2;
+ break;
+
+ case SEGPROC_FLIP:
+
+ temp0 = segPtr->u.b.pos[0];
+ temp1 = segPtr->u.b.pos[1];
+ temp2 = segPtr->u.b.pos[2];
+ temp3 = segPtr->u.b.pos[3];
+ segPtr->u.b.pos[0] = temp3;
+ segPtr->u.b.pos[1] = temp2;
+ segPtr->u.b.pos[2] = temp1;
+ segPtr->u.b.pos[3] = temp0;
+ FixUpBezierSeg(segPtr->u.b.pos,segPtr,segPtr->type == SEG_BEZTRK);
+ break;
+
+ case SEGPROC_NEWTRACK:
+ data->newTrack.trk = NewBezierTrack( segPtr->u.b.pos, (trkSeg_t *)segPtr->bezSegs.ptr, segPtr->bezSegs.cnt);
+ data->newTrack.ep[0] = 0;
+ data->newTrack.ep[1] = 1;
+ break;
+
+ case SEGPROC_LENGTH:
+ data->length.length = 0;
+ for(int i=0;i<segPtr->bezSegs.cnt;i++) {
+ SegProc(cmd,&(DYNARR_N(trkSeg_t,segPtr->bezSegs,i)),&segProcData);
+ data->length.length += segProcData.length.length;
+ }
+ break;
+
+ case SEGPROC_SPLIT:
+ //TODO Split
+ break;
+
+ case SEGPROC_GETANGLE:
+ inx = 0;
+ back = FALSE;
+ subSegsPtr = (trkSeg_p) segPtr->bezSegs.ptr;
+ coOrd pos = data->getAngle.pos;
+LOG( log_bezierSegments, 1, ( " BezGA-In P[%0.3f %0.3f] \n", pos.x, pos.y))
+ data->getAngle.angle = GetAngleSegs(segPtr->bezSegs.cnt,subSegsPtr, &pos, &inx, NULL, &back, NULL, NULL);
+ //Recurse for Bezier sub-segs (only straights and curves)
+
+ data->getAngle.negative_radius = FALSE;
+ data->getAngle.backwards = back;
+ data->getAngle.pos = pos;
+ data->getAngle.bezSegInx = inx;
+ subSegsPtr +=inx;
+ if (subSegsPtr->type == SEG_CRVTRK || subSegsPtr->type == SEG_CRVLIN ) {
+ data->getAngle.radius = fabs(subSegsPtr->u.c.radius);
+ if (subSegsPtr->u.c.radius<0 ) data->getAngle.negative_radius = TRUE;
+ data->getAngle.center = subSegsPtr->u.c.center;
+ }
+ else data->getAngle.radius = 0.0;
+LOG( log_bezierSegments, 1, ( " BezGA-Out SI%d A%0.3f P[%0.3f %0.3f] B%d\n", inx, data->getAngle.angle, pos.x, pos.y, back))
+ break;
+
+ }
+
+}
+
+
+/****************************************
+ *
+ * GRAPHICS COMMANDS
+ *
+ */
+
+
+track_p NewBezierTrack(coOrd pos[4], trkSeg_t * tempsegs, int count)
+{
+ struct extraData *xx;
+ track_p p;
+ p = NewTrack( 0, T_BEZIER, 2, sizeof *xx );
+ xx = GetTrkExtraData(p);
+ xx->bezierData.pos[0] = pos[0];
+ xx->bezierData.pos[1] = pos[1];
+ xx->bezierData.pos[2] = pos[2];
+ xx->bezierData.pos[3] = pos[3];
+ xx->bezierData.segsColor = wDrawColorBlack;
+ xx->bezierData.segsWidth = 0;
+ FixUpBezier(pos, xx, TRUE);
+LOG( log_bezier, 1, ( "NewBezierTrack( EP1 %0.3f, %0.3f, CP1 %0.3f, %0.3f, CP2 %0.3f, %0.3f, EP2 %0.3f, %0.3f ) = %d\n", pos[0].x, pos[0].y, pos[1].x, pos[1].y, pos[2].x, pos[2].y, pos[3].x, pos[3].y, GetTrkIndex(p) ) )
+ ComputeBezierBoundingBox( p, xx );
+ SetTrkEndPoint( p, 0, pos[0], xx->bezierData.a0);
+ SetTrkEndPoint( p, 1, pos[3], xx->bezierData.a1);
+ CheckTrackLength( p );
+ SetTrkBits( p, TB_HIDEDESC );
+ return p;
+}
+
+EXPORT track_p NewBezierLine( coOrd pos[4], trkSeg_t * tempsegs, int count, wDrawColor color, DIST_T width )
+{
+ struct extraData *xx;
+ track_p p;
+ p = NewTrack( 0, T_BZRLIN, 2, sizeof *xx );
+ xx = GetTrkExtraData(p);
+ xx->bezierData.pos[0] = pos[0];
+ xx->bezierData.pos[1] = pos[1];
+ xx->bezierData.pos[2] = pos[2];
+ xx->bezierData.pos[3] = pos[3];
+ xx->bezierData.segsColor = color;
+ xx->bezierData.segsWidth = width;
+ FixUpBezier(pos, xx, FALSE);
+LOG( log_bezier, 1, ( "NewBezierLine( EP1 %0.3f, %0.3f, CP1 %0.3f, %0.3f, CP2 %0.3f, %0.3f, EP2 %0.3f, %0.3f) = %d\n", pos[0].x, pos[0].y, pos[1].x, pos[1].y, pos[2].x, pos[2].y, pos[3].x, pos[3].y, GetTrkIndex(p) ) )
+ ComputeBezierBoundingBox( p, xx );
+ return p;
+}
+
+
+
+EXPORT void InitTrkBezier( void )
+{
+ T_BEZIER = InitObject( &bezierCmds );
+ T_BZRLIN = InitObject( &bezlinCmds );
+ log_bezier = LogFindIndex( "Bezier" );
+ log_traverseBezier = LogFindIndex( "traverseBezier" );
+ log_bezierSegments = LogFindIndex( "traverseBezierSegs");
+}
+
+/********************************************************************************
+ *
+ * Bezier Functions
+ *
+ ********************************************************************************/
+
+
+/**
+ * Return point on Bezier using "t" (from 0 to 1)
+ */
+extern coOrd BezierPointByParameter(coOrd p[4], double t)
+{
+
+ double a,b,c,d;
+ double mt = 1-t;
+ double mt2 = mt*mt;
+ double t2 = t*t;
+
+ a = mt2*mt;
+ b = mt2*t*3;
+ c = mt*t2*3;
+ d = t*t2;
+
+ coOrd o;
+ o.x = a*p[0].x+b*p[1].x+c*p[2].x+d*p[3].x;
+ o.y = a*p[0].y+b*p[1].y+c*p[2].y+d*p[3].y;
+
+ return o;
+
+}
+/**
+ * Find distance from point to Bezier. Return also the "t" value of that closest point.
+ */
+extern DIST_T BezierMathDistance( coOrd * pos, coOrd p[4], int segments, double * t_value)
+{
+ DIST_T dd = 10000.0;
+ double t = 0.0;
+ coOrd pt;
+ coOrd save_pt = p[0];
+ for (int i=0; i<=segments; i++) {
+ pt = BezierPointByParameter(p, (double)i/segments);
+ if (FindDistance(*pos,pt) < dd) {
+ dd = FindDistance(*pos,pt);
+ t = (double)i/segments;
+ save_pt = pt;
+ }
+ }
+ if (t_value) *t_value = t;
+ * pos = save_pt;
+ return dd;
+}
+
+extern coOrd BezierMathFindNearestPoint(coOrd *pos, coOrd p[4], int segments) {
+ double t = 0.0;
+ BezierMathDistance(pos, p, segments, &t);
+ return BezierPointByParameter(p, t);
+}
+
+void BezierSlice(coOrd input[], coOrd output[], double t) {
+ coOrd p1,p12,p2,p23,p3,p34,p4;
+ coOrd p123, p234, p1234;
+
+ p1 = input[0];
+ p2 = input[1];
+ p3 = input[2];
+ p4 = input[3];
+
+ p12.x = (p2.x-p1.x)*t+p1.x;
+ p12.y = (p2.y-p1.y)*t+p1.y;
+
+ p23.x = (p3.x-p2.x)*t+p2.x;
+ p23.y = (p3.y-p2.y)*t+p2.y;
+
+ p34.x = (p4.x-p3.x)*t+p3.x;
+ p34.y = (p4.y-p3.y)*t+p3.y;
+
+ p123.x = (p23.x-p12.x)*t+p12.x;
+ p123.y = (p23.y-p12.y)*t+p12.y;
+
+ p234.x = (p34.x-p23.x)*t+p23.x;
+ p234.y = (p34.y-p23.y)*t+p23.y;
+
+ p1234.x = (p234.x-p123.x)*t+p123.x;
+ p1234.y = (p234.y-p123.y)*t+p123.y;
+
+ output[0]= p1;
+ output[1] = p12;
+ output[2] = p123;
+ output[3] = p1234;
+
+};
+
+/**
+ * Split bezier into two parts
+ */
+extern void BezierSplit(coOrd input[], coOrd left[], coOrd right[] , double t) {
+
+ BezierSlice(input,left,t);
+
+ coOrd back[4],backright[4];
+
+ for (int i = 0;i<4;i++) {
+ back[i] = input[3-i];
+ }
+ BezierSlice(back,backright,1-t);
+ for (int i = 0;i<4;i++) {
+ right[i] = backright[3-i];
+ }
+
+}
+
+
+/**
+ * If close enough (length of control polygon exceeds chord by < error) add length of polygon.
+ * Else split and recurse
+ */
+double BezierAddLengthIfClose(coOrd start[4], double error) {
+ coOrd left[4], right[4]; /* bez poly splits */
+ double len = 0.0; /* arc length */
+ double chord; /* chord length */
+ int index; /* misc counter */
+
+ for (index = 0; index <= 2; index++)
+ len = len + FindDistance(start[index],start[index+1]); //add up control polygon
+
+ chord = FindDistance(start[0],start[3]); //find chord length
+
+ if((len-chord) > error) { // If error too large -
+ BezierSplit(start,left,right,0.5); /* split in two */
+ len = BezierAddLengthIfClose(left, error); /* recurse left side */
+ len += BezierAddLengthIfClose(right, error); /* recurse right side */
+ }
+ return len; // Add length of this curve
+
+}
+
+/**
+ * Use recursive splitting to get close approximation ot length of bezier
+ *
+ */
+extern double BezierMathLength(coOrd p[4], double error)
+{
+ if (error == 0.0) error = 0.01;
+ return BezierAddLengthIfClose(p, error); /* kick off recursion */
+
+}
+
+coOrd BezierFirstDerivative(coOrd p[4], double t)
+{
+ //checkParameterT(t);
+
+ double tSquared = t * t;
+ double s0 = -3 + 6 * t - 3 * tSquared;
+ double s1 = 3 - 12 * t + 9 * tSquared;
+ double s2 = 6 * t - 9 * tSquared;
+ double s3 = 3 * tSquared;
+ double resultX = p[0].x * s0 + p[1].x * s1 + p[2].x * s2 + p[3].x * s3;
+ double resultY = p[0].y * s0 + p[1].y * s1 + p[2].y * s2 + p[3].y * s3;
+
+ coOrd v;
+
+ v.x = resultX;
+ v.y = resultY;
+ return v;
+}
+
+/**
+ * Gets 2nd derivate wrt t of a Bezier curve at a point
+
+ */
+coOrd BezierSecondDerivative(coOrd p[4], double t)
+{
+ //checkParameterT(t);
+
+ double s0 = 6 - 6 * t;
+ double s1 = -12 + 18 * t;
+ double s2 = 6 - 18 * t;
+ double s3 = 6 * t;
+ double resultX = p[0].x * s0 + p[1].x * s1 + p[2].x * s2 + p[3].x * s3;
+ double resultY = p[0].y * s0 + p[1].y * s1 + p[2].y * s2 + p[3].y * s3;
+
+ coOrd v;
+ v.x = resultX;
+ v.y = resultY;
+ return v;
+}
+
+/**
+ * Get curvature of a Bezier at a point
+*/
+extern double BezierCurvature(coOrd p[4], double t, coOrd * center)
+{
+ //checkParameterT(t);
+
+ coOrd d1 = BezierFirstDerivative(p, t);
+ coOrd d2 = BezierSecondDerivative(p, t);
+
+ if (center) {
+ double curvnorm = (d1.x * d1.x + d1.y* d1.y)/(d1.x * d2.y - d2.x * d1.y);
+ coOrd p = BezierPointByParameter(&p, t);
+ center->x = p.x-d1.y*curvnorm;
+ center->y = p.y+d1.x*curvnorm;
+ }
+
+ double r1 = sqrt(pow(d1.x * d1.x + d1.y* d1.y, 3.0));
+ double r2 = fabs(d1.x * d2.y - d2.x * d1.y);
+ return r2 / r1;
+}
+
+/**
+ * Get Maximum Curvature
+ */
+extern double BezierMaxCurve(coOrd p[4]) {
+ double max = 0;
+ for (int t = 0;t<100;t++) {
+ double curv = BezierCurvature(p, t/100, NULL);
+ if (max<curv) max = curv;
+ }
+ return max;
+}
+
+/**
+ * Get Minimum Radius
+ */
+extern double BezierMathMinRadius(coOrd p[4]) {
+ double curv = BezierMaxCurve(p);
+ if (curv >= 1000.0 || curv <= 0.001 ) return 0.0;
+ return 1/curv;
+}
+
diff --git a/app/bin/tbezier.h b/app/bin/tbezier.h
new file mode 100644
index 0000000..1e8b915
--- /dev/null
+++ b/app/bin/tbezier.h
@@ -0,0 +1,57 @@
+/*
+ * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/tbezier.h,v 1.1 2005-12-07 15:47:36 rc-flyer Exp $
+ */
+
+/* XTrkCad - Model Railroad CAD
+ * Copyright (C) 2005 Dave Bullis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, 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 License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "common.h"
+#include "track.h"
+
+typedef struct {
+ coOrd pos[4];
+ DIST_T minCurveRadius;
+ ANGLE_T a0, a1;
+ DIST_T length;
+ dynArr_t arcSegs;
+ coOrd descriptionOff;
+ DIST_T segsWidth;
+ wDrawColor segsColor;
+ } BezierData_t;
+
+
+void BezierSplit(coOrd[4], coOrd[4], coOrd[4] , double );
+coOrd BezierPointByParameter(coOrd[4], double);
+double BezierMathLength(coOrd[4], double);
+coOrd BezierFirstDerivative(coOrd p[4], double);
+coOrd BezierSecondDerivative(coOrd p[4], double);
+double BezierCurvature(coOrd[4], double , coOrd *);
+double BezierMaxCurve(coOrd[4]);
+double BezierMathMinRadius(coOrd[4]);
+coOrd BezierMathFindNearestPoint(coOrd *, coOrd[4] , int );
+track_p NewBezierTrack(coOrd[4], trkSeg_t * , int );
+track_p NewBezierLine(coOrd[4], trkSeg_t * , int, wDrawColor, DIST_T);
+DIST_T BezierMathDistance( coOrd *, coOrd[4], int , double * );
+void FixUpBezier(coOrd[4], struct extraData*, BOOL_T);
+void FixUpBezierSeg(coOrd[4], trkSeg_p , BOOL_T);
+void FixUpBezierSegs(trkSeg_p p,int segCnt);
+BOOL_T GetBezierSegmentFromTrack(track_p, trkSeg_p);
+
+DIST_T BezierDescriptionDistance(coOrd pos,track_p trk );
+STATUS_T BezierDescriptionMove(track_p trk,wAction_t action,coOrd pos );
+
diff --git a/app/bin/tcornu.c b/app/bin/tcornu.c
new file mode 100644
index 0000000..9d9587a
--- /dev/null
+++ b/app/bin/tcornu.c
@@ -0,0 +1,1321 @@
+/** \file tcornu.c
+ *
+ * CORNU SPIRAL TRACK
+ *
+ * A Cornu is a spiral arc defined by a polynomial that has the property
+ * that curvature varies linearly with distance along the curve. It is a family
+ * of curves that include Euler spirals.
+ *
+ * In order to be useful in XtrkCAD it is defined as a set of Bezier curves each of
+ * which is defined as a set of circular arcs and lines.
+ *
+ * The derivation of the Beziers is done by the Cornu library which must be recalled
+ * whenever a change is made in the end conditions.
+ *
+ * A cornu has a minimum radius and a maximum rate of change of radius.
+ *
+ * Acknowledgment is given to Dr. Raph Levien whose seminal PhD work on Cornu curves and
+ * generous open-sourcing of his libraries both inspired and powers this function.
+ *
+ *
+ * XTrkCad - Model Railroad CAD
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, 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 License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#include "track.h"
+#include "draw.h"
+#include "cbezier.h"
+#include "tbezier.h"
+#include "tcornu.h"
+#include "ccornu.h"
+#include "ccurve.h"
+#include "cstraigh.h"
+#include "cjoin.h"
+#include "utility.h"
+#include "common.h"
+#include "i18n.h"
+#include "param.h"
+#include "math.h"
+#include "string.h"
+#include "cundo.h"
+#include "layout.h"
+#include "fileio.h"
+#include "assert.h"
+
+EXPORT TRKTYP_T T_CORNU = -1;
+
+struct extraData {
+ cornuData_t cornuData;
+ };
+
+static int log_cornu = 0;
+
+static DIST_T GetLengthCornu( track_p );
+
+/****************************************
+ *
+ * UTILITIES
+ *
+ */
+
+/*
+ * Run after any changes to the Cornu points
+ */
+void SetUpCornuParmFromTracks(track_p trk[2],cornuParm_t * cp, struct extraData* xx) {
+ if (!trk[0]) {
+ cp->center[0] = xx->cornuData.c[0];
+ cp->angle[0] = xx->cornuData.a[0];
+ cp->radius[0] = xx->cornuData.r[0];
+ }
+ if (!trk[1]) {
+ cp->center[1] = xx->cornuData.c[1];
+ cp->angle[1] = xx->cornuData.a[1];
+ cp->radius[1] = xx->cornuData.r[1];
+ }
+}
+
+EXPORT BOOL_T FixUpCornu(coOrd pos[2], track_p trk[2], EPINX_T ep[2], struct extraData* xx) {
+
+ cornuParm_t cp;
+
+ SetUpCornuParmFromTracks(trk,&cp,xx);
+
+ if (!CallCornu(pos, trk, ep, &xx->cornuData.arcSegs, &cp)) return FALSE;
+
+ xx->cornuData.r[0] = cp.radius[0];
+ if (cp.radius[0]==0) {
+ xx->cornuData.a[0] = cp.angle[0];
+ } else {
+ xx->cornuData.c[0] = cp.center[0];
+ }
+ xx->cornuData.r[1] = cp.radius[1];
+ if (cp.radius[1]==0) {
+ xx->cornuData.a[1] = cp.angle[1];
+ } else {
+ xx->cornuData.c[1] = cp.center[1];
+ }
+
+ xx->cornuData.minCurveRadius = CornuMinRadius(pos,xx->cornuData.arcSegs);
+ xx->cornuData.windingAngle = CornuTotalWindingArc(pos,xx->cornuData.arcSegs);
+ DIST_T last_c;
+ if (xx->cornuData.r[0] == 0) last_c = 0;
+ else last_c = 1/xx->cornuData.r[0];
+ xx->cornuData.maxRateofChange = CornuMaxRateofChangeofCurvature(pos,xx->cornuData.arcSegs,&last_c);
+ xx->cornuData.length = CornuLength(pos, xx->cornuData.arcSegs);
+ return TRUE;
+}
+
+EXPORT BOOL_T FixUpCornu0(coOrd pos[2],coOrd center[2],ANGLE_T angle[2],DIST_T radius[2],struct extraData* xx) {
+ DIST_T last_c;
+ if (!CallCornu0(pos, center, angle, radius,&xx->cornuData.arcSegs,FALSE)) return FALSE;
+ xx->cornuData.minCurveRadius = CornuMinRadius(pos,
+ xx->cornuData.arcSegs);
+ if (xx->cornuData.r[0] == 0) last_c = 0;
+ else last_c = 1/xx->cornuData.r[0];
+ xx->cornuData.maxRateofChange = CornuMaxRateofChangeofCurvature(pos,xx->cornuData.arcSegs,&last_c);
+ xx->cornuData.length = CornuLength(pos, xx->cornuData.arcSegs);
+ xx->cornuData.windingAngle = CornuTotalWindingArc(pos,xx->cornuData.arcSegs);
+ return TRUE;
+}
+
+EXPORT char * CreateSegPathList(track_p trk) {
+ char * cp = "\0\0";
+ if (GetTrkType(trk) != T_CORNU) return cp;
+ struct extraData *xx = GetTrkExtraData(trk);
+ if (xx->cornuData.cornuPath) free(xx->cornuData.cornuPath);
+ xx->cornuData.cornuPath = malloc(xx->cornuData.arcSegs.cnt+2);
+ int j= 0;
+ for (int i = 0;i<xx->cornuData.arcSegs.cnt;i++,j++) {
+ xx->cornuData.cornuPath[j] = i+1;
+ }
+ xx->cornuData.cornuPath[j] = cp[0];
+ xx->cornuData.cornuPath[j+1] = cp[0];
+ return xx->cornuData.cornuPath;
+}
+
+
+static void GetCornuAngles( ANGLE_T *a0, ANGLE_T *a1, track_p trk )
+{
+ assert( trk != NULL );
+
+ *a0 = NormalizeAngle( GetTrkEndAngle(trk,0) );
+ *a1 = NormalizeAngle( GetTrkEndAngle(trk,1) );
+
+ LOG( log_cornu, 4, ( "getCornuAngles: = %0.3f %0.3f\n", *a0, *a1 ) )
+}
+
+
+static void ComputeCornuBoundingBox( track_p trk, struct extraData * xx )
+{
+ coOrd orig, size;
+
+ GetSegBounds(zero,0,xx->cornuData.arcSegs.cnt,xx->cornuData.arcSegs.ptr, &orig, &size);
+
+ coOrd hi, lo;
+
+ lo.x = orig.x;
+ lo.y = orig.y;
+ hi.x = orig.x+size.x;
+ hi.y = orig.y+size.y;
+
+ SetBoundingBox( trk, hi, lo );
+}
+
+
+DIST_T CornuDescriptionDistance(
+ coOrd pos,
+ track_p trk )
+{
+ struct extraData *xx = GetTrkExtraData(trk);
+ coOrd p1;
+
+ if ( GetTrkType( trk ) != T_CORNU || ( GetTrkBits( trk ) & TB_HIDEDESC ) != 0 )
+ return 100000;
+
+ p1.x = xx->cornuData.pos[0].x + ((xx->cornuData.pos[1].x-xx->cornuData.pos[0].x)/2) + xx->cornuData.descriptionOff.x;
+ p1.y = xx->cornuData.pos[0].y + ((xx->cornuData.pos[1].y-xx->cornuData.pos[0].y)/2) + xx->cornuData.descriptionOff.y;
+
+ return FindDistance( p1, pos );
+}
+
+
+static void DrawCornuDescription(
+ track_p trk,
+ drawCmd_p d,
+ wDrawColor color )
+{
+ struct extraData *xx = GetTrkExtraData(trk);
+ wFont_p fp;
+ coOrd pos;
+
+ if (layoutLabels == 0)
+ return;
+ if ((labelEnable&LABELENABLE_TRKDESC)==0)
+ return;
+ pos.x = xx->cornuData.pos[0].x + ((xx->cornuData.pos[1].x - xx->cornuData.pos[0].x)/2);
+ pos.y = xx->cornuData.pos[0].y + ((xx->cornuData.pos[1].y - xx->cornuData.pos[0].y)/2);
+ pos.x += xx->cornuData.descriptionOff.x;
+ pos.y += xx->cornuData.descriptionOff.y;
+ fp = wStandardFont( F_TIMES, FALSE, FALSE );
+ sprintf( message, _("Cornu Curve: length=%0.3f min radius=%0.3f"),
+ xx->cornuData.length, xx->cornuData.minCurveRadius);
+ DrawBoxedString( BOX_BOX, d, pos, message, fp, (wFontSize_t)descriptionFontSize, color, 0.0 );
+}
+
+
+STATUS_T CornuDescriptionMove(
+ track_p trk,
+ wAction_t action,
+ coOrd pos )
+{
+ struct extraData *xx = GetTrkExtraData(trk);
+ static coOrd p0,p1;
+ static BOOL_T editState;
+ wDrawColor color;
+
+ if (GetTrkType(trk) != T_CORNU) return C_TERMINATE;
+
+ p0.x = xx->cornuData.pos[0].x + ((xx->cornuData.pos[1].x - xx->cornuData.pos[0].x)/2);
+ p0.y = xx->cornuData.pos[0].y + ((xx->cornuData.pos[1].y - xx->cornuData.pos[0].y)/2);
+
+ switch (action) {
+ case C_DOWN:
+ case C_MOVE:
+ case C_UP:
+ editState = TRUE;
+ p1 = pos;
+ color = GetTrkColor( trk, &mainD );
+ xx->cornuData.descriptionOff.x = pos.x - p0.x;
+ xx->cornuData.descriptionOff.y = pos.y - p0.y;
+ DrawCornuDescription( trk, &mainD, color );
+ if (action == C_UP) {
+ editState = FALSE;
+ }
+ MainRedraw();
+ MapRedraw();
+ return action==C_UP?C_TERMINATE:C_CONTINUE;
+
+ case C_REDRAW:
+ if (editState)
+ DrawLine( &mainD, p1, p0, 0, wDrawColorBlack );
+ break;
+
+ }
+ return C_CONTINUE;
+}
+
+/****************************************
+ *
+ * GENERIC FUNCTIONS
+ *
+ */
+
+static struct {
+ coOrd pos[2];
+ ANGLE_T angle[2];
+ DIST_T radius[2];
+ coOrd center[2];
+ FLOAT_T elev[2];
+ FLOAT_T length;
+ FLOAT_T grade;
+ DIST_T minRadius;
+ DIST_T maxRateOfChange;
+ ANGLE_T windingAngle;
+ unsigned int layerNumber;
+ dynArr_t segs;
+ long width;
+ wDrawColor color;
+ } cornData;
+
+typedef enum { P0, A0, R0, C0, Z0, P1, A1, R1, C1, Z1, RA, RR, WA, LN, GR, LY } cornuDesc_e;
+static descData_t cornuDesc[] = {
+/*P0*/ { DESC_POS, N_("End Pt 1: X,Y"), &cornData.pos[0] },
+/*A0*/ { DESC_ANGLE, N_("End Angle"), &cornData.angle[0] },
+/*R0*/ { DESC_DIM, N_("Radius "), &cornData.radius[0] },
+/*C0*/ { DESC_POS, N_("Center X,Y"), &cornData.center[0] },
+/*Z0*/ { DESC_DIM, N_("Z1"), &cornData.elev[0] },
+/*P1*/ { DESC_POS, N_("End Pt 2: X,Y"), &cornData.pos[1] },
+/*A1*/ { DESC_ANGLE, N_("End Angle"), &cornData.angle[1] },
+/*R1*/ { DESC_DIM, N_("Radius"), &cornData.radius[1] },
+/*C1*/ { DESC_POS, N_("Center X,Y"), &cornData.center[1] },
+/*Z1*/ { DESC_DIM, N_("Z2"), &cornData.elev[1] },
+/*RA*/ { DESC_DIM, N_("Minimum Radius"), &cornData.minRadius },
+/*RR*/ { DESC_DIM, N_("Maximum Rate Of Change Of Curvature"), &cornData.maxRateOfChange },
+/*WA*/ { DESC_ANGLE, N_("Total Winding Angle"), &cornData.windingAngle },
+/*LN*/ { DESC_DIM, N_("Length"), &cornData.length },
+/*GR*/ { DESC_FLOAT, N_("Grade"), &cornData.grade },
+/*LY*/ { DESC_LAYER, N_("Layer"), &cornData.layerNumber },
+ { DESC_NULL } };
+
+
+static void UpdateCornu( track_p trk, int inx, descData_p descUpd, BOOL_T final )
+{
+ struct extraData *xx = GetTrkExtraData(trk);
+ BOOL_T updateEndPts;
+ EPINX_T ep;
+
+ cornuParm_t cp;
+
+
+ if ( inx == -1 )
+ return;
+ updateEndPts = FALSE;
+ switch ( inx ) {
+ case P0:
+ if (GetTrkEndTrk(trk,0)) break;
+ updateEndPts = TRUE;
+ xx->cornuData.pos[0] = cornData.pos[0];
+ cornuDesc[P0].mode |= DESC_CHANGE;
+ /* no break */
+ case P1:
+ if (GetTrkEndTrk(trk,1)) break;
+ updateEndPts = TRUE;
+ xx->cornuData.pos[1]= cornData.pos[1];
+ cornuDesc[P1].mode |= DESC_CHANGE;
+ break;
+ case A0:
+ if (GetTrkEndTrk(trk,0)) break;
+ updateEndPts = TRUE;
+ xx->cornuData.a[0] = cornData.angle[0];
+ cornuDesc[A0].mode |= DESC_CHANGE;
+ break;
+ case A1:
+ if (GetTrkEndTrk(trk,1)) break;
+ updateEndPts = TRUE;
+ xx->cornuData.a[1]= cornData.angle[1];
+ cornuDesc[A1].mode |= DESC_CHANGE;
+ break;
+ case C0:
+ if (GetTrkEndTrk(trk,0)) break;
+ updateEndPts = TRUE;
+ xx->cornuData.c[0] = cornData.center[0];
+ cornuDesc[C0].mode |= DESC_CHANGE;
+ break;
+ case C1:
+ if (GetTrkEndTrk(trk,1)) break;
+ updateEndPts = TRUE;
+ xx->cornuData.c[1] = cornData.center[1];
+ cornuDesc[C1].mode |= DESC_CHANGE;
+ break;
+ case R0:
+ if (GetTrkEndTrk(trk,0)) break;
+ updateEndPts = TRUE;
+ xx->cornuData.r[0] = cornData.radius[0];
+ cornuDesc[R0].mode |= DESC_CHANGE;
+ break;
+ case R1:
+ if (GetTrkEndTrk(trk,1)) break;
+ updateEndPts = TRUE;
+ xx->cornuData.r[1]= cornData.radius[1];
+ cornuDesc[R1].mode |= DESC_CHANGE;
+ break;
+ case Z0:
+ case Z1:
+ ep = (inx==Z0?0:1);
+ UpdateTrkEndElev( trk, ep, GetTrkEndElevUnmaskedMode(trk,ep), cornData.elev[ep], NULL );
+ ComputeElev( trk, 1-ep, FALSE, &cornData.elev[1-ep], NULL );
+ if ( cornData.length > minLength )
+ cornData.grade = fabs( (cornData.elev[0]-cornData.elev[1])/cornData.length )*100.0;
+ else
+ cornData.grade = 0.0;
+ cornuDesc[GR].mode |= DESC_CHANGE;
+ cornuDesc[inx==Z0?Z1:Z0].mode |= DESC_CHANGE;
+ return;
+ case LY:
+ SetTrkLayer( trk, cornData.layerNumber);
+ break;
+ default:
+ AbortProg( "updateCornu: Bad inx %d", inx );
+ }
+ track_p tracks[2];
+ tracks[0] = GetTrkEndTrk(trk,0);
+ tracks[1] = GetTrkEndTrk(trk,1);
+
+ if (updateEndPts) {
+ if ( GetTrkEndTrk(trk,0) == NULL ) {
+ SetTrkEndPoint( trk, 0, cornData.pos[0], xx->cornuData.a[0]);
+ cornuDesc[A0].mode |= DESC_CHANGE;
+ }
+ if ( GetTrkEndTrk(trk,1) == NULL ) {
+ SetTrkEndPoint( trk, 1, cornData.pos[1], xx->cornuData.a[1]);
+ cornuDesc[A1].mode |= DESC_CHANGE;
+ }
+ }
+
+ EPINX_T new_ep[2];
+ track_p ts[2];
+ ts[0] = GetTrkEndTrk(trk,0);
+ ts[1] = GetTrkEndTrk(trk,1);
+ SetUpCornuParmFromTracks(ts,&cp,xx);
+ CallCornu(xx->cornuData.pos, tracks, new_ep, &xx->cornuData.arcSegs, &cp);
+
+
+ //FixUpCornu(xx->bezierData.pos, xx, IsTrack(trk));
+ ComputeCornuBoundingBox(trk, xx);
+ DrawNewTrack( trk );
+}
+
+
+static void DescribeCornu( track_p trk, char * str, CSIZE_T len )
+{
+ struct extraData *xx = GetTrkExtraData(trk);
+ DIST_T d;
+
+ d = xx->cornuData.length;
+ sprintf( str, _("Cornu Track(%d): Layer=%u MinRadius=%s Length=%s EP=[%0.3f,%0.3f] [%0.3f,%0.3f]"),
+ GetTrkIndex(trk),
+ GetTrkLayer(trk)+1,
+ FormatDistance(xx->cornuData.minCurveRadius),
+ FormatDistance(d),
+ PutDim(xx->cornuData.pos[0].x),PutDim(xx->cornuData.pos[0].y),
+ PutDim(xx->cornuData.pos[1].x),PutDim(xx->cornuData.pos[1].y)
+ );
+
+ cornData.length = xx->cornuData.length;
+ cornData.minRadius = xx->cornuData.minCurveRadius;
+ cornData.maxRateOfChange = xx->cornuData.maxRateofChange;
+ cornData.windingAngle = xx->cornuData.windingAngle;
+ cornData.layerNumber = GetTrkLayer(trk);
+ cornData.pos[0] = xx->cornuData.pos[0];
+ cornData.pos[1] = xx->cornuData.pos[1];
+ cornData.angle[0] = xx->cornuData.a[0];
+ cornData.angle[1] = xx->cornuData.a[1];
+ cornData.center[0] = xx->cornuData.c[0];
+ cornData.center[1] = xx->cornuData.c[1];
+ cornData.radius[0] = xx->cornuData.r[0];
+ cornData.radius[1] = xx->cornuData.r[1];
+ if (GetTrkType(trk) == T_CORNU) {
+ ComputeElev( trk, 0, FALSE, &cornData.elev[0], NULL );
+ ComputeElev( trk, 1, FALSE, &cornData.elev[1], NULL );
+
+ if ( cornData.length > minLength )
+ cornData.grade = fabs( (cornData.elev[0]-cornData.elev[1])/cornData.length )*100.0;
+ else
+ cornData.grade = 0.0;
+ }
+ BOOL_T trk0 = (GetTrkEndTrk(trk,0)!=NULL);
+ BOOL_T trk1 = (GetTrkEndTrk(trk,1)!=NULL);
+
+ cornuDesc[P0].mode = !trk0?0:DESC_RO;
+ cornuDesc[P1].mode = !trk1?0:DESC_RO;
+ cornuDesc[LN].mode = DESC_RO;
+ cornuDesc[Z0].mode = EndPtIsDefinedElev(trk,0)?0:DESC_RO;
+ cornuDesc[Z1].mode = EndPtIsDefinedElev(trk,1)?0:DESC_RO;
+
+
+ cornuDesc[A0].mode = !trk0?0:DESC_RO;
+ cornuDesc[A1].mode = !trk1?0:DESC_RO;
+ cornuDesc[C0].mode = !trk0?0:DESC_RO;
+ cornuDesc[C1].mode = !trk1?0:DESC_RO;
+ cornuDesc[R0].mode = !trk0?0:DESC_RO;
+ cornuDesc[R1].mode = !trk1?0:DESC_RO;
+ cornuDesc[GR].mode = DESC_RO;
+ cornuDesc[RA].mode = DESC_RO;
+ cornuDesc[RR].mode = DESC_RO;
+ cornuDesc[WA].mode = DESC_RO;
+ cornuDesc[LY].mode = DESC_NOREDRAW;
+
+ DoDescribe( _("Cornu Track"), trk, cornuDesc, UpdateCornu );
+
+
+}
+
+
+static DIST_T DistanceCornu( track_p t, coOrd * p )
+{
+ struct extraData *xx = GetTrkExtraData(t);
+ //return BezierMathDistance(p,xx->bezierData.pos,100, &s);
+
+ DIST_T d = 100000.0;
+ coOrd p2 = xx->cornuData.pos[0]; //Set initial point
+ segProcData_t segProcData;
+ for (int i = 0;i<xx->cornuData.arcSegs.cnt;i++) {
+ trkSeg_t seg = DYNARR_N(trkSeg_t,xx->cornuData.arcSegs,i);
+ if (seg.type == SEG_FILCRCL) continue;
+ segProcData.distance.pos1 = * p;
+ SegProc(SEGPROC_DISTANCE,&seg,&segProcData);
+ if (segProcData.distance.dd<d) {
+ d = segProcData.distance.dd;
+ p2 = segProcData.distance.pos1;
+ }
+ }
+ //d = BezierDistance( p, xx->bezierData.pos[0], xx->bezierData.pos[1], xx->bezierData.pos[2], xx->bezierData.pos[1], 100, NULL );
+ * p = p2;
+ return d;
+}
+
+static void DrawCornu( track_p t, drawCmd_p d, wDrawColor color )
+{
+ struct extraData *xx = GetTrkExtraData(t);
+ long widthOptions = DTS_LEFT|DTS_RIGHT;
+
+ if (GetTrkWidth(t) == 2)
+ widthOptions |= DTS_THICK2;
+ if (GetTrkWidth(t) == 3)
+ widthOptions |= DTS_THICK3;
+
+
+ if ( ((d->funcs->options&wDrawOptTemp)==0) &&
+ (labelWhen == 2 || (labelWhen == 1 && (d->options&DC_PRINT))) &&
+ labelScale >= d->scale &&
+ ( GetTrkBits( t ) & TB_HIDEDESC ) == 0 ) {
+ DrawCornuDescription( t, d, color );
+ }
+ DIST_T scale2rail = (d->options&DC_PRINT)?(twoRailScale*2+1):twoRailScale;
+ if ( tieDrawMode!=TIEDRAWMODE_NONE &&
+ d!=&mapD &&
+ (d->options&DC_TIES)!=0 &&
+ d->scale<scale2rail/2 )
+ DrawSegsO(d,t,zero,0.0,xx->cornuData.arcSegs.ptr,xx->cornuData.arcSegs.cnt, GetTrkGauge(t), color, widthOptions|DTS_TIES);
+ DrawSegsO(d,t,zero,0.0,xx->cornuData.arcSegs.ptr,xx->cornuData.arcSegs.cnt, GetTrkGauge(t), color, widthOptions);
+ if ( (d->funcs->options & wDrawOptTemp) == 0 &&
+ (d->options&DC_QUICK) == 0 ) {
+ DrawEndPt( d, t, 0, color );
+ DrawEndPt( d, t, 1, color );
+ }
+}
+
+void FreeSubSegs(trkSeg_t* s) {
+ if (s->type == SEG_BEZTRK || s->type == SEG_BEZLIN) {
+ if (s->bezSegs.ptr) {
+ MyFree(s->bezSegs.ptr);
+ }
+ s->bezSegs.max = 0;
+ s->bezSegs.cnt = 0;
+ s->bezSegs.ptr = NULL;
+ }
+}
+
+static void DeleteCornu( track_p t )
+{
+ struct extraData *xx = GetTrkExtraData(t);
+
+ for (int i=0;i<xx->cornuData.arcSegs.cnt;i++) {
+ trkSeg_t s = DYNARR_N(trkSeg_t,xx->cornuData.arcSegs,i);
+ FreeSubSegs(&s);
+ }
+ if (xx->cornuData.arcSegs.ptr)
+ MyFree(xx->cornuData.arcSegs.ptr);
+ xx->cornuData.arcSegs.max = 0;
+ xx->cornuData.arcSegs.cnt = 0;
+ xx->cornuData.arcSegs.ptr = NULL;
+}
+
+static BOOL_T WriteCornu( track_p t, FILE * f )
+{
+ struct extraData *xx = GetTrkExtraData(t);
+ long options;
+ BOOL_T rc = TRUE;
+ BOOL_T track =(GetTrkType(t)==T_CORNU);
+ options = GetTrkWidth(t) & 0x0F;
+ if ( ( GetTrkBits(t) & TB_HIDEDESC ) == 0 ) options |= 0x80;
+ rc &= fprintf(f, "%s %d %d %ld 0 0 %s %d %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f \n",
+ "CORNU",GetTrkIndex(t), GetTrkLayer(t), (long)options,
+ GetTrkScaleName(t), GetTrkVisible(t),
+ xx->cornuData.pos[0].x, xx->cornuData.pos[0].y,
+ xx->cornuData.a[0],
+ xx->cornuData.r[0],
+ xx->cornuData.c[0].x,xx->cornuData.c[0].y,
+ xx->cornuData.pos[1].x, xx->cornuData.pos[1].y,
+ xx->cornuData.a[1],
+ xx->cornuData.r[1],
+ xx->cornuData.c[1].x,xx->cornuData.c[1].y )>0;
+ if (track) {
+ rc &= WriteEndPt( f, t, 0 );
+ rc &= WriteEndPt( f, t, 1 );
+ }
+ rc &= WriteSegs( f, xx->cornuData.arcSegs.cnt, xx->cornuData.arcSegs.ptr );
+ //rc &= fprintf(f, "\tEND\n" )>0;
+ return rc;
+}
+
+static void ReadCornu( char * line )
+{
+ struct extraData *xx;
+ track_p t;
+ wIndex_t index;
+ BOOL_T visible;
+ DIST_T r0,r1;
+ ANGLE_T a0,a1;
+ coOrd p0, p1, c0, c1;
+ char scale[10];
+ wIndex_t layer;
+ long options;
+ char * cp = NULL;
+
+ if (!GetArgs( line+6, "dLl00sdpffppffp",
+ &index, &layer, &options, scale, &visible, &p0, &a0, &r0, &c0, &p1, &a1, &r1, &c1 ) ) {
+ return;
+ }
+ t = NewTrack( index, T_CORNU, 0, sizeof *xx );
+
+ xx = GetTrkExtraData(t);
+ SetTrkVisible(t, visible);
+ SetTrkScale(t, LookupScale(scale));
+ SetTrkLayer(t, layer );
+ SetTrkWidth(t, (int)(options&0x0F));
+ if ( ( options & 0x80 ) == 0 ) SetTrkBits(t,TB_HIDEDESC);
+ xx->cornuData.pos[0] = p0;
+ xx->cornuData.pos[1] = p1;
+ xx->cornuData.a[0] = a0;
+ xx->cornuData.r[0] = r0;
+ xx->cornuData.a[1] = a1;
+ xx->cornuData.c[0] = c0;
+ xx->cornuData.c[1] = c1;
+ xx->cornuData.r[1] = r1;
+ xx->cornuData.descriptionOff.x = xx->cornuData.descriptionOff.y = 0.0;
+ ReadSegs();
+ FixUpCornu0(xx->cornuData.pos,xx->cornuData.c,xx->cornuData.a, xx->cornuData.r, xx);
+ ComputeCornuBoundingBox(t,xx);
+ SetEndPts(t,2);
+}
+
+static void MoveCornu( track_p trk, coOrd orig )
+{
+ struct extraData *xx = GetTrkExtraData(trk);
+ UndoModify(trk);
+ for (int i=0;i<2;i++) {
+ xx->cornuData.pos[i].x += orig.x;
+ xx->cornuData.pos[i].y += orig.y;
+ xx->cornuData.c[i].x += orig.x;
+ xx->cornuData.c[i].y += orig.y;
+ }
+ RebuildCornu(trk);
+}
+
+static void RotateCornu( track_p trk, coOrd orig, ANGLE_T angle )
+{
+ struct extraData *xx = GetTrkExtraData(trk);
+ UndoModify(trk);
+ for (int i=0;i<2;i++) {
+ Rotate( &xx->cornuData.pos[i], orig, angle );
+ Rotate( &xx->cornuData.c[i], orig, angle);
+ xx->cornuData.a[i] = NormalizeAngle(xx->cornuData.a[i]+angle);
+ }
+ RebuildCornu(trk);
+}
+
+static void RescaleCornu( track_p trk, FLOAT_T ratio )
+{
+ struct extraData *xx = GetTrkExtraData(trk);
+ UndoModify(trk);
+ for (int i=0;i<2;i++) {
+ xx->cornuData.pos[i].x *= ratio;
+ xx->cornuData.pos[i].y *= ratio;
+ }
+ RebuildCornu(trk);
+
+}
+
+EXPORT BOOL_T SetCornuEndPt(track_p trk, EPINX_T inx, coOrd pos, coOrd center, ANGLE_T angle, DIST_T radius) {
+ struct extraData *xx = GetTrkExtraData(trk);
+
+ xx->cornuData.pos[inx] = pos;
+ xx->cornuData.c[inx] = center;
+ xx->cornuData.a[inx] = angle;
+ xx->cornuData.r[inx] = radius;
+ if (!RebuildCornu(trk)) return FALSE;
+ SetTrkEndPoint( trk, inx, xx->cornuData.pos[inx], xx->cornuData.a[inx]);
+ return TRUE;
+}
+
+
+/**
+ * Split the Track at approximately the point pos.
+ */
+static BOOL_T SplitCornu( track_p trk, coOrd pos, EPINX_T ep, track_p *leftover, EPINX_T * ep0, EPINX_T * ep1 )
+{
+ struct extraData *xx = GetTrkExtraData(trk);
+ track_p trk1;
+ DIST_T radius = 0.0;
+ coOrd center;
+ int inx;
+ BOOL_T track;
+ track = IsTrack(trk);
+
+ cornuParm_t new;
+
+ double dd = DistanceCornu(trk, &pos);
+ if (dd>minLength) return FALSE;
+ BOOL_T back, neg;
+
+ ANGLE_T angle = GetAngleSegs(xx->cornuData.arcSegs.cnt,(trkSeg_t *)(xx->cornuData.arcSegs.ptr),&pos,&inx,NULL,&back,NULL,&neg);
+
+ trkSeg_p segPtr = &DYNARR_N(trkSeg_t, xx->cornuData.arcSegs, inx);
+
+ GetAngleSegs(segPtr->bezSegs.cnt,(trkSeg_t *)(segPtr->bezSegs.ptr),&pos,&inx,NULL,&back,NULL,&neg);
+ segPtr = &DYNARR_N(trkSeg_t, segPtr->bezSegs, inx);
+
+ if (segPtr->type == SEG_STRTRK) {
+ radius = 0.0;
+ center = zero;
+ } else if (segPtr->type == SEG_CRVTRK) {
+ center = segPtr->u.c.center;
+ radius = fabs(segPtr->u.c.radius);
+ }
+ if (ep) {
+ new.pos[0] = pos;
+ new.pos[1] = xx->cornuData.pos[1];
+ new.angle[0] = NormalizeAngle(angle+(neg==back?180:0));
+ new.angle[1] = xx->cornuData.a[1];
+ new.center[0] = center;
+ new.center[1] = xx->cornuData.c[1];
+ new.radius[0] = radius;
+ new.radius[1] = xx->cornuData.r[1];
+ } else {
+ new.pos[1] = pos;
+ new.pos[0] = xx->cornuData.pos[0];
+ new.angle[1] = NormalizeAngle(angle+(neg==back?0:180));
+ new.angle[0] = xx->cornuData.a[0];
+ new.center[1] = center;
+ new.center[0] = xx->cornuData.c[0];
+ new.radius[1] = radius;
+ new.radius[0] = xx->cornuData.r[0];
+ }
+
+ trk1 = NewCornuTrack(new.pos,new.center,new.angle,new.radius,NULL,0);
+ if (trk1==NULL) {
+ wBeep();
+ InfoMessage(_("Cornu Create Failed for p1[%0.3f,%0.3f] p2[%0.3f,%0.3f], c1[%0.3f,%0.3f] c2[%0.3f,%0.3f], a1=%0.3f a2=%0.3f, r1=%s r2=%s"),
+ new.pos[0].x,new.pos[0].y,
+ new.pos[1].x,new.pos[1].y,
+ new.center[0].x,new.center[0].y,
+ new.center[1].x,new.center[1].y,
+ new.angle[0],new.angle[1],
+ FormatDistance(new.radius[0]),FormatDistance(new.radius[1]));
+ UndoEnd();
+ return FALSE;
+ }
+
+ UndoModify(trk);
+ xx->cornuData.pos[ep] = pos;
+ xx->cornuData.a[ep] = NormalizeAngle(new.angle[1-ep]+180);
+ xx->cornuData.r[ep] = new.radius[1-ep];
+ xx->cornuData.c[ep] = new.center[1-ep];
+
+ RebuildCornu(trk);
+
+ SetTrkEndPoint(trk, ep, xx->cornuData.pos[ep], xx->cornuData.a[ep]);
+
+ *leftover = trk1;
+ *ep0 = 1-ep;
+ *ep1 = ep;
+
+ return TRUE;
+}
+
+BOOL_T MoveCornuEndPt ( track_p *trk, EPINX_T *ep, coOrd pos, DIST_T d0 ) {
+ track_p trk2;
+ if (SplitTrack(*trk,pos,*ep,&trk2,TRUE)) {
+ struct extraData *xx = GetTrkExtraData(*trk);
+ if (trk2) DeleteTrack(trk2,TRUE);
+ SetTrkEndPoint( *trk, *ep, *ep?xx->cornuData.pos[1]:xx->cornuData.pos[0], *ep?xx->cornuData.a[1]:xx->cornuData.a[0] );
+ return TRUE;
+ }
+ return FALSE;
+}
+static int log_traverseCornu = 0;
+/*
+ * TraverseCornu is used to position a train/car.
+ * We find a new position and angle given a current pos, angle and a distance to travel.
+ *
+ * The output can be TRUE -> we have moved the point to a new point or to the start/end of the next track
+ * FALSE -> we have not found that point because pos was not on/near the track
+ *
+ * If true we supply the remaining distance to go (always positive).
+ * We detect the movement direction by comparing the current angle to the angle of the track at the point.
+ *
+ * The entire Cornu may be reversed or forwards depending on the way it was drawn.
+ *
+ * Each Bezier segment within that Cornu structure therefore may be processed forwards or in reverse.
+ * So for each segment we call traverse1 to get the direction and extra distance to go to get to the current point
+ * and then use that for traverse2 to actually move to the new point
+ *
+ * If we exceed the current point's segment we move on to the next until the end of this track or we have found the spot.
+ *
+ */
+static BOOL_T TraverseCornu( traverseTrack_p trvTrk, DIST_T * distR )
+{
+ track_p trk = trvTrk->trk;
+ struct extraData *xx = GetTrkExtraData(trk);
+ DIST_T dist = *distR;
+ segProcData_t segProcData;
+ BOOL_T cornu_backwards= FALSE;
+ BOOL_T neg = FALSE;
+ DIST_T d = 10000;
+ coOrd pos1 = trvTrk->pos, pos2 = trvTrk->pos;
+ ANGLE_T a1,a2;
+ int inx, segInx = 0;
+ EPINX_T ep;
+ BOOL_T back;
+LOG( log_traverseCornu, 1, ( "TravCornu-In [%0.3f %0.3f] A%0.3f D%0.3f \n", trvTrk->pos.x, trvTrk->pos.y, trvTrk->angle, *distR ))
+ trkSeg_p segPtr = (trkSeg_p)xx->cornuData.arcSegs.ptr;
+
+ a2 = GetAngleSegs( //Find correct Segment and nearest point in it
+ xx->cornuData.arcSegs.cnt,segPtr,
+ &pos2, &segInx, &d , &back , NULL, &neg); //d = how far pos2 from old pos2 = trvTrk->pos
+
+ if ( d > 10 ) {
+ ErrorMessage( "traverseCornu: Position is not near track: %0.3f", d );
+ return FALSE; //This means the input pos is not on or close to the track.
+ }
+ if (back) a2 = NormalizeAngle(a2+180); //If reverse segs - reverse angle
+ a1 = NormalizeAngle(a2-trvTrk->angle); //Establish if we are going fwds or backwards globally
+ if (a1<270 && a1>90) { //Must add 180 if the seg is reversed or inverted (but not both)
+ cornu_backwards = TRUE;
+ ep = 0;
+ } else {
+ cornu_backwards = FALSE;
+ ep = 1;
+ }
+ if (neg) {
+ cornu_backwards = !cornu_backwards; //Reversed direction
+ ep = 1-ep;
+ }
+ segProcData.traverse1.pos = pos2; //actual point on curve
+ segProcData.traverse1.angle = trvTrk->angle; //direction car is going for Traverse 1
+LOG( log_traverseCornu, 1, ( " TravCornu-GetSubA A%0.3f I%d N%d B%d CB%d\n", a2, segInx, neg, back, cornu_backwards ))
+ inx = segInx;
+ while (inx >=0 && inx<xx->cornuData.arcSegs.cnt) {
+ segPtr = (trkSeg_p)xx->cornuData.arcSegs.ptr+inx; //move in to the identified Bezier segment
+ SegProc( SEGPROC_TRAVERSE1, segPtr, &segProcData );
+ BOOL_T backwards = segProcData.traverse1.backwards; //do we process this segment backwards?
+ BOOL_T reverse_seg = segProcData.traverse1.reverse_seg; //Info only
+ int BezSegInx = segProcData.traverse1.BezSegInx; //Which subSeg was it?
+ BOOL_T segs_backwards = segProcData.traverse1.segs_backwards;
+
+ dist += segProcData.traverse1.dist; //Add in the part of the Bezier to get to pos
+
+ segProcData.traverse2.dist = dist; //Set up Traverse2
+ segProcData.traverse2.segDir = backwards;
+ segProcData.traverse2.BezSegInx = BezSegInx;
+ segProcData.traverse2.segs_backwards = segs_backwards;
+LOG( log_traverseCornu, 2, ( " TravCornu-Tr1 SI%d D%0.3f B%d RS%d \n", BezSegInx, dist, backwards, reverse_seg ) )
+ SegProc( SEGPROC_TRAVERSE2, segPtr, &segProcData ); //Angle at pos2
+ if ( segProcData.traverse2.dist <= 0 ) { //-ve or zero distance left over so stop there
+ *distR = 0;
+ trvTrk->pos = segProcData.traverse2.pos; //Use finishing pos
+ trvTrk->angle = segProcData.traverse2.angle; //Use finishing angle
+LOG( log_traverseCornu, 1, ( "TravCornu-Ex1 -> [%0.3f %0.3f] A%0.3f D%0.3f\n", trvTrk->pos.x, trvTrk->pos.y, trvTrk->angle, *distR ) )
+ return TRUE;
+ }
+ dist = segProcData.traverse2.dist; //How far left?
+ coOrd pos = segProcData.traverse2.pos; //Will always be at a Bezseg end
+ ANGLE_T angle = segProcData.traverse2.angle; //Angle of end therefore
+
+ segProcData.traverse1.angle = angle; //Set up Traverse1
+ segProcData.traverse1.pos = pos;
+ inx = cornu_backwards?inx-1:inx+1; //Here's where the global segment direction comes in
+LOG( log_traverseCornu, 2, ( " TravCornu-Loop D%0.3f A%0.3f I%d \n", dist, angle, inx ) )
+ }
+ //Ran out of Bez-Segs so punt to next Track
+ *distR = dist; //Tell caller what dist is left
+
+ trvTrk->pos = GetTrkEndPos(trk,ep); //Which end were we heading for?
+ trvTrk->angle = NormalizeAngle(GetTrkEndAngle(trk, ep)+(cornu_backwards?180:0));
+ trvTrk->trk = GetTrkEndTrk(trk,ep); //go onto next track (or NULL)
+
+ if (trvTrk->trk==NULL) {
+ trvTrk->pos = pos1;
+ return TRUE;
+ }
+LOG( log_traverseCornu, 1, ( "TravCornu-Ex2 --> [%0.3f %0.3f] A%0.3f D%0.3f\n", trvTrk->pos.x, trvTrk->pos.y, trvTrk->angle, *distR ) )
+ return TRUE;
+
+}
+
+
+static BOOL_T EnumerateCornu( track_p trk )
+{
+
+ if (trk != NULL) {
+ struct extraData *xx = GetTrkExtraData(trk);
+ DIST_T d;
+ d = xx->cornuData.length;
+ ScaleLengthIncrement( GetTrkScale(trk), d );
+ }
+ return TRUE;
+}
+
+static BOOL_T MergeCornu(
+ track_p trk0,
+ EPINX_T ep0,
+ track_p trk1,
+ EPINX_T ep1 )
+{
+ struct extraData *xx0 = GetTrkExtraData(trk0);
+ struct extraData *xx1 = GetTrkExtraData(trk1);
+ track_p trk_after,trk_before;
+ EPINX_T ep_before,ep_after=-1;
+ coOrd p[2];
+ coOrd c[2];
+ ANGLE_T a[2];
+ DIST_T r[2];
+
+
+ if (!IsTrack(trk0) || !IsTrack(trk1) ) return FALSE;
+ if (GetTrkType(trk0) != GetTrkType(trk1)) return FALSE;
+ if (GetEndPtConnectedToMe(trk0,trk1) != ep0) return FALSE;
+ if (GetEndPtConnectedToMe(trk1,trk0) != ep1) return FALSE;
+
+ if (ep0 == ep1)
+ return FALSE;
+
+ UndoStart( _("Merge Cornu"), "MergeCornu( T%d[%d] T%d[%d] )", GetTrkIndex(trk0), ep0, GetTrkIndex(trk1), ep1 );
+ p[0] = xx0->cornuData.pos[0];
+ p[1] = xx1->cornuData.pos[1];
+ a[0] = xx0->cornuData.a[0];
+ a[1] = xx1->cornuData.a[1];
+ c[0] = xx0->cornuData.c[0];
+ c[1] = xx1->cornuData.c[1];
+ r[0] = xx0->cornuData.r[0];
+ r[1] = xx1->cornuData.r[1];
+ track_p trk3 = NewCornuTrack(p,c,a,r,NULL,0);
+ if (trk3==NULL) {
+ wBeep();
+ InfoMessage(_("Cornu Create Failed for p1[%0.3f,%0.3f] p2[%0.3f,%0.3f], c1[%0.3f,%0.3f] c2[%0.3f,%0.3f], a1=%0.3f a2=%0.3f, r1=%s r2=%s"),
+ p[0].x,p[0].y,
+ p[1].x,p[1].y,
+ c[0].x,c[0].y,
+ c[1].x,c[1].y,
+ a[0],a[1],
+ FormatDistance(r[0]),FormatDistance(r[1]));
+ UndoEnd();
+ return FALSE;
+ }
+
+ UndoModify( trk0 );
+ UndoModify( trk1 );
+ UndrawNewTrack( trk0 );
+ UndrawNewTrack( trk1 );
+ trk_after = GetTrkEndTrk( trk1, 1-ep1 );
+ if (trk_after) {
+ ep_after = GetEndPtConnectedToMe( trk_after, trk1 );
+ DisconnectTracks( trk1, 1-ep1, trk_after, ep_after );
+ }
+ trk_before = GetTrkEndTrk( trk0, 1-ep0 );
+ if (trk_before) {
+ ep_before = GetEndPtConnectedToMe( trk_before, trk0 );
+ DisconnectTracks( trk0, 1-ep1, trk_before, ep_before );
+ }
+
+ DeleteTrack( trk1, TRUE );
+ DeleteTrack( trk0, TRUE );
+ if (trk_after) {
+ SetTrkEndPoint( trk_after, ep_after, xx0->cornuData.pos[1], NormalizeAngle(xx0->cornuData.a[1]+180));
+ ConnectTracks( trk3, 1, trk_after, ep_after);
+ }
+ if (trk_before) {
+ SetTrkEndPoint( trk_before, ep_before, xx0->cornuData.pos[0], NormalizeAngle(xx0->cornuData.a[0]+180));
+ ConnectTracks( trk3, 0, trk_before, ep_before);
+ }
+ DrawNewTrack( trk3 );
+ UndoEnd();
+
+
+ return TRUE;
+}
+
+BOOL_T GetBezierSegmentsFromCornu(track_p trk, dynArr_t * segs) {
+ struct extraData * xx = GetTrkExtraData(trk);
+ for (int i=0;i<xx->cornuData.arcSegs.cnt;i++) {
+ DYNARR_APPEND(trkSeg_t, * segs, 10);
+ trkSeg_p segPtr = &DYNARR_N(trkSeg_t,* segs,segs->cnt-1);
+ segPtr->type = SEG_BEZTRK;
+ segPtr->color = wDrawColorBlack;
+ segPtr->width = 0;
+ if (segPtr->bezSegs.ptr) MyFree(segPtr->bezSegs.ptr);
+ segPtr->bezSegs.cnt = 0;
+ segPtr->bezSegs.max = 0;
+ segPtr->bezSegs.ptr = NULL;
+ trkSeg_p p = (trkSeg_t *) xx->cornuData.arcSegs.ptr+i;
+ for (int j=0;j<4;j++) segPtr->u.b.pos[j] = p->u.b.pos[j];
+ FixUpBezierSeg(segPtr->u.b.pos,segPtr,TRUE);
+ }
+ return TRUE;
+}
+
+static DIST_T GetLengthCornu( track_p trk )
+{
+ struct extraData *xx = GetTrkExtraData(trk);
+ DIST_T length = 0.0;
+ segProcData_t segProcData;
+ for(int i=0;i<xx->cornuData.arcSegs.cnt;i++) {
+ trkSeg_t seg = DYNARR_N(trkSeg_t,xx->cornuData.arcSegs,i);
+ if (seg.type == SEG_FILCRCL) continue;
+ SegProc(SEGPROC_LENGTH, &seg, &segProcData);
+ length += segProcData.length.length;
+ }
+ return length;
+}
+
+
+static BOOL_T GetParamsCornu( int inx, track_p trk, coOrd pos, trackParams_t * params )
+{
+ int segInx, segInx2;
+ BOOL_T back, negative;
+ DIST_T d;
+ struct extraData *xx = GetTrkExtraData(trk);
+ params->type = curveTypeCornu;
+ params->track_angle = GetAngleSegs( //Find correct Segment and nearest point in it
+ xx->cornuData.arcSegs.cnt,xx->cornuData.arcSegs.ptr,
+ &pos, &segInx, &d , &back, &segInx2, &negative );
+ trkSeg_p segPtr = &DYNARR_N(trkSeg_t,xx->cornuData.arcSegs,segInx);
+ if (negative != back) params->track_angle = NormalizeAngle(params->track_angle+180); //Cornu is in reverse
+ if (segPtr->type == SEG_STRTRK) {
+ params->arcR = 0.0;
+ } else if (segPtr->type == SEG_CRVTRK) {
+ params->arcR = fabs(segPtr->u.c.radius);
+ params->arcP = segPtr->u.c.center;
+ params->arcA0 = segPtr->u.c.a0;
+ params->arcA1 = segPtr->u.c.a1;
+ } else if (segPtr->type == SEG_BEZTRK) {
+ trkSeg_p segPtr2 = &DYNARR_N(trkSeg_t,segPtr->bezSegs,segInx2);
+ if (segPtr2->type == SEG_STRTRK) {
+ params->arcR = 0.0;
+ } else if (segPtr2->type == SEG_CRVTRK) {
+ params->arcR = fabs(segPtr2->u.c.radius);
+ params->arcP = segPtr2->u.c.center;
+ params->arcA0 = segPtr2->u.c.a0;
+ params->arcA1 = segPtr2->u.c.a1;
+ }
+ }
+ for (int i=0;i<2;i++) {
+ params->cornuEnd[i] = xx->cornuData.pos[i];
+ params->cornuAngle[i] = xx->cornuData.a[i];
+ params->cornuRadius[i] = xx->cornuData.r[i];
+ params->cornuCenter[i] = xx->cornuData.c[i];
+ }
+ params->len = xx->cornuData.length;
+ if ( inx == PARAMS_PARALLEL ) {
+ params->ep = 0;
+ } else if (inx == PARAMS_CORNU) {
+ params->ep = PickEndPoint( pos, trk);
+ } else {
+ params->ep = PickUnconnectedEndPointSilent( pos, trk );
+ }
+ if (params->ep>=0) {
+ params->angle = GetTrkEndAngle(trk,params->ep);
+ }
+
+ return TRUE;
+
+}
+
+
+
+static BOOL_T QueryCornu( track_p trk, int query )
+{
+ struct extraData * xx = GetTrkExtraData(trk);
+ switch ( query ) {
+ case Q_CAN_GROUP:
+ return FALSE;
+ break;
+ case Q_FLIP_ENDPTS:
+ case Q_HAS_DESC:
+ return TRUE;
+ break;
+ case Q_EXCEPTION:
+ return xx->cornuData.minCurveRadius < (GetLayoutMinTrackRadius()-EPSILON);
+ break;
+ case Q_IS_CORNU:
+ return TRUE;
+ break;
+ case Q_ISTRACK:
+ return TRUE;
+ break;
+ case Q_CAN_PARALLEL:
+ return TRUE;
+ break;
+ // case Q_MODIFY_CANT_SPLIT: Remove Split Restriction
+ // case Q_CANNOT_BE_ON_END: Remove Restriction - Can have Cornu with no ends
+ case Q_CANNOT_PLACE_TURNOUT:
+ return TRUE;
+ break;
+ case Q_IGNORE_EASEMENT_ON_EXTEND:
+ return TRUE;
+ break;
+ case Q_MODIFY_CAN_SPLIT:
+ case Q_CAN_EXTEND:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+
+static void FlipCornu(
+ track_p trk,
+ coOrd orig,
+ ANGLE_T angle )
+{
+ struct extraData * xx = GetTrkExtraData(trk);
+ FlipPoint( &xx->cornuData.pos[0], orig, angle );
+ FlipPoint( &xx->cornuData.pos[1], orig, angle );
+ FlipPoint( &xx->cornuData.c[0], orig, angle);
+ FlipPoint( &xx->cornuData.c[1], orig, angle);
+ xx->cornuData.a[0] = NormalizeAngle( 2*angle - xx->cornuData.a[0] );
+ xx->cornuData.a[1] = NormalizeAngle( 2*angle - xx->cornuData.a[1] );
+
+ RebuildCornu(trk);
+
+}
+
+static ANGLE_T GetAngleCornu(
+ track_p trk,
+ coOrd pos,
+ EPINX_T * ep0,
+ EPINX_T * ep1 )
+{
+ struct extraData * xx = GetTrkExtraData(trk);
+ ANGLE_T angle;
+ BOOL_T back, neg;
+ int indx;
+ angle = GetAngleSegs( xx->cornuData.arcSegs.cnt, (trkSeg_p)xx->cornuData.arcSegs.ptr, &pos, &indx, NULL, &back, NULL, &neg );
+ if ( ep0 ) *ep0 = -1;
+ if ( ep1 ) *ep1 = -1;
+ return angle;
+}
+
+BOOL_T GetCornuSegmentFromTrack(track_p trk, trkSeg_p seg_p) {
+ //TODO If we support Group
+ return TRUE;
+}
+
+
+static BOOL_T MakeParallelCornu(
+ track_p trk,
+ coOrd pos,
+ DIST_T sep,
+ track_p * newTrkR,
+ coOrd * p0R,
+ coOrd * p1R )
+{
+ struct extraData * xx = GetTrkExtraData(trk);
+ coOrd np[4], p, nc[2];
+ ANGLE_T atrk, diff_a, na[2];
+ DIST_T nr[2];
+
+
+ //Produce cornu that is translated parallel to the existing Cornu
+ // - not a precise result if the cornu end angles are not in the same general direction.
+ // The expectation is that the user will have to adjust it - unless and until we produce
+ // a new algo to adjust the control points to be parallel to the endpoints.
+
+ p = pos;
+ DistanceCornu(trk, &p); //Find nearest point on curve
+ atrk = GetAngleSegs(xx->cornuData.arcSegs.cnt,(trkSeg_t *)(xx->cornuData.arcSegs.ptr),&p,NULL,NULL,NULL,NULL, NULL);
+ diff_a = NormalizeAngle(FindAngle(pos,p)-atrk); //we know it will be +/-90...
+ //find parallel move x and y for points
+ BOOL_T above = FALSE;
+ if ( diff_a < 180 ) above = TRUE; //Above track
+ if (xx->cornuData.a[0] <180) above = !above;
+ Translate(&np[0],xx->cornuData.pos[0],xx->cornuData.a[0]+(above?90:-90),sep);
+ Translate(&np[1],xx->cornuData.pos[1],xx->cornuData.a[1]+(above?-90:90),sep);
+ na[0]=xx->cornuData.a[0];
+ na[1]=xx->cornuData.a[1];
+ if (xx->cornuData.r[0]) {
+ //Find angle between center and end angle of track
+ ANGLE_T ea0 =
+ NormalizeAngle(FindAngle(xx->cornuData.c[0],xx->cornuData.pos[0])-xx->cornuData.a[0]);
+ DIST_T sep0 = sep;
+ if (ea0>180) sep0 = -sep;
+ nr[0]=xx->cornuData.r[0]+(above?sep0:-sep0); //Needs adjustment
+ nc[0]=xx->cornuData.c[0];
+ } else {
+ nr[0] = 0;
+ nc[0] = zero;
+ }
+
+ if (xx->cornuData.r[1]) {
+ ANGLE_T ea1 =
+ NormalizeAngle(FindAngle(xx->cornuData.c[1],xx->cornuData.pos[1])-xx->cornuData.a[1]);
+ DIST_T sep1 = sep;
+ if (ea1<180) sep1 = -sep;
+ nr[1]=xx->cornuData.r[1]+(above?sep1:-sep1); //Needs adjustment
+ nc[1]=xx->cornuData.c[1];
+ } else {
+ nr[1] = 0;
+ nc[1] = zero;
+ }
+
+ if ( newTrkR ) {
+ *newTrkR = NewCornuTrack( np, nc, na, nr, NULL, 0);
+ if (*newTrkR==NULL) {
+ wBeep();
+ InfoMessage(_("Cornu Create Failed for p1[%0.3f,%0.3f] p2[%0.3f,%0.3f], c1[%0.3f,%0.3f] c2[%0.3f,%0.3f], a1=%0.3f a2=%0.3f, r1=%s r2=%s"),
+ np[0].x,np[0].y,
+ np[1].x,np[1].y,
+ nc[0].x,nc[0].y,
+ nc[1].x,nc[1].y,
+ na[0],na[1],
+ FormatDistance(nr[0]),FormatDistance(nr[1]));
+ return FALSE;
+ }
+
+ } else {
+ tempSegs_da.cnt = 0;
+ CallCornu0(np,nc,na,nr,&tempSegs_da,FALSE);
+ }
+ if ( p0R ) *p0R = np[0];
+ if ( p1R ) *p1R = np[1];
+ return TRUE;
+}
+
+/*
+ * When an undo is run, the array of segs is missing - they are not saved to the Undo log. So Undo calls this routine to
+ * ensure
+ * - that the Segs are restored and
+ * - other fields reset.
+ */
+EXPORT BOOL_T RebuildCornu (track_p trk)
+{
+ struct extraData *xx;
+ xx = GetTrkExtraData(trk);
+ xx->cornuData.arcSegs.max = 0;
+ xx->cornuData.arcSegs.cnt = 0;
+ //if (xx->cornuData.arcSegs.ptr) MyFree(xx->cornuData.arcSegs.ptr);
+ xx->cornuData.arcSegs.ptr = NULL;
+ if (!FixUpCornu0(xx->cornuData.pos,xx->cornuData.c,xx->cornuData.a,xx->cornuData.r, xx)) return FALSE;
+ ComputeCornuBoundingBox(trk, xx);
+ return TRUE;
+}
+
+
+static trackCmd_t cornuCmds = {
+ "CORNU",
+ DrawCornu,
+ DistanceCornu,
+ DescribeCornu,
+ DeleteCornu,
+ WriteCornu,
+ ReadCornu,
+ MoveCornu,
+ RotateCornu,
+ RescaleCornu,
+ NULL,
+ GetAngleCornu,
+ SplitCornu,
+ TraverseCornu,
+ EnumerateCornu,
+ NULL, /* redraw */
+ NULL, /* trim */
+ MergeCornu,
+ NULL, /* modify */
+ GetLengthCornu,
+ GetParamsCornu,
+ MoveCornuEndPt, /* Move EndPt */
+ QueryCornu,
+ NULL, /* ungroup */
+ FlipCornu,
+ NULL,
+ NULL,
+ NULL,
+ MakeParallelCornu,
+ NULL,
+ RebuildCornu
+ };
+
+
+
+
+
+/****************************************
+ *
+ * GRAPHICS COMMANDS
+ *
+ */
+
+
+
+
+track_p NewCornuTrack(coOrd pos[2], coOrd center[2],ANGLE_T angle[2], DIST_T radius[2], trkSeg_t * tempsegs, int count)
+{
+ struct extraData *xx;
+ track_p p;
+ p = NewTrack( 0, T_CORNU, 2, sizeof *xx );
+ xx = GetTrkExtraData(p);
+ xx->cornuData.pos[0] = pos[0];
+ xx->cornuData.pos[1] = pos[1];
+ xx->cornuData.a[0] = angle[0];
+ xx->cornuData.a[1] = angle[1];
+ xx->cornuData.r[0] = radius[0];
+ xx->cornuData.r[1] = radius[1];
+ xx->cornuData.c[0] = center[0];
+ xx->cornuData.c[1] = center[1];
+
+ if (!FixUpCornu0(xx->cornuData.pos,xx->cornuData.c,xx->cornuData.a,xx->cornuData.r, xx)) {
+ ErrorMessage("Create Cornu Failed");
+ return NULL;
+ }
+LOG( log_cornu, 1, ( "NewCornuTrack( EP1 %0.3f, %0.3f, EP2 %0.3f, %0.3f ) = %d\n", pos[0].x, pos[0].y, pos[1].x, pos[1].y, GetTrkIndex(p) ) )
+ ComputeCornuBoundingBox( p, xx );
+ SetTrkEndPoint( p, 0, pos[0], xx->cornuData.a[0]);
+ SetTrkEndPoint( p, 1, pos[1], xx->cornuData.a[1]);
+ CheckTrackLength( p );
+ SetTrkBits( p, TB_HIDEDESC );
+ return p;
+}
+
+
+EXPORT void InitTrkCornu( void )
+{
+ T_CORNU = InitObject( &cornuCmds );
+ log_cornu = LogFindIndex( "Cornu" );
+ log_traverseCornu = LogFindIndex( "traverseCornu" );
+}
+
diff --git a/app/bin/tcornu.h b/app/bin/tcornu.h
new file mode 100644
index 0000000..c41d381
--- /dev/null
+++ b/app/bin/tcornu.h
@@ -0,0 +1,66 @@
+/*
+ * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/tcornu.h,v 1.1 2005-12-07 15:47:36 rc-flyer Exp $
+ */
+
+/* XTrkCad - Model Railroad CAD
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, 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 License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+typedef struct {
+ coOrd pos[2];
+ coOrd c[2];
+ ANGLE_T a[2];
+ DIST_T r[2];
+ DIST_T minCurveRadius;
+ DIST_T maxRateofChange;
+ DIST_T length;
+ ANGLE_T windingAngle;
+ dynArr_t arcSegs;
+ coOrd descriptionOff;
+ char * cornuPath;
+ } cornuData_t;
+
+typedef struct {
+ coOrd pos[2]; //All values for end if trk[end] = NULL
+ DIST_T radius[2]; //0.0 if straight
+ ANGLE_T angle[2]; //Set if straight
+ coOrd center[2]; //Set if radius >0
+ } cornuParm_t;
+
+
+double CornuMaxCurve(coOrd[2],ANGLE_T[2],DIST_T[2]);
+double BezierMathMinRadius(coOrd[4]);
+coOrd BezierMathFindNearestPoint(coOrd *, coOrd[4] , int );
+track_p NewCornuTrack(coOrd pos[2], coOrd center[2], ANGLE_T angle[2], DIST_T radius[2], trkSeg_t * tempsegs, int count);
+DIST_T CornuDistance( coOrd *, coOrd[2], ANGLE_T[2], DIST_T[2], trkSeg_t * ,int , double * );
+BOOL_T FixUpCornu(coOrd pos[2], track_p [2], EPINX_T ep[2], struct extraData* xx);
+BOOL_T FixUpCornu0(coOrd pos[2], coOrd center[2], ANGLE_T angle[2], DIST_T radius[2], struct extraData* xx);
+BOOL_T GetCornuSegmentsFromTrack(track_p, trkSeg_p);
+BOOL_T SetCornuEndPt(track_p trk, EPINX_T inx, coOrd pos, coOrd center, ANGLE_T angle, DIST_T radius);
+BOOL_T RebuildCornu (track_p trk);
+
+STATUS_T CornuDescriptionMove(track_p trk,wAction_t action,coOrd pos );
+DIST_T CornuDescriptionDistance(coOrd pos,track_p trk );
+
+
+BOOL_T CallCornu(coOrd[2],track_p[2],EPINX_T[2],dynArr_t *,cornuParm_t *);
+BOOL_T CallCornu0(coOrd[2], coOrd[2], ANGLE_T[2], DIST_T[2], dynArr_t *,BOOL_T);
+
+BOOL_T GetBezierSegmentsFromCornu(track_p, dynArr_t *);
+
+char * CreateSegPathList(track_p trk);
+
+
+
diff --git a/app/bin/tcurve.c b/app/bin/tcurve.c
index 7e9fc90..7233ebf 100644
--- a/app/bin/tcurve.c
+++ b/app/bin/tcurve.c
@@ -1,8 +1,5 @@
-/*
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/tcurve.c,v 1.3 2009-06-15 19:29:57 m_fischer Exp $
- *
+/** \file tcurve.c
* CURVE
- *
*/
/* XTrkCad - Model Railroad CAD
@@ -23,11 +20,20 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-#include "track.h"
+#include <assert.h>
+#include <math.h>
+
#include "ccurve.h"
-#include "cstraigh.h"
#include "cjoin.h"
+#include "cstraigh.h"
+#include "cundo.h"
+#include "fileio.h"
#include "i18n.h"
+#include "layout.h"
+#include "messages.h"
+#include "param.h"
+#include "track.h"
+#include "utility.h"
static TRKTYP_T T_CURVE = -1;
@@ -43,6 +49,7 @@ struct extraData {
#define xcircle extraData->circle
static int log_curve = 0;
+static int log_curveSegs = 0;
static DIST_T GetLengthCurve( track_p );
@@ -261,26 +268,28 @@ STATUS_T CurveDescriptionMove(
coOrd pos )
{
struct extraData *xx = GetTrkExtraData(trk);
- static coOrd p0;
+ static coOrd p0,p1;
+ static BOOL_T editMode;
wDrawColor color;
ANGLE_T a, a0, a1;
DIST_T d;
+ p0 = xx->pos;
+
switch (action) {
case C_DOWN:
case C_MOVE:
case C_UP:
+ editMode = TRUE;
color = GetTrkColor( trk, &mainD );
- DrawCurveDescription( trk, &tempD, color );
if ( xx->helixTurns > 0 ) {
- if (action != C_DOWN)
- DrawLine( &tempD, xx->pos, p0, 0, wDrawColorBlack );
xx->descriptionOff.x = (pos.x-xx->pos.x);
xx->descriptionOff.y = (pos.y-xx->pos.y);
- p0 = pos;
+ p1 = pos;
if (action != C_UP)
- DrawLine( &tempD, xx->pos, p0, 0, wDrawColorBlack );
+ DrawLine( &tempD, p0, p1, 0, wDrawColorBlack );
} else {
+ p1 = pos;
GetCurveAngles( &a0, &a1, trk );
if ( a1 < 1 ) a1 = 1.0;
a = FindAngle( xx->pos, pos );
@@ -301,14 +310,18 @@ STATUS_T CurveDescriptionMove(
if ( d < 0.1 )
d = 0.1;
xx->descriptionOff.y = d * 2.0 - 1.0;
+ GetCurveAngles( &a0, &a1, trk );
+ a = a0 + (0.5 * a1);
+ PointOnCircle( &p0, xx->pos, xx->radius/2, a );
}
- DrawCurveDescription( trk, &tempD, color );
- MainRedraw();
+ if (action == C_UP) editMode = FALSE;
+ MainRedraw();
+ MapRedraw();
return action==C_UP?C_TERMINATE:C_CONTINUE;
case C_REDRAW:
- if ( xx->helixTurns > 0 ) {
- DrawLine( &tempD, xx->pos, p0, 0, wDrawColorBlack );
+ if (editMode) {
+ DrawLine( &tempD, p1, p0, 0, wDrawColorBlack );
}
break;
@@ -335,15 +348,15 @@ static struct {
ANGLE_T angle;
FLOAT_T grade;
descPivot_t pivot;
- LAYER_T layerNumber;
+ unsigned int layerNumber;
} crvData;
typedef enum { E0, Z0, E1, Z1, CE, RA, TU, SE, LN, AL, A1, A2, GR, PV, LY } crvDesc_e;
static descData_t crvDesc[] = {
-/*E0*/ { DESC_POS, N_("End Pt 1: X"), &crvData.endPt[0] },
+/*E0*/ { DESC_POS, N_("End Pt 1: X,Y"), &crvData.endPt[0] },
/*Z0*/ { DESC_DIM, N_("Z"), &crvData.elev[0] },
-/*E1*/ { DESC_POS, N_("End Pt 2: X"), &crvData.endPt[1] },
+/*E1*/ { DESC_POS, N_("End Pt 2: X,Y"), &crvData.endPt[1] },
/*Z1*/ { DESC_DIM, N_("Z"), &crvData.elev[1] },
-/*CE*/ { DESC_POS, N_("Center: X"), &crvData.center },
+/*CE*/ { DESC_POS, N_("Center: X,Y"), &crvData.center },
/*RA*/ { DESC_DIM, N_("Radius"), &crvData.radius },
/*TU*/ { DESC_LONG, N_("Turns"), &crvData.turns },
/*SE*/ { DESC_DIM, N_("Separation"), &crvData.separation },
@@ -380,6 +393,10 @@ static void UpdateCurve( track_p trk, int inx, descData_p descUpd, BOOL_T final
ErrorMessage( MSG_RADIUS_GTR_0 );
crvData.radius = xx0.radius;
crvDesc[RA].mode |= DESC_CHANGE;
+ } else if (crvData.radius > 10000) {
+ ErrorMessage( MSG_RADIUS_GTR_10000 );
+ crvData.radius = xx0.radius;
+ crvDesc[RA].mode |= DESC_CHANGE;
} else {
if ( crvData.pivot == DESC_PIVOT_FIRST || GetTrkEndTrk(trk,0) ) {
Translate( &xx0.pos, xx0.pos, a0, xx0.radius-crvData.radius );
@@ -1164,6 +1181,10 @@ static BOOL_T GetParamsCurve( int inx, track_p trk, coOrd pos, trackParams_t * p
params->type = curveTypeCurve;
GetTrkCurveCenter( trk, &params->arcP, &params->arcR);
GetCurveAngles( &params->arcA0, &params->arcA1, trk );
+ ANGLE_T angle1 = FindAngle(params->arcP,pos);
+
+ params->track_angle = NormalizeAngle(FindAngle(params->arcP,pos)+90);
+
if ( easeR > 0.0 && params->arcR < easeR ) {
ErrorMessage( MSG_RADIUS_LSS_EASE_MIN,
FormatDistance( params->arcR ), FormatDistance( easeR ) );
@@ -1184,12 +1205,20 @@ static BOOL_T GetParamsCurve( int inx, track_p trk, coOrd pos, trackParams_t * p
params->arcA1 = 360.0;
}
} else {
- if ( IsCurveCircle( trk ) )
+ params->circleOrHelix = FALSE;
+ if ( IsCurveCircle( trk ) ) {
params->ep = PickArcEndPt( params->arcP, /*Dj.inp[0].*/pos, pos );
- else
- params->ep = PickUnconnectedEndPoint( pos, trk );
+ params->angle = params->track_angle;
+ params->circleOrHelix = TRUE;
+ return TRUE;
+ } else if (inx == PARAMS_CORNU ) {
+ params->ep = PickEndPoint(pos, trk);
+ } else {
+ params->ep = PickUnconnectedEndPointSilent( pos, trk );
+ }
if (params->ep == -1)
return FALSE;
+ params->angle = GetTrkEndAngle(trk,params->ep); ;
}
return TRUE;
}
@@ -1224,11 +1253,27 @@ static BOOL_T QueryCurve( track_p trk, int query )
case Q_FLIP_ENDPTS:
case Q_ISTRACK:
case Q_HAS_DESC:
+ case Q_CORNU_CAN_MODIFY:
+ case Q_MODIFY_CAN_SPLIT:
return TRUE;
+ break;
case Q_EXCEPTION:
- return xx->radius < minTrackRadius;
+ return xx->radius < GetLayoutMinTrackRadius() - EPSILON;
+ break;
case Q_NOT_PLACE_FROGPOINTS:
return IsCurveCircle( trk );
+ break;
+ //case Q_CAN_EXTEND:
+ // if (xx->helixTurns > 0) return FALSE;
+ // return TRUE;
+ // break;
+ case Q_CANNOT_PLACE_TURNOUT:
+ return (xx->helixTurns > 0);
+ break;
+ case Q_HAS_VARIABLE_ENDPOINTS:
+ if ((xx->helixTurns >0) || xx->circle) return TRUE;
+ return FALSE;
+ break;
default:
return FALSE;
}
@@ -1337,38 +1382,61 @@ EXPORT void CurveSegProc(
case SEGPROC_TRAVERSE1:
a1 = FindAngle( segPtr->u.c.center, data->traverse1.pos );
- a1 += (segPtr->u.c.radius>0?90.0:-90.0);
- a2 = NormalizeAngle( data->traverse1.angle+a1 );
- data->traverse1.backwards = (a2 < 270 && a2 > 90 );
+ a1 = NormalizeAngle(a1+90); // ClockWise angle
+ // work out within this segment which way we are going
+ a2 = NormalizeAngle( a1 - data->traverse1.angle );
+ data->traverse1.backwards = ((a2 < 270) && (a2 > 90 ));
+ //Find angular distance from end opposite to direction of travel
a2 = FindAngle( segPtr->u.c.center, data->traverse1.pos );
- if ( data->traverse1.backwards == (segPtr->u.c.radius<0) ) {
- a2 = NormalizeAngle( a2-segPtr->u.c.a0 );
+ //A segment may be fractionally too short - limit to angles within segment!
+ int res = AngleInRange(a2,segPtr->u.c.a0,segPtr->u.c.a1);
+ if (res == 1 ) {
+LOG( log_curveSegs, 1, ("CrvSegsAngle miss A%0.3f S%0.3f E%0.3f R%d B%d \n",a2,segPtr->u.c.a0,segPtr->u.c.a1,res,data->traverse1.backwards))
+ a2 = segPtr->u.c.a0;
+ } else if (res == -1) {
+LOG( log_curveSegs, 1, ("CrvSegsAngle miss A%0.3f S%0.3f E%0.3f R%d B%d \n",a2,segPtr->u.c.a0,segPtr->u.c.a1,res,data->traverse1.backwards))
+ a2 = segPtr->u.c.a1+segPtr->u.c.a0;
+ }
+ //Fix issue of angles passing through zero -
+ if ( !data->traverse1.backwards ) {
+ a2 = NormalizeAngle(DifferenceBetweenAngles(segPtr->u.c.a0,a2));
} else {
- a2 = NormalizeAngle( segPtr->u.c.a0+segPtr->u.c.a1-a2 );
+ a2 = NormalizeAngle(DifferenceBetweenAngles(a2,segPtr->u.c.a0+segPtr->u.c.a1));
}
- data->traverse1.dist = a2/360.0*2*M_PI*fabs(segPtr->u.c.radius);
+
+ //Make sure backwards means going towards EP0
+ if (segPtr->u.c.radius<0) data->traverse1.backwards = !data->traverse1.backwards;
+ data->traverse1.dist = a2/360.0*2*M_PI*fabs(segPtr->u.c.radius); //Distance from end in direction of travel
+ data->traverse1.reverse_seg = ((segPtr->u.c.a0>=90) && (segPtr->u.c.a0<270));
+ data->traverse1.negative = (segPtr->u.c.radius < 0);
+ data->traverse1.segs_backwards = FALSE;
+ data->traverse1.BezSegInx = 0;
+LOG( log_curveSegs, 2, (" CrvSegs D=%0.3f A%0.3f B%d \n",data->traverse1.dist,data->traverse1.backwards))
break;
case SEGPROC_TRAVERSE2:
circum = 2*M_PI*segPtr->u.c.radius;
if ( circum < 0 )
circum = - circum;
- d = segPtr->u.c.a1/360.0*circum;
+ d = (segPtr->u.c.a1*circum)/360;
if ( d > data->traverse2.dist ) {
- a2 = (data->traverse2.dist)/circum*360.0;
- if ( data->traverse2.segDir == (segPtr->u.c.radius<0) ) {
- a2 = NormalizeAngle( segPtr->u.c.a0+a2 );
- a1 = a2+90;
- } else {
- a2 = NormalizeAngle( segPtr->u.c.a0+segPtr->u.c.a1-a2 );
- a1 = a2-90;
- }
- PointOnCircle( &data->traverse2.pos, segPtr->u.c.center, fabs(segPtr->u.c.radius), a2 );
+ a2 = (data->traverse2.dist*360.0)/circum;
data->traverse2.dist = 0;
- data->traverse2.angle = a1;
} else {
+ a2 = segPtr->u.c.a1;
data->traverse2.dist -= d;
}
+ if (segPtr->u.c.radius<0) data->traverse2.segDir = !data->traverse2.segDir;
+ if ( !data->traverse2.segDir ) {
+ a2 = NormalizeAngle( segPtr->u.c.a0+a2 );
+ a1 = NormalizeAngle(a2+90);
+ } else {
+ a2 = NormalizeAngle( segPtr->u.c.a0+segPtr->u.c.a1-a2 );
+ a1 = NormalizeAngle(a2-90);
+ }
+ PointOnCircle( &data->traverse2.pos, segPtr->u.c.center, fabs(segPtr->u.c.radius), a2 );
+ data->traverse2.angle = a1;
+
break;
case SEGPROC_DRAWROADBEDSIDE:
@@ -1429,8 +1497,14 @@ EXPORT void CurveSegProc(
data->split.newSeg[s1].u.c.a1 -= a2;
break;
+
case SEGPROC_GETANGLE:
- data->getAngle.angle = NormalizeAngle( FindAngle( data->getAngle.pos, segPtr->u.c.center ) + 90 );
+ data->getAngle.angle = NormalizeAngle( FindAngle( segPtr->u.c.center, data->getAngle.pos ) + 90 );
+ data->getAngle.negative_radius = segPtr->u.c.radius<0;
+ data->getAngle.radius = fabs(segPtr->u.c.radius);
+ data->getAngle.center = segPtr->u.c.center;
+ data->getAngle.backwards = segPtr->u.c.a0>=90 && segPtr->u.c.a0<270;
+ if (data->getAngle.backwards) data->getAngle.angle = NormalizeAngle(data->getAngle.angle+180);
break;
}
}
@@ -1457,13 +1531,14 @@ EXPORT void PlotCurve(
coOrd posx;
switch ( mode ) {
+ case crvCmdFromCornu:
case crvCmdFromEP1:
angle = FindAngle( pos0, pos1 );
d0 = FindDistance( pos0, pos2 )/2.0;
a0 = FindAngle( pos0, pos2 );
a1 = NormalizeAngle( a0 - angle );
LOG( log_curve, 3, ( "P1 = [%0.3f %0.3f] D=%0.3f A0=%0.3f A1=%0.3f\n", pos2.x, pos2.y, d0, a0, a1 ) )
- if (fabs(d0*sin(D2R(a1))) < (4.0/75.0)*mainD.scale) {
+ if ((fabs(d0*sin(D2R(a1))) < (4.0/75.0)*mainD.scale) & (mode == crvCmdFromEP1)) {
LOG( log_curve, 3, ( "Straight: %0.3f < %0.3f\n", d0*sin(D2R(a1)), (4.0/75.0)*mainD.scale ) )
curveData->pos1.x = pos0.x + d0*2.0*sin(D2R(angle));
curveData->pos1.y = pos0.y + d0*2.0*cos(D2R(angle));
@@ -1485,7 +1560,7 @@ LOG( log_curve, 3, ( "Straight: %0.3f < %0.3f\n", d0*sin(D2R(a1)), (4.0/75.0)*ma
else
curveData->curveRadius = d0/sin(D2R(-a1));
}
- if (curveData->curveRadius > 1000) {
+ if (curveData->curveRadius > 1000 && mode == crvCmdFromEP1) {
LOG( log_curve, 3, ( "Straight %0.3f > 1000\n", curveData->curveRadius ) )
curveData->pos1.x = pos0.x + d0*2.0*sin(D2R(angle));
curveData->pos1.y = pos0.y + d0*2.0*cos(D2R(angle));
@@ -1498,9 +1573,11 @@ LOG( log_curve, 3, ( "Straight: %0.3f < %0.3f\n", d0*sin(D2R(a1)), (4.0/75.0)*ma
if (a1 > 0.0) {
curveData->a0 = NormalizeAngle( a2-180 );
curveData->a1 = a1 * 2.0;
+ curveData->negative = FALSE;
} else {
curveData->a1 = (-a1) * 2.0;
curveData->a0 = NormalizeAngle( a2-180-curveData->a1 );
+ curveData->negative = TRUE;
}
curveData->type = curveTypeCurve;
}
@@ -1551,9 +1628,11 @@ LOG( log_curve, 3, ( "Straight: %0.3f < %0.3f\n", d0*sin(D2R(a1)), (4.0/75.0)*ma
if ( r > 0 ) {
curveData->a0 = a0;
curveData->a1 = NormalizeAngle(a1-a0);
+ curveData->negative = FALSE;
} else {
curveData->a0 = a1;
curveData->a1 = NormalizeAngle(a0-a1);
+ curveData->negative = TRUE;
}
curveData->type = curveTypeCurve;
break;
@@ -1584,4 +1663,5 @@ EXPORT void InitTrkCurve( void )
{
T_CURVE = InitObject( &curveCmds );
log_curve = LogFindIndex( "curve" );
+ log_curveSegs = LogFindIndex( "curveSegs");
}
diff --git a/app/bin/tease.c b/app/bin/tease.c
index 3667fe1..ad281df 100644
--- a/app/bin/tease.c
+++ b/app/bin/tease.c
@@ -1,8 +1,5 @@
-/*
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/tease.c,v 1.2 2008-01-20 23:29:15 mni77 Exp $
- *
+/** \file tease.c
* TRANSISTION-CURVES (JOINTS)
- *
*/
/* XTrkCad - Model Railroad CAD
@@ -63,12 +60,20 @@ For a better representation of this, build 'testjoin' and
do 'testjoin psplot 10 10 40 1 | lpr -Ppostscript'
*/
+#include <math.h>
-#include "track.h"
#include "ccurve.h"
+#include "cselect.h"
#include "cstraigh.h"
#include "cjoin.h"
+#include "cundo.h"
+#include "fileio.h"
#include "i18n.h"
+#include "layout.h"
+#include "messages.h"
+#include "param.h"
+#include "track.h"
+#include "utility.h"
static TRKTYP_T T_EASEMENT = -1;
@@ -404,7 +409,7 @@ static track_p NewJoint(
static coOrd qZero = { 0.0, 0.0 };
ANGLE_T az0, a01, b, b01, b1, d, d1;
trk = NewTrack( 0, T_EASEMENT, 2, sizeof *xx );
- SetTrkScale( trk, curScaleInx );
+ SetTrkScale( trk, GetLayoutCurScale() );
xx = GetTrkExtraData( trk );
SetTrkEndPoint( trk, 0, pos0, NormalizeAngle(angle0+180.0) );
SetTrkEndPoint( trk, 1, pos1, NormalizeAngle(angle1+180.0) );
@@ -491,15 +496,15 @@ static struct {
DIST_T l1;
FLOAT_T grade;
descPivot_t pivot;
- LAYER_T layerNumber;
+ unsigned int layerNumber;
} jointData;
typedef enum { E0, Z0, E1, Z1, OR, AL, RR, LL, L0, L1, GR, PV, LY } jointDesc_e;
static descData_t jointDesc[] = {
-/*E0*/ { DESC_POS, N_("End Pt 1: X"), &jointData.endPt[0] },
+/*E0*/ { DESC_POS, N_("End Pt 1: X,Y"), &jointData.endPt[0] },
/*Z0*/ { DESC_DIM, N_("Z"), &jointData.elev[0] },
-/*E1*/ { DESC_POS, N_("End Pt 2: X"), &jointData.endPt[1] },
+/*E1*/ { DESC_POS, N_("End Pt 2: X,Y"), &jointData.endPt[1] },
/*Z1*/ { DESC_DIM, N_("Z"), &jointData.elev[1] },
-/*OR*/ { DESC_POS, N_("Origin: X"), &jointData.orig },
+/*OR*/ { DESC_POS, N_("Origin: X,Y"), &jointData.orig },
/*AL*/ { DESC_ANGLE, N_("Angle"), &jointData.angle },
/*RR*/ { DESC_DIM, N_("R"), &jointData.r },
/*LL*/ { DESC_DIM, N_("L"), &jointData.l },
@@ -1289,7 +1294,7 @@ static BOOL_T MergeJoint(
static BOOL_T GetParamsJoint( int inx, track_p trk, coOrd pos, trackParams_t * params )
{
params->type = curveTypeStraight;
- params->ep = PickUnconnectedEndPoint( pos, trk );
+ params->ep = PickUnconnectedEndPointSilent( pos, trk );
if (params->ep == -1)
return FALSE;
params->lineOrig = GetTrkEndPos(trk,params->ep);
@@ -1516,8 +1521,11 @@ EXPORT void JointSegProc(
else
data->traverse1.dist = JoinD( segPtr->u.j.l1, segPtr->u.j.R, segPtr->u.j.L ) + JoinD( l, segPtr->u.j.R, segPtr->u.j.L );
}
- if ( segPtr->u.j.flip )
+ data->traverse1.reverse_seg = FALSE;
+ if ( segPtr->u.j.flip ) {
data->traverse1.backwards = !data->traverse1.backwards;
+ data->traverse1.reverse_seg = TRUE;
+ }
LOG( log_traverseJoint, 1, ( "TJ0: ?[%0.3f %0.3f] A=%0.3f l=%0.3f J[%0.3f %0.3f] A=%0.3f l0=%0.3f l1=%0.3f R=%0.3f L=%0.3f N:%d F:%d S:%d = a=%0.3f D=%0.3f B=%d\n",
data->traverse1.pos.x, data->traverse1.pos.y, data->traverse1.angle,
l,
@@ -1525,6 +1533,9 @@ LOG( log_traverseJoint, 1, ( "TJ0: ?[%0.3f %0.3f] A=%0.3f l=%0.3f J[%0.3f %0.3f]
segPtr->u.j.l0, segPtr->u.j.l1, segPtr->u.j.R, segPtr->u.j.L,
segPtr->u.j.negate, segPtr->u.j.flip, segPtr->u.j.Scurve,
a, data->traverse1.dist, data->traverse1.backwards ) );
+ data->traverse1.negative = FALSE;
+ data->traverse1.BezSegInx = 0;
+ data->traverse1.segs_backwards = FALSE;
break;
case SEGPROC_TRAVERSE2:
@@ -1632,6 +1643,7 @@ LOG( log_traverseJoint, 1, ( "TJ0: ?[%0.3f %0.3f] A=%0.3f l=%0.3f J[%0.3f %0.3f]
}
}
data->getAngle.angle = a;
+ data->getAngle.radius = 0.0;
break;
}
}
diff --git a/app/bin/track.c b/app/bin/track.c
index bbbf48a..22af292 100644
--- a/app/bin/track.c
+++ b/app/bin/track.c
@@ -1,5 +1,5 @@
-/*
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/track.c,v 1.7 2009-07-05 15:11:02 m_fischer Exp $
+/** \file track.c
+ * Track
*/
/* XTrkCad - Model Railroad CAD
@@ -20,17 +20,29 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
+#include <assert.h>
#include <time.h>
#include <ctype.h>
#include <stdarg.h>
#include <math.h>
-#include "track.h"
+#include <string.h>
+
#include "ccurve.h"
-#include "cstraigh.h"
#include "cjoin.h"
#include "compound.h"
-#include "i18n.h"
+#include "cselect.h"
+#include "cstraigh.h"
+#include "cundo.h"
+#include "custom.h"
#include "draw.h"
+#include "fileio.h"
+#include "i18n.h"
+#include "layout.h"
+#include "messages.h"
+#include "param.h"
+#include "paths.h"
+#include "track.h"
+#include "utility.h"
#ifndef TRACKDEP
#ifndef FASTTRACK
@@ -64,8 +76,10 @@ static int log_readTracks = 0;
EXPORT wIndex_t trackCount;
EXPORT long drawEndPtV = 2;
+EXPORT long drawUnconnectedEndPt = 0; /**< How do we draw Unconnected EndPts */
EXPORT long centerDrawMode = FALSE; /**< flag to control drawing of circle centers */
+EXPORT long printCenterLines = FALSE; /**< flag to control drawing of centerline in Print */
static BOOL_T exportingTracks = FALSE;
@@ -78,15 +92,15 @@ static dynArr_t trackCmds_da;
EXPORT BOOL_T useCurrentLayer = FALSE;
-EXPORT LAYER_T curTrackLayer;
+EXPORT unsigned int curTrackLayer;
EXPORT coOrd descriptionOff;
EXPORT DIST_T roadbedWidth = 0.0;
EXPORT DIST_T roadbedLineWidth = 3.0/75.0;
-EXPORT DIST_T minTrackRadius;
-EXPORT DIST_T maxTrackGrade = 5.0;
+//EXPORT DIST_T minTrackRadius;
+//EXPORT DIST_T maxTrackGrade = 5.0;
static int suspendElevUpdates = FALSE;
@@ -115,9 +129,9 @@ EXPORT void DescribeTrack( track_cp trk, char * str, CSIZE_T len )
}
-EXPORT DIST_T GetTrkDistance( track_cp trk, coOrd pos )
+EXPORT DIST_T GetTrkDistance( track_cp trk, coOrd * pos )
{
- return trackCmds( GetTrkType(trk) )->distance( trk, &pos );
+ return trackCmds( GetTrkType(trk) )->distance( trk, pos );
}
/**
@@ -133,7 +147,7 @@ EXPORT DIST_T GetTrkDistance( track_cp trk, coOrd pos )
* \return NULL if there is no track, pointer to track otherwise
*/
-EXPORT track_p OnTrack2( coOrd * fp, BOOL_T complain, BOOL_T track, BOOL_T ignoreHidden )
+EXPORT track_p OnTrack2( coOrd * fp, BOOL_T complain, BOOL_T track, BOOL_T ignoreHidden, track_p t )
{
track_p trk;
DIST_T distance, closestDistance = 1000000;
@@ -148,6 +162,7 @@ EXPORT track_p OnTrack2( coOrd * fp, BOOL_T complain, BOOL_T track, BOOL_T ignor
TRK_ITERATE( trk ) {
if ( track && !IsTrack(trk) )
continue;
+ if (trk == t) continue;
if (trk->hi.x < q0.x ||
trk->lo.x > q1.x ||
trk->hi.y < q0.y ||
@@ -186,7 +201,11 @@ EXPORT track_p OnTrack2( coOrd * fp, BOOL_T complain, BOOL_T track, BOOL_T ignor
EXPORT track_p OnTrack( coOrd * fp, BOOL_T complain, BOOL_T track )
{
- return OnTrack2( fp, complain, track, TRUE );
+ return OnTrack2( fp, complain, track, TRUE, NULL );
+}
+
+EXPORT track_p OnTrackIgnore (coOrd * fp, BOOL_T complain, BOOL_T track, track_p t ) {
+ return OnTrack2(fp, complain, track, TRUE, t);
}
@@ -316,7 +335,7 @@ EXPORT void SetTrkScale( track_p trk, SCALEINX_T si )
trk->scale = (char)si;
}
-EXPORT LAYER_T GetTrkLayer( track_p trk )
+EXPORT unsigned int GetTrkLayer( track_p trk )
{
return trk->layer;
}
@@ -490,13 +509,22 @@ EXPORT void SetTrkEndPtCnt( track_p trk, EPINX_T cnt )
memset( &trk->endPt[oldCnt], 0, (cnt-oldCnt) * sizeof *trk->endPt );
}
-
-EXPORT void SetTrkLayer( track_p trk, int layer )
+/**
+ * Set the layer for a track.
+ *
+ * \param trk IN the layer to change the layer for
+ * \param layer IN the new layer for the track
+ */
+void SetTrkLayer( track_p trk, int layer )
{
+ DecrementLayerObjects(trk->layer);
+
if (useCurrentLayer)
- trk->layer = (LAYER_T)curLayer;
+ trk->layer = (unsigned int)curLayer;
else
- trk->layer = (LAYER_T)layer;
+ trk->layer = (unsigned int)layer;
+
+ IncrementLayerObjects(trk->layer);
}
@@ -580,27 +608,36 @@ EXPORT BOOL_T WriteEndPt( FILE * f, track_cp trk, EPINX_T ep )
assert ( endPt != NULL );
if (endPt->track == NULL ||
( exportingTracks && !GetTrkSelected(endPt->track) ) ) {
- rc &= fprintf( f, "\tE " )>0;
+ rc &= fprintf( f, "\tE4 " )>0;
} else {
- rc &= fprintf( f, "\tT %d ", endPt->track->index )>0;
+ rc &= fprintf( f, "\tT4 %d ", endPt->track->index )>0;
}
rc &= fprintf( f, "%0.6f %0.6f %0.6f", endPt->pos.x, endPt->pos.y, endPt->angle )>0;
option = (endPt->option<<8) | (endPt->elev.option&0xFF);
if ( option != 0 ) {
rc &= fprintf( f, " %ld %0.6f %0.6f", option, endPt->elev.doff.x, endPt->elev.doff.y )>0;
- if ( (endPt->elev.option&ELEV_MASK) != ELEV_NONE ) {
- switch ( endPt->elev.option&ELEV_MASK ) {
- case ELEV_DEF:
- rc &= fprintf( f, " %0.6f", endPt->elev.u.height )>0;
- break;
- case ELEV_STATION:
- rc &= fprintf( f, " \"%s\"", PutTitle( endPt->elev.u.name ) )>0;
- break;
- default:
- ;
- }
+ switch ( endPt->elev.option&ELEV_MASK ) {
+ case ELEV_DEF:
+ rc &= fprintf( f, " %0.6f ", endPt->elev.u.height )>0;
+ break;
+ case ELEV_STATION:
+ rc &= fprintf( f, " \"%s\" ", PutTitle( endPt->elev.u.name ) )>0;
+ break;
+ default:
+ rc &= fprintf( f, " 0.0 ")>0;
}
+ } else {
+ rc &= fprintf( f, " 0 0.0 0.0 0.0 ")>0;
}
+ if ((endPt->elev.option&ELEV_MASK) == ELEV_DEF)
+ rc &= fprintf( f, "%0.6f ",endPt->elev.u.height)>0;
+ else
+ rc &= fprintf( f, "0.0 ")>0;
+ long elevVisible = (endPt->elev.option&ELEV_VISIBLE)?1:0;
+ long elevType = endPt->elev.option&ELEV_MASK;
+ long gapType = endPt->option;
+ rc &= fprintf( f, "%ld %ld %ld ", elevVisible, elevType, gapType)>0;
+ rc &= fprintf( f, "%0.6f ", trk->elev)>0;
rc &= fprintf( f, "\n" )>0;
return rc;
}
@@ -652,6 +689,28 @@ EXPORT EPINX_T PickUnconnectedEndPoint( coOrd p, track_cp trk )
return inx;
}
+EXPORT EPINX_T PickUnconnectedEndPointSilent( coOrd p, track_cp trk )
+{
+ EPINX_T inx, i;
+ DIST_T d=10000.0, dd;
+ coOrd pos;
+ inx = -1;
+
+ for ( i=0; i<trk->endCnt; i++ ) {
+ if (trk->endPt[i].track == NULL) {
+ pos = trk->endPt[i].pos;
+ dd=FindDistance(p, pos);
+ if (inx == -1 || dd <= d) {
+ d = dd;
+ inx = i;
+ }
+ }
+ }
+
+ return inx;
+}
+
+
EXPORT EPINX_T GetEndPtConnectedToMe( track_p trk, track_p me )
{
@@ -805,6 +864,15 @@ EXPORT BOOL_T MakeParallelTrack(
return FALSE;
}
+EXPORT BOOL_T RebuildTrackSegs(
+ track_p trk)
+{
+ if (trackCmds(trk->type)->rebuildSegs)
+ return trackCmds(trk->type)->rebuildSegs(trk);
+ return FALSE;
+}
+
+
/*****************************************************************************
*
@@ -855,7 +923,7 @@ LOG( log_track, 1, ( "NewTrack( T%d, t%d, E%d, X%ld)\n", index, type, endCnt, ex
trk->index = index;
trk->type = type;
trk->layer = curLayer;
- trk->scale = (char)curScaleInx;
+ trk->scale = (char)GetLayoutCurScale();
trk->bits = TB_VISIBLE;
trk->elevMode = ELEV_ALONE;
trk->elev = 0;
@@ -874,6 +942,7 @@ LOG( log_track, 1, ( "NewTrack( T%d, t%d, E%d, X%ld)\n", index, type, endCnt, ex
trk->extraSize = extraSize;
UndoNew( trk );
trackCount++;
+ IncrementLayerObjects(curLayer);
InfoCount( trackCount );
return trk;
}
@@ -971,10 +1040,12 @@ LOG( log_track, 4, ( "DeleteTrack(T%d)\n", GetTrkIndex(trk) ) )
}
CheckDeleteSwitchmotor( trk );
CheckDeleteBlock( trk );
- UndoDelete( trk );
- MainRedraw();
+ DecrementLayerObjects(trk->layer);
trackCount--;
AuditTracks( "deleteTrack T%d", trk->index);
+ UndoDelete(trk); /**< Attention: trk is invalidated during that call */
+ MainRedraw();
+ MapRedraw();
InfoCount( trackCount );
return TRUE;
}
@@ -1296,8 +1367,10 @@ static void AuditPrint( char * msg )
{
time_t clock;
if (auditFile == NULL) {
- sprintf( message, "%s%s%s", workingDir, FILE_SEP_CHAR, sAuditF );
- auditFile = fopen( message, "a+" );
+ char *path;
+ MakeFullpath(&path, workingDir, sAuditF, NULL);
+ auditFile = fopen( path, "a+" );
+ free(path);
if (auditFile == NULL) {
NoticeMessage( MSG_OPEN_FAIL, _("Continue"), NULL, _("Audit"), message, strerror(errno) );
auditIgnore = TRUE;
@@ -1524,6 +1597,7 @@ EXPORT STATUS_T EndPtDescriptionMove(
if (action != C_UP)
DrawLine( &tempD, p0, p1, 0, wDrawColorBlack );
MainRedraw();
+ MapRedraw();
return action==C_UP?C_TERMINATE:C_CONTINUE;
case C_REDRAW:
@@ -1697,9 +1771,11 @@ EXPORT BOOL_T SplitTrack( track_p trk, coOrd pos, EPINX_T ep, track_p *leftover,
*leftover = NULL;
LOG( log_track, 2, ( "SplitTrack( T%d[%d], (%0.3f %0.3f)\n", trk->index, ep, pos.x, pos.y ) )
- if ((splitCmd = trackCmds(trk->type)->split) == NULL) {
- ErrorMessage( MSG_CANT_SPLIT_TRK, trackCmds(trk->type)->name );
- return FALSE;
+ if (((splitCmd = trackCmds(trk->type)->split) == NULL)) {
+ if (!(FindDistance( trk->endPt[ep].pos, pos) <= minLength)) {
+ ErrorMessage( MSG_CANT_SPLIT_TRK, trackCmds(trk->type)->name );
+ return FALSE;
+ }
}
UndrawNewTrack( trk );
UndoModify( trk );
@@ -1720,11 +1796,6 @@ LOG( log_track, 2, ( "SplitTrack( T%d[%d], (%0.3f %0.3f)\n", trk->index, ep, pos
*leftover = trk2;
DrawNewTrack( trk );
-#ifdef LATER
- } else if ( IsTurnout(trk) ) {
- ErrorMessage( MSG_CANT_SPLIT_TRK, _("Turnout") );
- return FALSE;
-#endif
} else if ( epCnt == 2 &&
(d = FindDistance( trk->endPt[1-ep].pos, pos )) <= minLength) {
@@ -1737,15 +1808,9 @@ LOG( log_track, 2, ( "SplitTrack( T%d[%d], (%0.3f %0.3f)\n", trk->index, ep, pos
DisconnectTracks( trk, 1-ep, trk2, ep2 );
LOG( log_track, 2, ( " at endPt with T%d[%d]\n", trk2->index, ep2 ) )
DrawNewTrack( trk2 );
-#ifdef LATER
- *trk = trk2;
- *ep = ep1;
- *leftover = trk;
-#endif
+
} else {
-#ifdef LATER
- *trk = NULL;
-#endif
+
LOG( log_track, 2, ( " at endPt (no connection)\n") )
}
DrawNewTrack( trk );
@@ -1833,6 +1898,7 @@ EXPORT BOOL_T TraverseTrack(
return FALSE;
trvTrk->length = -1;
trvTrk->dist = 0.0;
+
}
return TRUE;
}
@@ -1954,22 +2020,6 @@ EXPORT BOOL_T GetTrackParams( int inx, track_p trk, coOrd pos, trackParams_t * p
return trackCmds(trk->type)->getTrackParams( inx, trk, pos, params );
} else {
ASSERT( FALSE ); /* CHECKME */
-#ifdef LATER
- switch ( inx ) {
- case PARAMS_1ST_JOIN:
- case PARAMS_2ND_JOIN:
- ErrorMessage( MSG_JOIN_TRK, (inx==PARAMS_1ST_JOIN?_("First"):_("Second")) );
- break;
- case PARAMS_EXTEND:
- ErrorMessage( MSG_CANT_EXTEND );
- break;
- case PARAMS_PARALLEL:
- ErrorMessage( MSG_INV_TRK_PARALLEL );
- break;
- default:
- ErrorMessage( MSG_INVALID_TRK );
- }
-#endif
return FALSE;
}
}
@@ -2113,15 +2163,7 @@ EXPORT void DrawTie(
Translate( &p[2], pos, angle+180, length );
Translate( &p[3], p[2], angle-90, width );
Translate( &p[2], p[2], angle+90, width );
-#ifdef LATER
- lo = hi = p[0];
- for ( i=1; i<4; i++ ) {
- if ( p[i].x < lo.x ) lo.x = p[i].x;
- if ( p[i].y < lo.y ) lo.y = p[i].y;
- if ( p[i].x > hi.x ) hi.x = p[i].x;
- if ( p[i].y > hi.y ) hi.y = p[i].y;
- }
-#endif
+
if ( d == &mainD ) {
lo.x -= RBORDER/mainD.dpi*mainD.scale;
lo.y -= TBORDER/mainD.dpi*mainD.scale;
@@ -2150,7 +2192,7 @@ EXPORT void DrawCurvedTies(
ANGLE_T a1,
wDrawColor color )
{
- tieData_p td = GetScaleTieData(GetTrkScale(trk));
+ tieData_p td;
DIST_T len;
ANGLE_T ang, dang;
coOrd pos;
@@ -2160,6 +2202,9 @@ EXPORT void DrawCurvedTies(
return;
if ( trk == NULL )
return;
+
+ td = GetScaleTieData(GetTrkScale(trk));
+
if ( (!GetTrkVisible(trk)) && drawTunnel!=DRAW_TUNNEL_SOLID )
return;
if (color == wDrawColorBlack)
@@ -2235,7 +2280,8 @@ LOG( log_track, 4, ( "DST( (%0.3f %0.3f) R%0.3f A%0.3f..%0.3f)\n",
} else if (d->options & DC_QUICK) {
DrawArc( d, p, r, a0, a1, ((d->scale<32) && centerDrawMode && !(options&DTS_NOCENTER)) ? 1 : 0, 0, color );
} else {
- if ( (d->scale <= 1 && (d->options&DC_SIMPLE)==0) || (d->options&DC_CENTERLINE)!=0 ) {
+ if ( (d->scale <= 1 && (d->options&DC_SIMPLE)==0) || (d->options&DC_CENTERLINE)!=0
+ || (d->scale <= scale2rail/2 && d->options&DC_PRINT && printCenterLines)) { // if printing two rails respect print CenterLine option
long options = d->options;
d->options |= DC_DASH;
DrawArc( d, p, r, a0, a1, 0, 0, color );
@@ -2263,7 +2309,7 @@ EXPORT void DrawStraightTies(
coOrd p1,
wDrawColor color )
{
- tieData_p td = GetScaleTieData(GetTrkScale(trk));
+ tieData_p td;
DIST_T tieOff0=0.0, tieOff1=0.0;
DIST_T len, dlen;
coOrd pos;
@@ -2274,6 +2320,8 @@ EXPORT void DrawStraightTies(
return;
if ( trk == NULL )
return;
+
+ td = GetScaleTieData(GetTrkScale(trk));
if ( (!GetTrkVisible(trk)) && drawTunnel!=DRAW_TUNNEL_SOLID )
return;
if ( color == wDrawColorBlack )
@@ -2350,7 +2398,8 @@ LOG( log_track, 4, ( "DST( (%0.3f %0.3f) .. (%0.3f..%0.3f)\n",
} else if (d->options&DC_QUICK) {
DrawLine( d, p0, p1, 0, color );
} else {
- if ( (d->scale <= 1 && (d->options&DC_SIMPLE)==0) || (d->options&DC_CENTERLINE)!=0 ) {
+ if ( (d->scale <= 1 && (d->options&DC_SIMPLE)==0) || (d->options&DC_CENTERLINE)!=0
+ || (d->scale <= scale2rail/2 && d->options&DC_PRINT && printCenterLines)) { // if printing two rails respect print CenterLine option
long options = d->options;
d->options |= DC_DASH;
DrawLine( d, p0, p1, 0, color );
@@ -2393,7 +2442,7 @@ EXPORT wDrawColor GetTrkColor( track_p trk, drawCmd_p d )
}
}
if ( (d->options&(DC_GROUP)) == 0 ) {
- if ( grade > maxTrackGrade )
+ if ( grade > GetLayoutMaxTrackGrade())
return exceptionColor;
if ( QueryTrack( trk, Q_EXCEPTION ) )
return exceptionColor;
@@ -2406,7 +2455,7 @@ EXPORT wDrawColor GetTrkColor( track_p trk, drawCmd_p d )
}
if ( (d->options&(DC_GROUP)) == 0 ) {
if ( (IsTrack(trk)?(colorLayers&1):(colorLayers&2)) )
- return GetLayerColor((LAYER_T)curTrackLayer);
+ return GetLayerColor((unsigned int)curTrackLayer);
}
return wDrawColorBlack;
}
@@ -2434,7 +2483,7 @@ EXPORT void DrawTrack( track_cp trk, drawCmd_p d, wDrawColor color )
return;
if ( (IsTrack(trk)?(colorLayers&1):(colorLayers&2)) &&
d != &mapD && color == wDrawColorBlack )
- color = GetLayerColor((LAYER_T)curTrackLayer);
+ color = GetLayerColor((unsigned int)curTrackLayer);
scale2rail = (d->options&DC_PRINT)?(twoRailScale*2+1):twoRailScale;
if ( (!inDrawTracks) &&
tieDrawMode!=TIEDRAWMODE_NONE &&
@@ -2516,11 +2565,11 @@ static void DrawUnconnectedEndPt( drawCmd_p d, coOrd p, ANGLE_T a, DIST_T trackG
Translate( &p0, p, a, trackGauge );
Translate( &p1, p, a-180.0, trackGauge );
DrawLine( d, p0, p1, 0, color );
- if (d->scale < 8) {
+ if (d->scale < 8 || drawUnconnectedEndPt > 0) {
Translate( &p, p, a+90.0, 0.2 );
Translate( &p0, p, a, trackGauge );
Translate( &p1, p, a-180.0, trackGauge );
- DrawLine( d, p0, p1, 0, color );
+ DrawLine( d, p0, p1, (drawUnconnectedEndPt>0)?4:0, (drawUnconnectedEndPt>1)?exceptionColor:color );
}
}
@@ -2605,7 +2654,7 @@ EXPORT void DrawEndPt(
EPINX_T ep,
wDrawColor color )
{
- coOrd p, pp;
+ coOrd p;
ANGLE_T a;
track_p trk1;
coOrd p0, p1, p2;
@@ -2630,11 +2679,8 @@ EXPORT void DrawEndPt(
if (labelScale >= d->scale)
DrawEndElev( d, trk, ep, color );
- if ( d->scale >= ((d->options&DC_PRINT)?(twoRailScale*2+1):twoRailScale) )
- return;
-
trk1 = GetTrkEndTrk(trk,ep);
- pp = p = GetTrkEndPos( trk, ep );
+ p = GetTrkEndPos( trk, ep );
a = GetTrkEndAngle( trk, ep ) + 90.0;
trackGauge = GetTrkGauge(trk);
@@ -2643,6 +2689,9 @@ EXPORT void DrawEndPt(
return;
}
+ if ( d->scale >= ((d->options&DC_PRINT)?(twoRailScale*2+1):twoRailScale) )
+ return;
+
sepBoundary = FALSE;
if ((d->options&DC_PRINT)==0 && importTrack == NULL && GetTrkSelected(trk) && (!GetTrkSelected(trk1))) {
DIST_T len;
@@ -2734,14 +2783,13 @@ EXPORT void DrawTracks( drawCmd_p d, DIST_T scale, coOrd orig, coOrd size )
{
track_cp trk;
TRKINX_T inx;
- wIndex_t count;
+ wIndex_t count = 0;
coOrd lo, hi;
BOOL_T doSelectRecount = FALSE;
inDrawTracks = TRUE;
- count = 0;
InfoCount( 0 );
- count = 0;
+
d->options |= DC_TIES;
TRK_ITERATE( trk ) {
if ( (d->options&DC_PRINT) != 0 &&
@@ -2779,43 +2827,11 @@ EXPORT void DrawTracks( drawCmd_p d, DIST_T scale, coOrd orig, coOrd size )
}
-EXPORT void RedrawLayer( LAYER_T l, BOOL_T draw )
+EXPORT void RedrawLayer( unsigned int l, BOOL_T draw )
{
MainRedraw();
-#ifdef LATER
- track_cp trk;
- track_cp trk1;
- EPINX_T ep;
- wIndex_t count;
- coOrd hi, lo;
+ MapRedraw();
- count = 0;
- InfoCount( 0 );
- TRK_ITERATE( trk ) {
- if (GetTrkLayer(trk) != l)
- continue;
- GetBoundingBox( trk, &hi, &lo );
- if ( !OFF_MAIND( lo, hi ) ) {
- if ( GetLayerVisible( l ) ) {
- DrawTrack( trk, &mainD, draw?wDrawColorBlack:wDrawColorWhite );
- }
- for (ep=0; ep<GetTrkEndPtCnt(trk); ep++) {
- trk1 = GetTrkEndTrk( trk, ep );
- if ( trk1 && GetTrkLayer(trk1) != l && GetLayerVisible(GetTrkLayer(trk1)) ) {
- DrawEndPt( &mainD, trk1, GetEndPtConnectedToMe( trk1, trk ),
- draw?wDrawColorBlack:wDrawColorWhite );
- }
- }
- count++;
- if (count%10 == 0)
- InfoCount( count );
- }
- if (draw)
- ClrTrkBits( trk, TB_SELECTED );
- }
- InfoCount( trackCount );
- SelectRecount();
-#endif
}
diff --git a/app/bin/track.h b/app/bin/track.h
index e26a47a..e39a98f 100644
--- a/app/bin/track.h
+++ b/app/bin/track.h
@@ -1,5 +1,5 @@
-/*
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/track.h,v 1.3 2009-05-25 18:11:03 m_fischer Exp $
+/** \file track.h
+ *
*/
/* XTrkCad - Model Railroad CAD
@@ -23,24 +23,9 @@
#ifndef TRACK_H
#define TRACK_H
-#include <assert.h>
-#include <stdio.h>
-#include <stdlib.h>
-#ifndef WINDOWS
-#include <unistd.h>
-#endif
-#include <math.h>
-#ifdef HAVE_MALLOC_H
-#include <malloc.h>
-#endif
-#include <string.h>
-
-#include "wlib.h"
#include "common.h"
-#include "utility.h"
#include "draw.h"
-#include "misc.h"
-
+#include "misc2.h"
extern TRKTYP_T T_NOTRACK;
@@ -51,19 +36,19 @@ extern track_p tempTrack;
extern wIndex_t trackCount;
extern long drawTunnel;
extern long drawEndPtV;
+extern long drawUnconnectedEndPt;
extern long centerDrawMode;
extern wDrawColor selectedColor;
extern wDrawColor normalColor;
extern BOOL_T useCurrentLayer;
-extern LAYER_T curTrackLayer;
+extern unsigned int curTrackLayer;
extern coOrd descriptionOff;
extern DIST_T roadbedWidth;
extern DIST_T roadbedLineWidth;
+extern long printCenterLines;
extern long drawElevations;
extern wDrawColor elevColorIgnore;
extern wDrawColor elevColorDefined;
-extern DIST_T minTrackRadius;
-extern DIST_T maxTrackGrade;
extern wDrawColor exceptionColor;
#define TIEDRAWMODE_NONE (0)
#define TIEDRAWMODE_OUTLINE (1)
@@ -81,24 +66,36 @@ extern int pathMax;
extern BOOL_T onTrackInSplit;
-typedef enum { curveTypeNone, curveTypeCurve, curveTypeStraight } curveType_e;
+typedef enum { curveTypeNone, curveTypeCurve, curveTypeStraight, curveTypeBezier, curveTypeCornu } curveType_e;
#define PARAMS_1ST_JOIN (0)
#define PARAMS_2ND_JOIN (1)
#define PARAMS_EXTEND (2)
#define PARAMS_PARALLEL (3)
+#define PARAMS_BEZIER (4) //Not used (yet)
+#define PARAMS_CORNU (5) //Called to get end characteristics
typedef struct {
- curveType_e type;
- EPINX_T ep;
- DIST_T len;
- ANGLE_T angle;
- coOrd lineOrig;
- coOrd lineEnd;
- coOrd arcP;
- DIST_T arcR;
- ANGLE_T arcA0, arcA1;
+ curveType_e type; //Straight, Curve, Bezier, Cornu
+ EPINX_T ep; //End point that is nearby pos
+ DIST_T len; //Length of track
+ ANGLE_T angle; //Angle at end of track
+ coOrd lineOrig; //Start of straight
+ coOrd lineEnd; //End of Straight (or zero for Turnout)
+ coOrd arcP; //center or zero
+ DIST_T arcR; //radius or zero
+ ANGLE_T arcA0, arcA1; //Start angle and angular length (clockwise)
long helixTurns;
+ ANGLE_T track_angle;
+ BOOL_T circleOrHelix; //Track is circle or Helix
+ coOrd bezierPoints[4]; //Bezier Ends and CPs
+ coOrd cornuEnd[2]; //Cornu Ends
+ ANGLE_T cornuAngle[2]; //Angle at Cornu Ends
+ DIST_T cornuRadius[2]; //Radius at Cornu Ends
+ coOrd cornuCenter[2]; //Center at Cornu Ends
+ coOrd ttcenter; //Turntable
+ DIST_T ttradius; //Turntable
+
} trackParams_t;
#define Q_CANNOT_BE_ON_END (1)
@@ -118,13 +115,20 @@ typedef struct {
#define Q_NOT_PLACE_FROGPOINTS (15)
#define Q_HAS_DESC (16)
#define Q_MODIFY_REDRAW_DONT_UNDRAW_TRACK (17)
+#define Q_CAN_MODIFY_CONTROL_POINTS (18) // Is T_BEZIER or T_BEZLIN
+#define Q_IS_CORNU (19) // Is T_CORNU
+#define Q_MODIFY_CAN_SPLIT (20) // Is able to be Split
+#define Q_CAN_EXTEND (21) // Add extra curve or straight in CORNU MODIFY
+#define Q_CAN_ADD_ENDPOINTS (22) // Is T_TURNTABLE
+#define Q_HAS_VARIABLE_ENDPOINTS (23) // Is Helix or Circle
+#define Q_CORNU_CAN_MODIFY (24) // can be modified by CORNU MODIFY
typedef struct {
- track_p trk;
- DIST_T length;
- DIST_T dist;
- coOrd pos;
- ANGLE_T angle;
+ track_p trk; // IN Current Track OUT Next Track
+ DIST_T length; // IN How far to go
+ DIST_T dist; // OUT how far left = 0 if found
+ coOrd pos; // IN/OUT - where we are, where we will be // IN/OUT - where we are now
+ ANGLE_T angle; // IN/OUT - angle now
} traverseTrack_t, *traverseTrack_p;
@@ -159,6 +163,7 @@ typedef struct {
BOOL_T (*checkTraverse)( track_p, coOrd );
BOOL_T (*makeParallel)( track_p, coOrd, DIST_T, track_p *, coOrd *, coOrd * );
void (*drawDesc)( track_p, drawCmd_p, wDrawColor );
+ BOOL_T (*rebuildSegs)(track_p);
} trackCmd_t;
@@ -187,18 +192,30 @@ typedef struct {
dynArr_t tempEndPts_da;
#define tempEndPts(N) DYNARR_N( trkEndPt_t, tempEndPts_da, N )
+typedef enum { FREEFORM, RECTANGLE
+} PolyType_e;
typedef struct {
char type;
wDrawColor color;
DIST_T width;
+ dynArr_t bezSegs; //placed here to avoid overwrites
union {
struct {
- coOrd pos[2];
+ coOrd pos[4];
ANGLE_T angle;
long option;
} l;
struct {
+ coOrd pos[4];
+ ANGLE_T angle0;
+ ANGLE_T angle3;
+ DIST_T minRadius;
+ DIST_T radius0;
+ DIST_T radius3;
+ DIST_T length;
+ } b;
+ struct {
coOrd center;
ANGLE_T a0, a1;
DIST_T radius;
@@ -224,14 +241,17 @@ typedef struct {
coOrd * pts;
coOrd orig;
ANGLE_T angle;
+ PolyType_e polyType;
} p;
} u;
} trkSeg_t, * trkSeg_p;
#define SEG_STRTRK ('S')
#define SEG_CRVTRK ('C')
+#define SEG_BEZTRK ('W')
#define SEG_STRLIN ('L')
#define SEG_CRVLIN ('A')
+#define SEG_BEZLIN ('H')
#define SEG_JNTTRK ('J')
#define SEG_FILCRCL ('G')
#define SEG_POLY ('Y')
@@ -247,7 +267,7 @@ typedef struct {
#define SEG_DIMLIN ('M')
#define SEG_TBLEDGE ('Q')
-#define IsSegTrack( S ) ( (S)->type == SEG_STRTRK || (S)->type == SEG_CRVTRK || (S)->type == SEG_JNTTRK )
+#define IsSegTrack( S ) ( (S)->type == SEG_STRTRK || (S)->type == SEG_CRVTRK || (S)->type == SEG_JNTTRK || (S)->type == SEG_BEZTRK)
dynArr_t tempSegs_da;
#define tempSegs(N) DYNARR_N( trkSeg_t, tempSegs_da, N )
@@ -295,24 +315,31 @@ void DrawSegsO(
DIST_T trackGauge,
wDrawColor color,
long options );
-ANGLE_T GetAngleSegs( wIndex_t, trkSeg_p, coOrd, wIndex_t * );
+ANGLE_T GetAngleSegs( wIndex_t, trkSeg_p, coOrd *, wIndex_t *, DIST_T *, BOOL_T *, wIndex_t *, BOOL_T * );
void RecolorSegs( wIndex_t, trkSeg_p, wDrawColor );
BOOL_T ReadSegs( void );
BOOL_T WriteSegs( FILE * f, wIndex_t segCnt, trkSeg_p segs );
+BOOL_T WriteSegsEnd(FILE * f, wIndex_t segCnt, trkSeg_p segs, BOOL_T writeEnd);
typedef union {
struct {
- coOrd pos; /* IN */
- ANGLE_T angle;
- DIST_T dist; /* OUT */
- BOOL_T backwards;
- } traverse1;
+ coOrd pos; /* IN the point to get to */
+ ANGLE_T angle; /* IN is the angle that the object starts at (-ve for forwards) */
+ DIST_T dist; /* OUT is how far it is along the track to get to pos*/
+ BOOL_T backwards; /* OUT which way are we going? */
+ BOOL_T reverse_seg; /* OUT the seg is backwards curve */
+ BOOL_T negative; /* OUT the curve is negative */
+ int BezSegInx; /* OUT for Bezier Seg - the segment we are on in Bezier */
+ BOOL_T segs_backwards; /* OUT for Bezier Seg - the direction of the overall Bezier */
+ } traverse1; // Find dist between pos and end of track that starts with angle set backwards
struct {
- EPINX_T segDir; /* IN */
- DIST_T dist; /* IN/OUT */
- coOrd pos; /* OUT */
- ANGLE_T angle;
- } traverse2;
+ BOOL_T segDir; /* IN Direction to go in this seg*/
+ int BezSegInx; /* IN for Bezier Seg which element to start with*/
+ BOOL_T segs_backwards; /* IN for Bezier Seg which way to go down the array*/
+ DIST_T dist; /* IN/OUT In = distance to go, Out = distance left */
+ coOrd pos; /* OUT = point reached if dist = 0 */
+ ANGLE_T angle; /* OUT = angle at point */
+ } traverse2; //Return distance left (or 0) and angle and pos when going dist from segDir end
struct {
int first, last; /* IN */
ANGLE_T side;
@@ -324,15 +351,15 @@ typedef union {
drawCmd_p d;
} drawRoadbedSide;
struct {
- coOrd pos1; /* IN/OUT */
- DIST_T dd; /* OUT */
+ coOrd pos1; /* IN pos to find */
+ DIST_T dd; /* OUT distance from nearest point in seg to input pos */
} distance;
struct {
track_p trk; /* OUT */
EPINX_T ep[2];
} newTrack;
struct {
- DIST_T length;
+ DIST_T length; /* OUT */
} length;
struct {
coOrd pos; /* IN */
@@ -340,9 +367,14 @@ typedef union {
trkSeg_t newSeg[2];
} split;
struct {
- coOrd pos; /* IN */
- ANGLE_T angle; /* OUT */
- } getAngle;
+ coOrd pos; /* IN Pos to find nearest to - OUT found pos on curve*/
+ ANGLE_T angle; /* OUT angle at pos - (-ve if backwards)*/
+ BOOL_T negative_radius; /* OUT Radius <0? */
+ BOOL_T backwards; /* OUT Seg is backwards */
+ DIST_T radius; /* OUT radius at pos */
+ coOrd center; /* OUT center of curvature at pos (0 = straight)*/
+ int bezSegInx; /* OUT if a bezier proc, the index of the sub segment */
+ } getAngle; // Get pos on seg nearest, angle at that (-ve for forwards)
} segProcData_t, *segProcData_p;
typedef enum {
SEGPROC_TRAVERSE1,
@@ -359,6 +391,8 @@ void SegProc( segProc_e, trkSeg_p, segProcData_p );
void StraightSegProc( segProc_e, trkSeg_p, segProcData_p );
void CurveSegProc( segProc_e, trkSeg_p, segProcData_p );
void JointSegProc( segProc_e, trkSeg_p, segProcData_p );
+void BezierSegProc( segProc_e, trkSeg_p, segProcData_p ); //Used in Cornu join
+void CleanSegs( dynArr_t *);
@@ -416,7 +450,7 @@ void SetTrkScale( track_p, SCALEINX_T );
BOOL_T GetTrkSelected( track_p );
BOOL_T GetTrkVisible( track_p );
void SetTrkVisible( track_p, BOOL_T );
-LAYER_T GetTrkLayer( track_p );
+unsigned int GetTrkLayer( track_p );
void SetBoundingBox( track_p, coOrd, coOrd );
void GetBoundingBox( track_p, coOrd*, coOrd* );
EPINX_T GetTrkEndPtCnt( track_p );
@@ -465,6 +499,7 @@ void SetTrkEndPtCnt( track_p, EPINX_T );
BOOL_T WriteEndPt( FILE *, track_cp, EPINX_T );
EPINX_T PickEndPoint( coOrd, track_cp );
EPINX_T PickUnconnectedEndPoint( coOrd, track_cp );
+EPINX_T PickUnconnectedEndPointSilent( coOrd, track_cp );
void AuditTracks( char *, ... );
void CheckTrackLength( track_cp );
@@ -501,9 +536,10 @@ void DrawStraightTies( drawCmd_p, track_p, coOrd, coOrd, wDrawColor );
void DrawStraightTrack( drawCmd_p, coOrd, coOrd, ANGLE_T, track_p, DIST_T, wDrawColor, long );
ANGLE_T GetAngleAtPoint( track_p, coOrd, EPINX_T *, EPINX_T * );
-DIST_T GetTrkDistance( track_cp, coOrd );
+DIST_T GetTrkDistance( track_cp, coOrd *);
track_p OnTrack( coOrd *, INT_T, BOOL_T );
-track_p OnTrack2( coOrd *, INT_T, BOOL_T, BOOL_T );
+track_p OnTrackIgnore(coOrd *, INT_T, BOOL_T , track_p );
+track_p OnTrack2( coOrd *, INT_T, BOOL_T, BOOL_T, track_p );
void ComputeRectBoundingBox( track_p, coOrd, coOrd );
void ComputeBoundingBox( track_p );
@@ -513,7 +549,7 @@ void DrawEndElev( drawCmd_p, track_p, EPINX_T, wDrawColor );
wDrawColor GetTrkColor( track_p, drawCmd_p );
void DrawTrack( track_cp, drawCmd_p, wDrawColor );
void DrawTracks( drawCmd_p, DIST_T, coOrd, coOrd );
-void RedrawLayer( LAYER_T, BOOL_T );
+void RedrawLayer( unsigned int, BOOL_T );
void DrawNewTrack( track_cp );
void DrawOneTrack( track_cp, drawCmd_p );
void UndrawNewTrack( track_cp );
@@ -547,6 +583,7 @@ void ConnectTracks( track_p, EPINX_T, track_p, EPINX_T );
BOOL_T ReconnectTrack( track_p, EPINX_T, track_p, EPINX_T );
void DisconnectTracks( track_p, EPINX_T, track_p, EPINX_T );
BOOL_T ConnectAbuttingTracks( track_p, EPINX_T, track_p, EPINX_T );
+BOOL_T ConnectTurntableTracks(track_p, EPINX_T, track_p, EPINX_T );
BOOL_T SplitTrack( track_p, coOrd, EPINX_T, track_p *leftover, BOOL_T );
BOOL_T TraverseTrack( traverseTrack_p, DIST_T * );
BOOL_T RemoveTrack( track_p*, EPINX_T*, DIST_T* );
@@ -560,6 +597,7 @@ BOOL_T QueryTrack( track_p, int );
void UngroupTrack( track_p );
BOOL_T IsTrack( track_p );
char * GetTrkTypeName( track_p );
+BOOL_T RebuildTrackSegs(track_p);
DIST_T GetFlexLength( track_p, EPINX_T, coOrd * );
void LabelLengths( drawCmd_p, track_p, wDrawColor );
@@ -576,8 +614,6 @@ void AdvancePositionIndicator( track_p, coOrd, coOrd *, ANGLE_T * );
BOOL_T MakeParallelTrack( track_p, coOrd, DIST_T, track_p *, coOrd *, coOrd * );
-#include "cundo.h"
-#include "cselect.h"
/* cmisc.c */
wIndex_t describeCmdInx;
@@ -600,7 +636,7 @@ typedef struct {
wControl_p control0;
wControl_p control1;
wPos_t posy;
- } descData_t, * descData_p;
+ } descData_t, * descData_p;
typedef void (*descUpdate_t)( track_p, int, descData_p, BOOL_T );
void DoDescribe( char *, track_p, descData_p, descUpdate_t );
void DescribeCancel( void );
@@ -632,6 +668,7 @@ void DrawTrackElev( track_p, drawCmd_p, BOOL_T );
/* cdraw.c */
track_p MakeDrawFromSeg( coOrd, ANGLE_T, trkSeg_p );
BOOL_T OnTableEdgeEndPt( track_p, coOrd * );
+BOOL_T GetClosestEndPt( track_p, coOrd * );
BOOL_T ReadTableEdge( char * );
BOOL_T ReadText( char * );
diff --git a/app/bin/trackx.h b/app/bin/trackx.h
index 6b46140..9f24e7c 100644
--- a/app/bin/trackx.h
+++ b/app/bin/trackx.h
@@ -1,5 +1,5 @@
-/*
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/trackx.h,v 1.1 2005-12-07 15:47:39 rc-flyer Exp $
+/** \file trackx.h
+ *
*/
/* XTrkCad - Model Railroad CAD
@@ -24,13 +24,16 @@
#ifndef TRACKX_H
#define TRACKX_H
+#include "common.h"
+#include "track.h"
+
struct extraData;
typedef struct track_t {
struct track_t *next;
TRKINX_T index;
TRKTYP_T type;
- LAYER_T layer;
+ unsigned int layer;
signed char scale;
BOOL_T modified:1;
BOOL_T deleted:1;
diff --git a/app/bin/trkseg.c b/app/bin/trkseg.c
index 972463f..9517a09 100644
--- a/app/bin/trkseg.c
+++ b/app/bin/trkseg.c
@@ -1,5 +1,5 @@
-/** \file trkseg.c
- * Modification and drawing of track segments
+/*
+ * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/trkseg.c,v 1.2 2006-05-30 16:11:55 m_fischer Exp $
*/
/* XTrkCad - Model Railroad CAD
@@ -20,18 +20,44 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
+#include <track.h>
#include <time.h>
#include <ctype.h>
+#include <math.h>
#include <stdarg.h>
#include "track.h"
+#include <common.h>
+#include <cbezier.h>
+#include <string.h>
+
+#include <tbezier.h>
+
#include "cjoin.h"
+#include "fileio.h"
+#include "param.h"
+#include "track.h"
+#include "utility.h"
+#include "misc.h"
+
/*****************************************************************************
*
* TRACK SEGMENTS
*
+ * Notes: Segments are used
+ * 1. as temporary elements during editing operations
+ * 2. as a means of grouping primitives for compounds
+ * 3. as the way of drawing and operating on Bezier curves
+ *
+ * They are stored as dynamic arrays which can be displayed and operated on as sets.
+ *
*/
+
+/*
+ * Build a Segment that has a radius and passes through two points. This uses the knowledge
+ * that the center of curve is always on an orthogonal line through the bisection of a chord.
+ */
EXPORT void ComputeCurvedSeg(
trkSeg_p s,
DIST_T radius,
@@ -66,7 +92,7 @@ EXPORT coOrd GetSegEndPt(
ANGLE_T * angleR )
{
coOrd pos;
- ANGLE_T angle, a, a0, a1;
+ ANGLE_T angle, a, a0, a1 = 0.0;
DIST_T r;
POS_T x0, y0, x1, y1;
@@ -114,7 +140,12 @@ EXPORT coOrd GetSegEndPt(
case SEG_JNTTRK:
pos = GetJointSegEndPos( segPtr->u.j.pos, segPtr->u.j.angle, segPtr->u.j.l0, segPtr->u.j.l1, segPtr->u.j.R, segPtr->u.j.L, segPtr->u.j.negate, segPtr->u.j.flip, segPtr->u.j.Scurve, ep, &angle );
break;
- default:
+ case SEG_BEZTRK:
+ case SEG_BEZLIN:
+ if (ep ==1) pos = segPtr->u.b.pos[3]; //For Bezier, use the End Points of the overall curve
+ else pos = segPtr->u.b.pos[0];
+ break;
+ default:
AbortProg("GetSegCntPt(%c)", segPtr->type );
}
if ( angleR )
@@ -123,7 +154,7 @@ EXPORT coOrd GetSegEndPt(
}
/**
- * Caclulate the bounding box for a string.
+ * Calculate the bounding box for a string.
*
* \param coOrd IN position of text
* \param angle IN text angle
@@ -131,9 +162,7 @@ EXPORT coOrd GetSegEndPt(
* \param fs IN size of font
* \param loR OUT bottom left corner
* \param hiR OUT top right corner
- * \return describe the return value
*/
-
EXPORT void GetTextBounds(
coOrd pos,
ANGLE_T angle,
@@ -142,29 +171,44 @@ EXPORT void GetTextBounds(
coOrd * loR,
coOrd * hiR )
{
+
coOrd size;
- POS_T descent;
+ POS_T descent = 0.0;
coOrd lo, hi;
coOrd p[4];
+ coOrd lastL;
int i;
- DrawTextSize2( &mainD, str, NULL, fs, FALSE, &size, &descent );
-
+ DrawMultiLineTextSize(&mainD, str, NULL, fs, FALSE, &size, &lastL);
// set up the corners of the rectangle
p[0].x = p[3].x = 0.0;
p[1].x = p[2].x = size.x;
- p[0].y = p[1].y = -descent;
+ DrawTextSize2(&mainD, "A", NULL, fs, FALSE, &size, &descent);
+ p[0].y = p[1].y = lastL.y - descent;
p[2].y = p[3].y = size.y;
lo = hi = zero;
// rotate each point
- for ( i=1; i<4; i++ ) {
- Rotate( &p[i], zero, angle );
- if ( p[i].x < lo.x ) lo.x = p[i].x;
- if ( p[i].y < lo.y ) lo.y = p[i].y;
- if ( p[i].x > hi.x ) hi.x = p[i].x;
- if ( p[i].y > hi.y ) hi.y = p[i].y;
+ for (i=0; i<4; i++)
+ {
+ Rotate(&p[i], zero, angle);
+
+ if (p[i].x < lo.x) {
+ lo.x = p[i].x;
+ }
+
+ if (p[i].y < lo.y) {
+ lo.y = p[i].y;
+ }
+
+ if (p[i].x > hi.x) {
+ hi.x = p[i].x;
+ }
+
+ if (p[i].y > hi.y) {
+ hi.y = p[i].y;
+ }
}
// now recaclulate the corners
@@ -178,7 +222,7 @@ EXPORT void GetTextBounds(
static void Get1SegBounds( trkSeg_p segPtr, coOrd xlat, ANGLE_T angle, coOrd *lo, coOrd *hi )
{
int inx;
- coOrd p0, p1, pc;
+ coOrd p0, p1, pBez[4], pc;
ANGLE_T a0, a1;
coOrd width;
DIST_T radius;
@@ -266,15 +310,31 @@ static void Get1SegBounds( trkSeg_p segPtr, coOrd xlat, ANGLE_T angle, coOrd *lo
break;
case SEG_FILCRCL:
REORIGIN( p0, segPtr->u.c.center, angle, xlat )
- lo->x = p0.x - segPtr->u.c.radius;
- hi->x = p0.x + segPtr->u.c.radius;
- lo->y = p0.y - segPtr->u.c.radius;
- hi->y = p0.y + segPtr->u.c.radius;
+ lo->x = p0.x - fabs(segPtr->u.c.radius);
+ hi->x = p0.x + fabs(segPtr->u.c.radius);
+ lo->y = p0.y - fabs(segPtr->u.c.radius);
+ hi->y = p0.y + fabs(segPtr->u.c.radius);
break;
case SEG_TEXT:
REORIGIN( p0, segPtr->u.t.pos, angle, xlat )
GetTextBounds( p0, angle+segPtr->u.t.angle, segPtr->u.t.string, segPtr->u.t.fontSize, lo, hi );
break;
+ case SEG_BEZLIN:
+ case SEG_BEZTRK: //Bezier control arms form a "tent" around the curve
+ REORIGIN( pBez[0], segPtr->u.b.pos[0], angle, xlat )
+ REORIGIN( pBez[1], segPtr->u.b.pos[1], angle, xlat )
+ REORIGIN( pBez[2], segPtr->u.b.pos[2], angle, xlat )
+ REORIGIN( pBez[3], segPtr->u.b.pos[3], angle, xlat )
+ lo->x = hi->x = pBez[0].x;
+ lo->y = hi->y = pBez[0].y;
+ for (int i=1;i<4;i++) {
+ lo->x = lo->x>pBez[i].x?pBez[i].x:lo->x;
+ lo->y = lo->y>pBez[i].y?pBez[i].y:lo->y;
+ hi->x = hi->x<pBez[i].x?pBez[i].x:hi->x;
+ hi->y = hi->y<pBez[i].y?pBez[i].y:hi->y;
+ }
+ width.x = width.y = segPtr->width/2.0;
+ break;
default:
;
}
@@ -379,6 +439,14 @@ EXPORT void MoveSegs(
s->u.j.pos.x += orig.x;
s->u.j.pos.y += orig.y;
break;
+ case SEG_BEZTRK:
+ case SEG_BEZLIN:
+ for (inx=0;inx<4;inx++) {
+ s->u.b.pos[inx].x +=orig.x;
+ s->u.b.pos[inx].y +=orig.y;
+ }
+ FixUpBezierSeg(s->u.b.pos,s,s->type == SEG_BEZTRK);
+ break;
}
}
}
@@ -423,11 +491,18 @@ EXPORT void RotateSegs(
Rotate( &s->u.j.pos, orig, angle );
s->u.j.angle = NormalizeAngle( s->u.j.angle+angle );
break;
- }
+ case SEG_BEZLIN:
+ case SEG_BEZTRK:
+ Rotate( &s->u.b.pos[0], orig, angle );
+ Rotate( &s->u.b.pos[1], orig, angle );
+ Rotate( &s->u.b.pos[2], orig, angle );
+ Rotate( &s->u.b.pos[3], orig, angle );
+ FixUpBezierSeg(s->u.b.pos,s,s->type == SEG_BEZTRK);
+ break;
+ }
}
}
-
EXPORT void FlipSegs(
wIndex_t segCnt,
trkSeg_p segs,
@@ -468,12 +543,21 @@ EXPORT void FlipSegs(
for (inx=0; inx<s->u.p.cnt; inx++) {
s->u.p.pts[inx].y = -s->u.p.pts[inx].y;
}
+ MyFree(pts);
break;
case SEG_JNTTRK:
s->u.j.pos.y = - s->u.j.pos.y;
s->u.j.angle = NormalizeAngle( 180.0 - s->u.j.angle );
s->u.j.negate = ! s->u.j.negate;
break;
+ case SEG_BEZTRK:
+ case SEG_BEZLIN:
+ s->u.b.pos[0].y = -s->u.b.pos[0].y;
+ s->u.b.pos[1].y = -s->u.b.pos[1].y;
+ s->u.b.pos[2].y = -s->u.b.pos[2].y;
+ s->u.b.pos[3].y = -s->u.b.pos[3].y;
+ FixUpBezierSeg(s->u.b.pos,s,s->type == SEG_BEZTRK);
+ break;
}
}
}
@@ -529,6 +613,20 @@ EXPORT void RescaleSegs(
s->u.j.l0 *= scale_w;
s->u.j.l1 *= scale_w;
break;
+ case SEG_BEZTRK:
+ case SEG_BEZLIN:
+ s->u.b.pos[0].y *= scale_y;
+ s->u.b.pos[0].x *= scale_x;
+ s->u.b.pos[1].x *= scale_x;
+ s->u.b.pos[1].y *= scale_y;
+ s->u.b.pos[2].y *= scale_y;
+ s->u.b.pos[2].x *= scale_x;
+ s->u.b.pos[3].x *= scale_x;
+ s->u.b.pos[3].y *= scale_y;
+ FixUpBezierSeg(s->u.b.pos,s,s->type == SEG_BEZTRK);
+
+ break;
+
}
}
}
@@ -556,10 +654,20 @@ EXPORT void CloneFilledDraw(
} else {
memcpy( newPts, sp->u.p.pts, sp->u.p.cnt * sizeof *(coOrd*)0 );
}
+ //if (sp->u.p.pts) Can't do this a pts could be pointing at static
+ // free(sp->u.p.pts);
sp->u.p.pts = newPts;
break;
case SEG_TEXT:
- sp->u.t.string = MyStrdup( sp->u.t.string );
+ sp->u.t.string = strdup( sp->u.t.string);
+ break;
+ case SEG_BEZTRK:
+ case SEG_BEZLIN:
+ sp->bezSegs.cnt = 0;
+ if (sp->bezSegs.ptr) MyFree(sp->bezSegs.ptr);
+ sp->bezSegs.ptr = NULL;
+ sp->bezSegs.max = 0;
+ FixUpBezierSeg(sp->u.b.pos,sp,sp->type == SEG_BEZTRK);
break;
default:
break;
@@ -583,8 +691,8 @@ EXPORT void FreeFilledDraw(
sp->u.p.pts = NULL;
break;
case SEG_TEXT:
- if ( sp->u.t.string )
- MyFree( sp->u.t.string );
+ if (sp->u.t.string)
+ MyFree(sp->u.t.string);
sp->u.t.string = NULL;
break;
default:
@@ -593,6 +701,13 @@ EXPORT void FreeFilledDraw(
}
}
+/*
+ * DistanceSegs
+ *
+ * Find the closest point on the Segs to the point pos.
+ * Return the distance to the point, the point on the curve and the index of the segment that contains it.
+ *
+ */
EXPORT DIST_T DistanceSegs(
coOrd orig,
@@ -606,6 +721,7 @@ EXPORT DIST_T DistanceSegs(
coOrd p0, p1, p2, pt, lo, hi;
BOOL_T found = FALSE;
wIndex_t inx, lin;
+ segProcData_t segProcData2;
p0 = *pos;
Rotate( &p0, orig, -angle );
p0.x -= orig.x;
@@ -645,27 +761,41 @@ EXPORT DIST_T DistanceSegs(
}
}
break;
+ case SEG_BEZTRK:
+ case SEG_BEZLIN:
+ dd = 100000.0;
+ pt = p0;
+ for (int i = 0;i<segPtr->bezSegs.cnt;i++) {
+ segProcData2.distance.pos1 = pt;
+ SegProc(SEGPROC_DISTANCE,&DYNARR_N(trkSeg_t,segPtr->bezSegs,i),&segProcData2);
+ if (segProcData2.distance.dd<dd) {
+ dd = segProcData2.distance.dd;
+ p1 = segProcData2.distance.pos1;
+ }
+ }
+ break;
case SEG_TEXT:
/*GetTextBounds( segPtr->u.t.pos, angle+segPtr->u.t.angle, segPtr->u.t.string, segPtr->u.t.fontSize, &lo, &hi );*/
- GetTextBounds( zero, 0, segPtr->u.t.string, segPtr->u.t.fontSize, &lo, &hi );
- Rotate( &p0, segPtr->u.t.pos, segPtr->u.t.angle );
+ GetTextBounds( zero, 0, segPtr->u.t.string, segPtr->u.t.fontSize, &lo, &hi ); //lo and hi are relative to seg origin
p0.x -= segPtr->u.t.pos.x;
p0.y -= segPtr->u.t.pos.y;
- if ( p0.x < hi.x && p0.y < hi.y ) {
- DIST_T dx, dy;
+ Rotate( &p0, zero, -segPtr->u.t.angle );
+ if (p0.x > lo.x && p0.x < hi.x && p0.y >lo.y && p0.y < hi.y) { //Within rectangle - therefore small dist
hi.x /= 2.0;
hi.y /= 2.0;
- p0.x -= hi.x;
- p0.y -= hi.y;
- dx = fabs(p0.x/hi.x);
- dy = fabs(p0.y/hi.y);
- if ( dx > dy )
- dd = dx;
- else
- dd = dy;
- dd *= 0.25*mainD.scale;
- /*printf( "dx=%0.4f dy=%0.4f dd=%0.3f\n", dx, dy, dd );*/
+ dd = 0.1*FindDistance(hi, p0)/FindDistance(lo,hi); // Proportion to mean that the closer we to the center or the smaller the target in overlapping cases, the more likely we pick it
+ break;
}
+ hi.x /= 2.0; // rough center of rectangle
+ hi.y /= 2.0;
+ if (fabs((p0.x-hi.x)/hi.x)<fabs((p0.y-hi.y)/hi.y)) { // Proportionally closer to x
+ if (p0.x > hi.x) dd = (p0.x - hi.x);
+ else dd = fabs(p0.x-hi.x);
+ } else { // Closer to y
+ if (p0.y > hi.y) dd = (p0.y - hi.y);
+ else dd = fabs(p0.y-hi.y);
+ }
+ /*printf( "dx=%0.4f dy=%0.4f dd=%0.3f\n", dx, dy, dd );*/
/*
if ( p0.x >= lo.x && p0.x <= hi.x &&
p0.y >= lo.y && p0.y <= hi.y ) {
@@ -698,22 +828,35 @@ EXPORT DIST_T DistanceSegs(
return d;
}
-
+/*
+ * Get the angle at a point on the segments closest to pos1
+ * Optionally return the index of the segment and the distance to that point
+ *
+ */
EXPORT ANGLE_T GetAngleSegs(
wIndex_t segCnt,
trkSeg_p segPtr,
- coOrd pos,
- wIndex_t * segInxR )
+ coOrd * pos1, // Now IN/OUT OUT =
+ wIndex_t * segInxR,
+ DIST_T * dist,
+ BOOL_T * seg_backwards,
+ wIndex_t * subSegInxR,
+ BOOL_T * negative_radius)
{
wIndex_t inx;
ANGLE_T angle = 0.0;
coOrd p0;
DIST_T d, dd;
segProcData_t segProcData;
+ coOrd pos2 = * pos1;
+ BOOL_T negative = FALSE;
+ BOOL_T backwards = FALSE;
+ if (subSegInxR) *subSegInxR = -1;
- DistanceSegs( zero, 0.0, segCnt, segPtr, &pos, &inx );
+ d = DistanceSegs( zero, 0.0, segCnt, segPtr, &pos2, &inx ); //
+ if (dist) * dist = d;
segPtr += inx;
- segProcData.getAngle.pos = pos;
+ segProcData.getAngle.pos = pos2;
switch ( segPtr->type ) {
case SEG_STRTRK:
case SEG_STRLIN:
@@ -728,18 +871,28 @@ EXPORT ANGLE_T GetAngleSegs(
case SEG_FILCRCL:
CurveSegProc( SEGPROC_GETANGLE, segPtr, &segProcData );
angle = segProcData.getAngle.angle;
+ negative = segProcData.getAngle.negative_radius;
+ backwards = segProcData.getAngle.backwards;
break;
case SEG_JNTTRK:
JointSegProc( SEGPROC_GETANGLE, segPtr, &segProcData );
angle = segProcData.getAngle.angle;
break;
+ case SEG_BEZTRK:
+ case SEG_BEZLIN:
+ BezierSegProc( SEGPROC_GETANGLE, segPtr, &segProcData );
+ angle = segProcData.getAngle.angle;
+ negative = segProcData.getAngle.negative_radius;
+ backwards = segProcData.getAngle.backwards;
+ if (subSegInxR) *subSegInxR = segProcData.getAngle.bezSegInx;
+ break;
case SEG_POLY:
case SEG_FILPOLY:
- p0 = pos;
+ p0 = pos2;
dd = LineDistance( &p0, segPtr->u.p.pts[segPtr->u.p.cnt-1], segPtr->u.p.pts[0] );
angle = FindAngle( segPtr->u.p.pts[segPtr->u.p.cnt-1], segPtr->u.p.pts[0] );
for ( inx=0; inx<segPtr->u.p.cnt-1; inx++ ) {
- p0 = pos;
+ p0 = pos2;
d = LineDistance( &p0, segPtr->u.p.pts[inx], segPtr->u.p.pts[inx+1] );
if ( d < dd ) {
dd = d;
@@ -754,6 +907,10 @@ EXPORT ANGLE_T GetAngleSegs(
AbortProg( "GetAngleSegs(%d)", segPtr->type );
}
if ( segInxR ) *segInxR = inx;
+ if (seg_backwards) *seg_backwards = backwards;
+ if (negative_radius) *negative_radius = negative;
+
+ * pos1 = pos2;
return angle;
}
@@ -970,12 +1127,17 @@ EXPORT BOOL_T ReadSegs( void )
BOOL_T rc=FALSE;
trkSeg_p s;
trkEndPt_p e;
- unsigned long rgb;
+ long rgb;
int i;
DIST_T elev0, elev1;
BOOL_T hasElev;
+ BOOL_T isPolyV2;
+ BOOL_T improvedEnds;
+ FLOAT_T ignoreFloat;
char type;
- long option;
+ char * plain_text;
+ long option, option2;
+ BOOL_T subsegs = FALSE;
descriptionOff = zero;
tempSpecial[0] = '\0';
@@ -984,12 +1146,22 @@ EXPORT BOOL_T ReadSegs( void )
DYNARR_RESET( trkEndPt_t, tempEndPts_da );
pathCnt = 0;
while ( (cp = GetNextLine()) != NULL ) {
- while (isspace((unsigned char)*cp)) cp++;
+ while (isspace(*cp)) cp++;
hasElev = FALSE;
+ improvedEnds = FALSE;
if ( strncmp( cp, "END", 3 ) == 0 ) {
rc = TRUE;
+ subsegs = FALSE;
break;
}
+ if ( strncmp(cp, "SUBSEGS", 7) == 0) {
+ subsegs = TRUE;
+ continue;
+ }
+ if (strncmp (cp, "SUBSEND", 7) == 0) {
+ subsegs = FALSE;
+ continue;
+ }
if ( *cp == '\n' || *cp == '#' ) {
continue;
}
@@ -999,13 +1171,20 @@ EXPORT BOOL_T ReadSegs( void )
cp++;
hasElev = TRUE;
}
+ isPolyV2 = FALSE;
+ if (*cp == '4') {
+ cp++;
+ hasElev = TRUE;
+ isPolyV2 = TRUE;
+ improvedEnds = TRUE;
+ }
switch (type) {
case SEG_STRLIN:
case SEG_TBLEDGE:
DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 );
s = &tempSegs(tempSegs_da.cnt-1);
s->type = type;
- if ( !GetArgs( cp, hasElev?"uwpfpf":"uwpYpY",
+ if ( !GetArgs( cp, hasElev?"lwpfpf":"lwpYpY",
&rgb, &s->width, &s->u.l.pos[0], &elev0, &s->u.l.pos[1], &elev1 ) ) {
rc = FALSE;
break;
@@ -1018,7 +1197,7 @@ EXPORT BOOL_T ReadSegs( void )
DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 );
s = &tempSegs(tempSegs_da.cnt-1);
s->type = type;
- if ( !GetArgs( cp, hasElev?"uwpfpfl":"uwpYpYZ",
+ if ( !GetArgs( cp, hasElev?"lwpfpfl":"lwpYpYZ",
&rgb, &s->width, &s->u.l.pos[0], &elev0, &s->u.l.pos[1], &elev1, &option ) ) {
rc = FALSE;
break;
@@ -1033,7 +1212,7 @@ EXPORT BOOL_T ReadSegs( void )
DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 );
s = &tempSegs(tempSegs_da.cnt-1);
s->type = SEG_CRVLIN;
- if ( !GetArgs( cp, hasElev?"uwfpfff":"uwfpYff",
+ if ( !GetArgs( cp, hasElev?"lwfpfff":"lwfpYff",
&rgb, &s->width,
&s->u.c.radius,
&s->u.c.center,
@@ -1048,20 +1227,27 @@ EXPORT BOOL_T ReadSegs( void )
DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 );
s = &tempSegs(tempSegs_da.cnt-1);
s->type = SEG_STRTRK;
- if ( !GetArgs( cp, hasElev?"uwpfpf":"uwpYpY",
+ s->bezSegs.max = 0;
+ s->bezSegs.cnt = 0;
+ s->bezSegs.ptr = NULL;
+ if ( !GetArgs( cp, hasElev?"lwpfpf":"lwpYpY",
&rgb, &s->width,
&s->u.l.pos[0], &elev0,
&s->u.l.pos[1], &elev1 ) ) {
rc = FALSE;
break;
}
+ rgb = 0;
s->color = wDrawFindColor( rgb );
break;
case SEG_CRVTRK:
DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 );
s = &tempSegs(tempSegs_da.cnt-1);
s->type = SEG_CRVTRK;
- if ( !GetArgs( cp, hasElev?"uwfpfff":"uwfpYff",
+ s->bezSegs.max = 0;
+ s->bezSegs.cnt = 0;
+ s->bezSegs.ptr = NULL;
+ if ( !GetArgs( cp, hasElev?"lwfpfff":"lwfpYff",
&rgb, &s->width,
&s->u.c.radius,
&s->u.c.center,
@@ -1070,13 +1256,14 @@ EXPORT BOOL_T ReadSegs( void )
rc = FALSE;
break;
}
+ rgb = 0;
s->color = wDrawFindColor( rgb );
break;
case SEG_JNTTRK:
DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 );
s = &tempSegs(tempSegs_da.cnt-1);
s->type = SEG_JNTTRK;
- if ( !GetArgs( cp, hasElev?"uwpffffffl":"uwpYfffffl",
+ if ( !GetArgs( cp, hasElev?"lwpffffffl":"lwpYfffffl",
&rgb, &s->width,
&s->u.j.pos,
&elev0,
@@ -1092,13 +1279,51 @@ EXPORT BOOL_T ReadSegs( void )
s->u.j.negate = ( option&1 )!=0;
s->u.j.flip = ( option&2 )!=0;
s->u.j.Scurve = ( option&4 )!=0;
+ rgb = 0;
s->color = wDrawFindColor( rgb );
break;
+ case SEG_BEZTRK:
+ DYNARR_APPEND( trkSeg_t, tempSegs_da, 10);
+ s = &tempSegs(tempSegs_da.cnt-1);
+ s->type=SEG_BEZTRK;
+ s->bezSegs.max=0;
+ s->bezSegs.ptr= NULL;
+ s->bezSegs.cnt=0;
+ if ( !GetArgs( cp, "lwpppp",
+ &rgb, &s->width,
+ &s->u.b.pos[0],
+ &s->u.b.pos[1],
+ &s->u.b.pos[2],
+ &s->u.b.pos[3])) {
+ rc = FALSE;
+ break;
+ }
+ rgb = 0;
+ s->color = wDrawFindColor( rgb );
+ break;
+ case SEG_BEZLIN:
+ DYNARR_APPEND( trkSeg_t, tempSegs_da, 10);
+ s = &tempSegs(tempSegs_da.cnt-1);
+ s->type=SEG_BEZLIN;
+ s->bezSegs.max=0;
+ s->bezSegs.ptr= NULL;
+ s->bezSegs.cnt=0;
+ if ( !GetArgs( cp, "lwpppp",
+ &rgb, &s->width,
+ &s->u.b.pos[0],
+ &s->u.b.pos[1],
+ &s->u.b.pos[2],
+ &s->u.b.pos[3])) {
+ rc = FALSE;
+ break;
+ }
+ s->color = wDrawFindColor( rgb );
+ break;
case SEG_FILCRCL:
DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 );
s = &tempSegs(tempSegs_da.cnt-1);
s->type = SEG_FILCRCL;
- if ( !GetArgs( cp, hasElev?"uwfpf":"uwfpY",
+ if ( !GetArgs( cp, hasElev?"lwfpf":"lwfpY",
&rgb, &s->width,
&s->u.c.radius,
&s->u.c.center,
@@ -1115,11 +1340,20 @@ EXPORT BOOL_T ReadSegs( void )
DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 );
s = &tempSegs(tempSegs_da.cnt-1);
s->type = type;
- if ( !GetArgs( cp, "uwd",
- &rgb, &s->width,
- &s->u.p.cnt ) ) {
- rc = FALSE;
- /*??*/break;
+ if (isPolyV2) {
+ if ( !GetArgs( cp, "lwdd",
+ &rgb, &s->width,
+ &s->u.p.cnt, &s->u.p.polyType) ) {
+ rc = FALSE;
+ /*??*/break;
+ }
+ } else {
+ if ( !GetArgs( cp, "lwd",
+ &rgb, &s->width,
+ &s->u.p.cnt) ) {
+ rc = FALSE;
+ /*??*/break;
+ }
}
s->color = wDrawFindColor( rgb );
s->u.p.pts = (coOrd*)MyMalloc( s->u.p.cnt * sizeof *(coOrd*)NULL );
@@ -1138,10 +1372,14 @@ EXPORT BOOL_T ReadSegs( void )
s = &tempSegs(tempSegs_da.cnt-1);
s->type = type;
s->u.t.fontP = NULL;
- if ( !GetArgs( cp, "upf0fq", &rgb, &s->u.t.pos, &s->u.t.angle, &s->u.t.fontSize, &s->u.t.string ) ) {
+ char * expandedText;
+ if ( !GetArgs( cp, "lpf0fq", &rgb, &s->u.t.pos, &s->u.t.angle, &s->u.t.fontSize, &expandedText ) ) {
rc = FALSE;
/*??*/break;
}
+ plain_text = ConvertFromEscapedText(expandedText);
+ s->u.t.string = plain_text;
+ MyFree(expandedText);
s->color = wDrawFindColor( rgb );
break;
case SEG_UNCEP:
@@ -1150,8 +1388,8 @@ EXPORT BOOL_T ReadSegs( void )
e = &tempEndPts(tempEndPts_da.cnt-1);
if (type == SEG_CONEP) {
if ( !GetArgs( cp, "dc", &e->index, &cp ) ) {
- rc = FALSE;
- /*??*/break;
+ rc = FALSE;
+ /*??*/break;
}
} else {
e->index = -1;
@@ -1165,6 +1403,27 @@ EXPORT BOOL_T ReadSegs( void )
e->elev.u.height = 0.0;
e->elev.doff = zero;
e->option = 0;
+ if (improvedEnds) { //E4 and T4
+ if (!GetArgs( cp, "lpc", &option, &e->elev.doff, &cp )) {
+ rc = FALSE;
+ /*??*/break;
+ }
+ switch (option&ELEV_MASK) {
+ case ELEV_STATION:
+ GetArgs( cp, "qc", &e->elev.u.name, &cp);
+ break;
+ default:
+ GetArgs( cp, "fc", &e->elev.u.height, &cp); //First height
+ }
+ DIST_T height2;
+ if (!GetArgs( cp, "flLlc", &height2, &option2, &e->elev.option, &e->option, &cp ) ) {
+ rc = FALSE;
+ break;
+ }
+ if (option2) e->elev.option |= ELEV_VISIBLE;
+ GetArgs(cp, "fc", &ignoreFloat, &cp);
+ break;
+ }
if ( cp != NULL ) {
if (paramVersion < 7) {
GetArgs( cp, "dfp", &e->elev.option, &e->elev.u.height, &e->elev.doff, &cp );
@@ -1188,7 +1447,7 @@ EXPORT BOOL_T ReadSegs( void )
}
break;
case SEG_PATH:
- while (isspace((unsigned char)*cp)) cp++;
+ while (isspace(*cp)) cp++;
if (*cp == '\"') cp++;
while ( *cp != '\"') AppendPath((signed char)*cp++);
AppendPath(0);
@@ -1220,55 +1479,34 @@ EXPORT BOOL_T ReadSegs( void )
}
}
AppendPath( 0 );
-
-#ifdef LATER
- if ( logTable(log_readTracks).level >= 4 ) {
- for (s=&tempSegs(0); s<&tempSegs(tempSegs_da.cnt); s++) {
- switch (s->type) {
- case SEG_STRTRK:
- case SEG_STRLIN:
- case SEG_DIMLIN:
- case SEG_BENCH:
- case SEG_TBLEDGE:
- LogPrintf( "seg[%d] = %c [%0.3f %0.3f] [%0.3f %0.3f]\n",
- tempSegs_da.cnt, s->type,
- s->u.l.pos[0].x, s->u.l.pos[0].y,
- s->u.l.pos[1].x, s->u.l.pos[1].y );
- break;
- case SEG_CRVTRK:
- case SEG_CRVLIN:
- LogPrintf( "seg[%d] = %c R=%0.3f A0=%0.3f A1=%0.3f [%0.3f %0.3f]\n",
- tempSegs_da.cnt, s->type,
- s->u.c.radius,
- s->u.c.center.x, s->u.c.center.y,
- s->u.c.a0, s->u.c.a1 );
- break;
- case SEG_JNTTRK:
- LogPrintf( "seg[%d] = %c\n",
- tempSegs_da.cnt, s->type );
- break;
- }
- }
- }
-#endif
return rc;
}
-
EXPORT BOOL_T WriteSegs(
FILE * f,
wIndex_t segCnt,
trkSeg_p segs )
{
+ return WriteSegsEnd(f,segCnt,segs,TRUE);
+}
+
+
+EXPORT BOOL_T WriteSegsEnd(
+ FILE * f,
+ wIndex_t segCnt,
+ trkSeg_p segs, BOOL_T writeEnd)
+
+{
int i, j;
BOOL_T rc = TRUE;
long option;
+ char * escaped_text;
for ( i=0; i<segCnt; i++ ) {
switch ( segs[i].type ) {
case SEG_STRTRK:
- rc &= fprintf( f, "\t%c %ld %0.6f %0.6f %0.6f %0.6f %0.6f\n",
- segs[i].type, wDrawGetRGB(segs[i].color), segs[i].width,
+ rc &= fprintf( f, "\t%c 0 %0.6f %0.6f %0.6f %0.6f %0.6f\n",
+ segs[i].type, segs[i].width,
segs[i].u.l.pos[0].x, segs[i].u.l.pos[0].y,
segs[i].u.l.pos[1].x, segs[i].u.l.pos[1].y ) > 0;
break;
@@ -1294,16 +1532,16 @@ EXPORT BOOL_T WriteSegs(
BenchOutputOption(segs[i].u.l.option) ) > 0;
break;
case SEG_CRVTRK:
- rc &= fprintf( f, "\t%c %ld %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f\n",
- segs[i].type, wDrawGetRGB(segs[i].color), segs[i].width,
+ rc &= fprintf( f, "\t%c 0 %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f\n",
+ segs[i].type, segs[i].width,
segs[i].u.c.radius,
segs[i].u.c.center.x, segs[i].u.c.center.y,
segs[i].u.c.a0, segs[i].u.c.a1 ) > 0;
break;
case SEG_JNTTRK:
option = (segs[i].u.j.negate?1:0) + (segs[i].u.j.flip?2:0) + (segs[i].u.j.Scurve?4:0);
- rc &= fprintf( f, "\t%c %ld %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %ld\n",
- segs[i].type, wDrawGetRGB(segs[i].color), segs[i].width,
+ rc &= fprintf( f, "\t%c 0 %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %ld\n",
+ segs[i].type, segs[i].width,
segs[i].u.j.pos.x, segs[i].u.j.pos.y,
segs[i].u.j.angle,
segs[i].u.j.l0,
@@ -1312,12 +1550,25 @@ EXPORT BOOL_T WriteSegs(
segs[i].u.j.L,
option )>0;
break;
+ case SEG_BEZTRK:
+ case SEG_BEZLIN:
+ rc &= fprintf( f, "\t%c3 0 %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f\n",
+ segs[i].type, segs[i].width,
+ segs[i].u.l.pos[0].x, segs[i].u.l.pos[0].y,
+ segs[i].u.l.pos[1].x, segs[i].u.l.pos[1].y,
+ segs[i].u.l.pos[2].x, segs[i].u.l.pos[2].y,
+ segs[i].u.l.pos[3].x, segs[i].u.l.pos[3].y ) > 0;
+ rc &= fprintf(f,"\tSUBSEGS\n");
+ rc &= WriteSegsEnd(f,segs[i].bezSegs.cnt,segs[i].bezSegs.ptr,FALSE);
+ rc &= fprintf(f,"\tSUBSEND\n");
+ break;
case SEG_CRVLIN:
rc &= fprintf( f, "\t%c3 %ld %0.6f %0.6f %0.6f %0.6f 0 %0.6f %0.6f\n",
segs[i].type, wDrawGetRGB(segs[i].color), segs[i].width,
segs[i].u.c.radius,
segs[i].u.c.center.x, segs[i].u.c.center.y,
segs[i].u.c.a0, segs[i].u.c.a1 ) > 0;
+
break;
case SEG_FILCRCL:
rc &= fprintf( f, "\t%c3 %ld %0.6f %0.6f %0.6f %0.6f 0\n",
@@ -1327,22 +1578,24 @@ EXPORT BOOL_T WriteSegs(
break;
case SEG_POLY:
case SEG_FILPOLY:
- rc &= fprintf( f, "\t%c3 %ld %0.6f %d\n",
+ rc &= fprintf( f, "\t%c4 %ld %0.6f %d %d \n",
segs[i].type, wDrawGetRGB(segs[i].color), segs[i].width,
- segs[i].u.p.cnt ) > 0;
+ segs[i].u.p.cnt, segs[i].u.p.polyType ) > 0;
for ( j=0; j<segs[i].u.p.cnt; j++ )
rc &= fprintf( f, "\t\t%0.6f %0.6f 0\n",
segs[i].u.p.pts[j].x, segs[i].u.p.pts[j].y ) > 0;
break;
case SEG_TEXT: /* 0pf0fq */
+ escaped_text = ConvertToEscapedText(segs[i].u.t.string);
rc &= fprintf( f, "\t%c %ld %0.6f %0.6f %0.6f 0 %0.6f \"%s\"\n",
segs[i].type, wDrawGetRGB(segs[i].color),
segs[i].u.t.pos.x, segs[i].u.t.pos.y, segs[i].u.t.angle,
- segs[i].u.t.fontSize, PutTitle(segs[i].u.t.string) ) > 0;
+ segs[i].u.t.fontSize, escaped_text ) > 0;
+ MyFree(escaped_text);
break;
}
}
- rc &= fprintf( f, "\tEND\n" )>0;
+ if (writeEnd) rc &= fprintf( f, "\tEND\n" )>0;
return rc;
}
@@ -1354,20 +1607,27 @@ EXPORT void SegProc(
{
switch (segPtr->type) {
case SEG_STRTRK:
+ case SEG_STRLIN:
StraightSegProc( cmd, segPtr, data );
break;
case SEG_CRVTRK:
+ case SEG_CRVLIN:
CurveSegProc( cmd, segPtr, data );
break;
case SEG_JNTTRK:
JointSegProc( cmd, segPtr, data );
break;
- default:
+ case SEG_BEZTRK:
+ case SEG_BEZLIN:
+ BezierSegProc( cmd, segPtr, data);
+ break;
+ default:
AbortProg( "SegProg( %d )", segPtr->type );
break;
}
}
+
/*
* Draw Segs
@@ -1456,6 +1716,10 @@ EXPORT void DrawDimLine(
DrawLine( d, p, p1, 0, color );
}
+/*
+ * Display the array of segments.
+ * Note that Bezier segments in particular contain sub-arrays of Curve and Straight segments.
+ */
EXPORT void DrawSegsO(
drawCmd_p d,
track_p trk,
@@ -1468,13 +1732,12 @@ EXPORT void DrawSegsO(
long options )
{
wIndex_t i, j;
- coOrd p0, p1, c;
+ coOrd p0, p1, p2, p3, c;
ANGLE_T a0;
wDrawColor color1, color2;
DIST_T factor = d->dpi/d->scale;
trkSeg_p tempPtr;
- static dynArr_t tempPts_da;
-#define tempPts(N) DYNARR_N( coOrd, tempPts_da, N )
+
long option;
wFontSize_t fs;
@@ -1503,13 +1766,33 @@ EXPORT void DrawSegsO(
REORIGIN( p0, segPtr->u.j.pos, angle, orig );
DrawJointTrack( d, p0, NormalizeAngle(segPtr->u.j.angle+angle), segPtr->u.j.l0, segPtr->u.j.l1, segPtr->u.j.R, segPtr->u.j.L, segPtr->u.j.negate, segPtr->u.j.flip, segPtr->u.j.Scurve, trk, -1, -1, trackGauge, color1, options );
break;
- }
+ case SEG_BEZTRK:
+ REORIGIN(p0, segPtr->u.b.pos[0], angle, orig);
+ REORIGIN(p1, segPtr->u.b.pos[1], angle, orig);
+ REORIGIN(p2, segPtr->u.b.pos[2], angle, orig);
+ REORIGIN(p3, segPtr->u.b.pos[3], angle, orig);
+ tempPtr = segPtr->bezSegs.ptr;
+ for(int j=0;j<segPtr->bezSegs.cnt;j++,tempPtr++) { //Loop through sub parts (only Trks supported)
+ if (tempPtr->type == SEG_CRVTRK) {
+ a0 = NormalizeAngle(tempPtr->u.c.a0 + angle);
+ REORIGIN( c, tempPtr->u.c.center, angle, orig );
+ DrawCurvedTies( d, trk, c, fabs(tempPtr->u.c.radius), a0, tempPtr->u.c.a1, color );
+ }
+ if (tempPtr->type == SEG_STRTRK) {
+ REORIGIN( p0, tempPtr->u.l.pos[0], angle, orig )
+ REORIGIN( p1, tempPtr->u.l.pos[1], angle, orig )
+ DrawStraightTies( d, trk, p0, p1, color );
+ }
+ }
+ break;
+ }
continue;
}
switch (segPtr->type) {
case SEG_STRTRK:
case SEG_CRVTRK:
case SEG_JNTTRK:
+ case SEG_BEZTRK:
case SEG_TEXT:
break;
default:
@@ -1593,24 +1876,79 @@ EXPORT void DrawSegsO(
FALSE, (wDrawWidth)floor(segPtr->width*factor+0.5), color1 );
}
break;
+ case SEG_BEZTRK:
+ case SEG_BEZLIN:
+ if (segPtr->type == SEG_BEZTRK) {
+ if (color1 == wDrawColorBlack)
+ color1 = normalColor;
+ if ( segPtr->color == wDrawColorWhite )
+ break;
+ }
+ REORIGIN(p0, segPtr->u.b.pos[0], angle, orig);
+ REORIGIN(p1, segPtr->u.b.pos[1], angle, orig);
+ REORIGIN(p2, segPtr->u.b.pos[2], angle, orig);
+ REORIGIN(p3, segPtr->u.b.pos[3], angle, orig);
+
+ for(int j=0;j<segPtr->bezSegs.cnt;j++) { //Loop through sub Segs
+ tempPtr = &DYNARR_N(trkSeg_t,segPtr->bezSegs,j);
+ switch (tempPtr->type) {
+ case SEG_CRVTRK:
+ case SEG_CRVLIN:
+ a0 = NormalizeAngle(tempPtr->u.c.a0 + angle);
+ REORIGIN( c, tempPtr->u.c.center, angle, orig );
+ if (tempPtr->type == SEG_CRVTRK) {
+ if (color1 == wDrawColorBlack) color1 = normalColor;
+ if ( tempPtr->color == wDrawColorWhite ) break;
+ p0.x = p0.y = p1.x = p1.y = 0;
+ DrawCurvedTrack( d,
+ c,
+ fabs(tempPtr->u.c.radius),
+ a0, tempPtr->u.c.a1,
+ p0, p1,
+ NULL, trackGauge, color1, options );
+ } else if (tempPtr->type == SEG_CRVLIN) {
+ DrawArc( d, c, fabs(tempPtr->u.c.radius), a0, tempPtr->u.c.a1,
+ FALSE, (wDrawWidth)floor(tempPtr->width*factor+0.5), color1 );
+ }
+ break;
+ case SEG_STRTRK:
+ if (color1 == wDrawColorBlack) color1 = normalColor;
+ if ( tempPtr->color == wDrawColorWhite ) break;
+ REORIGIN(p0,tempPtr->u.l.pos[0], angle, orig);
+ REORIGIN(p1,tempPtr->u.l.pos[1], angle, orig);
+ DrawStraightTrack( d,
+ p0, p1,
+ FindAngle(p0, p1 ),
+ NULL, trackGauge, color1, options );
+ break;
+ case SEG_STRLIN:
+ REORIGIN(p0,tempPtr->u.l.pos[0], angle, orig);
+ REORIGIN(p1,tempPtr->u.l.pos[1], angle, orig);
+ DrawLine( d, p0, p1, (wDrawWidth)floor(tempPtr->width*factor+0.5), color1 );
+ break;
+ }
+ }
+ break;
case SEG_JNTTRK:
REORIGIN( p0, segPtr->u.j.pos, angle, orig );
DrawJointTrack( d, p0, NormalizeAngle(segPtr->u.j.angle+angle), segPtr->u.j.l0, segPtr->u.j.l1, segPtr->u.j.R, segPtr->u.j.L, segPtr->u.j.negate, segPtr->u.j.flip, segPtr->u.j.Scurve, NULL, -1, -1, trackGauge, color1, options );
break;
case SEG_TEXT:
REORIGIN( p0, segPtr->u.t.pos, angle, orig )
- DrawString( d, p0, NormalizeAngle(angle+segPtr->u.t.angle), segPtr->u.t.string, segPtr->u.t.fontP, segPtr->u.t.fontSize, color1 );
+ DrawMultiString( d, p0, segPtr->u.t.string, segPtr->u.t.fontP, segPtr->u.t.fontSize, color1, NormalizeAngle(angle + segPtr->u.t.angle), NULL, NULL );
break;
case SEG_FILPOLY:
if ( (d->options&DC_GROUP) == 0 &&
d->funcs != &tempSegDrawFuncs ) {
/* Note: if we call tempSegDrawFillPoly we get a nasty bug
/+ because we don't make a private copy of p.pts */
- DYNARR_SET( coOrd, tempPts_da, segPtr->u.p.cnt );
- for ( j=0; j<segPtr->u.p.cnt; j++ ) {
- REORIGIN( tempPts(j), segPtr->u.p.pts[j], angle, orig )
+ coOrd *tempPts = malloc(sizeof(coOrd)*segPtr->u.p.cnt);
+// coOrd tempPts[segPtr->u.p.cnt];
+ for (j=0;j<segPtr->u.p.cnt;j++) {
+ REORIGIN( tempPts[j], segPtr->u.p.pts[j], angle, orig );
}
- DrawFillPoly( d, segPtr->u.p.cnt, &tempPts(0), color1 );
+ DrawFillPoly( d, segPtr->u.p.cnt, tempPts, color1 );
+ free(tempPts);
break;
} /* else fall thru */
case SEG_POLY:
@@ -1646,6 +1984,9 @@ EXPORT void DrawSegsO(
}
+/*
+ * Draw Segments without setting DTS_ options.
+ */
EXPORT void DrawSegs(
drawCmd_p d,
@@ -1659,4 +2000,24 @@ EXPORT void DrawSegs(
DrawSegsO( d, NULL, orig, angle, segPtr, segCnt, trackGauge, color, 0 );
}
+/*
+ * Free dynamic storage added to each of an array of Track Segments.
+ */
+EXPORT void CleanSegs(dynArr_t * seg_p) {
+ if (seg_p->cnt ==0) return;
+ for (int i=0;i<seg_p->cnt;i++) {
+ trkSeg_t t = DYNARR_N(trkSeg_t,* seg_p,i);
+ if (t.type == SEG_BEZLIN || t.type == SEG_BEZTRK) {
+ if (t.bezSegs.ptr) MyFree(t.bezSegs.ptr);
+ t.bezSegs.cnt = 0;
+ t.bezSegs.max = 0;
+ t.bezSegs.ptr = NULL;
+ }
+ }
+ seg_p->cnt = 0;
+ if (seg_p->ptr) MyFree(seg_p->ptr);
+ seg_p->ptr = NULL;
+ seg_p->max = 0;
+}
+
diff --git a/app/bin/tstraigh.c b/app/bin/tstraigh.c
index 0f5f273..5cf1cda 100644
--- a/app/bin/tstraigh.c
+++ b/app/bin/tstraigh.c
@@ -1,5 +1,5 @@
-/*
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/tstraigh.c,v 1.2 2008-01-20 23:29:15 mni77 Exp $
+/** \file tstraigh.c
+ * Straight track
*/
/* XTrkCad - Model Railroad CAD
@@ -20,9 +20,17 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-#include "track.h"
+#include <math.h>
+
#include "cstraigh.h"
+#include "cundo.h"
+#include "fileio.h"
#include "i18n.h"
+#include "layout.h"
+#include "messages.h"
+#include "param.h"
+#include "track.h"
+#include "utility.h"
/*******************************************************************************
*
@@ -70,13 +78,13 @@ static struct {
ANGLE_T angle;
FLOAT_T grade;
descPivot_t pivot;
- LAYER_T layerNumber;
+ unsigned int layerNumber;
} strData;
typedef enum { E0, Z0, E1, Z1, LN, AN, GR, PV, LY } strDesc_e;
static descData_t strDesc[] = {
-/*E0*/ { DESC_POS, N_("End Pt 1: X"), &strData.endPt[0] },
+/*E0*/ { DESC_POS, N_("End Pt 1: X,Y"), &strData.endPt[0] },
/*Z0*/ { DESC_DIM, N_("Z"), &strData.elev[0] },
-/*E1*/ { DESC_POS, N_("End Pt 2: X"), &strData.endPt[1] },
+/*E1*/ { DESC_POS, N_("End Pt 2: X,Y"), &strData.endPt[1] },
/*Z1*/ { DESC_DIM, N_("Z"), &strData.elev[1] },
/*LN*/ { DESC_DIM, N_("Length"), &strData.length },
/*AN*/ { DESC_ANGLE, N_("Angle"), &strData.angle },
@@ -420,7 +428,7 @@ static BOOL_T TrimStraight( track_p trk, EPINX_T ep, DIST_T dist )
AdjustStraightEndPt( trk, ep, pos );
DrawNewTrack( trk );
} else
- DeleteTrack( trk, FALSE );
+ DeleteTrack( trk, TRUE );
return TRUE;
}
@@ -548,6 +556,7 @@ static STATUS_T ModifyStraight( track_p trk, wAction_t action, coOrd pos )
InfoMessage( _("Straight: Length=%s Angle=%0.3f"),
FormatDistance( d ), PutAngle( GetTrkEndAngle( trk, ep ) ) );
MainRedraw();
+ MapRedraw();
return C_CONTINUE;
case C_UP:
@@ -556,6 +565,7 @@ static STATUS_T ModifyStraight( track_p trk, wAction_t action, coOrd pos )
tempSegs_da.cnt = 0;
DrawNewTrack( trk );
MainRedraw();
+ MapRedraw();
return C_TERMINATE;
default:
@@ -576,14 +586,19 @@ static BOOL_T GetParamsStraight( int inx, track_p trk, coOrd pos, trackParams_t
params->type = curveTypeStraight;
if ( inx == PARAMS_PARALLEL ) {
params->ep = 0;
+ } else if (inx == PARAMS_CORNU ){
+ params->ep = PickEndPoint( pos, trk);
+ params->arcP = zero;
+ params->arcR = 0.0;
} else {
- params->ep = PickUnconnectedEndPoint( pos, trk );
- if (params->ep == -1)
- return FALSE;
+ params->ep = PickUnconnectedEndPointSilent( pos, trk );
}
+ if (params->ep == -1)
+ return FALSE;
params->lineOrig = GetTrkEndPos(trk,1-params->ep);
params->lineEnd = GetTrkEndPos(trk,params->ep);
params->len = FindDistance( params->lineOrig, params->lineEnd );
+ params->track_angle = FindAngle( params->lineOrig, params->lineEnd);
params->angle = GetTrkEndAngle(trk,params->ep);
params->arcR = 0.0;
return TRUE;
@@ -610,6 +625,8 @@ static BOOL_T QueryStraight( track_p trk, int query )
case Q_CAN_MODIFYRADIUS:
case Q_CAN_GROUP:
case Q_ISTRACK:
+ case Q_CORNU_CAN_MODIFY:
+ case Q_MODIFY_CAN_SPLIT:
return TRUE;
default:
return FALSE;
@@ -703,9 +720,13 @@ EXPORT void StraightSegProc(
case SEGPROC_TRAVERSE1:
a1 = FindAngle( segPtr->u.l.pos[0], segPtr->u.l.pos[1] );
- a2 = NormalizeAngle( data->traverse1.angle+a1 );
- data->traverse1.backwards = (a2 < 270 && a2 > 90 );
+ a2 = NormalizeAngle( a1-data->traverse1.angle );
+ data->traverse1.backwards = ((a2 < 270) && (a2 > 90));
data->traverse1.dist = FindDistance( segPtr->u.l.pos[data->traverse1.backwards?1:0], data->traverse1.pos );
+ data->traverse1.reverse_seg = FALSE;
+ data->traverse1.negative = FALSE;
+ data->traverse1.segs_backwards = FALSE;
+ data->traverse1.BezSegInx = 0;
break;
case SEGPROC_TRAVERSE2:
@@ -716,7 +737,10 @@ EXPORT void StraightSegProc(
data->traverse2.dist = 0;
data->traverse2.angle = a1;
} else {
+ a1 = FindAngle( segPtr->u.l.pos[data->traverse2.segDir], segPtr->u.l.pos[1-data->traverse2.segDir] );
+ Translate( &data->traverse2.pos, segPtr->u.l.pos[data->traverse2.segDir], a1, d );
data->traverse2.dist -= d;
+ data->traverse2.angle = a1;
}
break;
@@ -768,9 +792,12 @@ EXPORT void StraightSegProc(
data->split.newSeg[1] = *segPtr;
data->split.newSeg[0].u.l.pos[1] = data->split.newSeg[1].u.l.pos[0] = p0;
break;
-
+ /*
+ * Note GetAngle always gives a positive angle because p0 is always left of p1
+ */
case SEGPROC_GETANGLE:
data->getAngle.angle = FindAngle( segPtr->u.l.pos[0], segPtr->u.l.pos[1] );
+ data->getAngle.radius = 0.0;
break;
}
}
@@ -788,7 +815,7 @@ track_p NewStraightTrack( coOrd p0, coOrd p1 )
track_p t;
ANGLE_T a;
t = NewTrack( 0, T_STRAIGHT, 2, 0 );
- SetTrkScale( t, curScaleInx );
+ SetTrkScale( t, GetLayoutCurScale() );
a = FindAngle( p1, p0 );
SetTrkEndPoint( t, 0, p0, a );
SetTrkEndPoint( t, 1, p1, NormalizeAngle( a+180.0 ) );
diff --git a/app/bin/unittest/CMakeLists.txt b/app/bin/unittest/CMakeLists.txt
index b6d2bc5..32e2ddb 100644
--- a/app/bin/unittest/CMakeLists.txt
+++ b/app/bin/unittest/CMakeLists.txt
@@ -9,4 +9,23 @@ target_link_libraries(dxfformattest
dynstring
${LIBS})
-add_test(DXFOutputTest dxfformattest) \ No newline at end of file
+add_test(DXFOutputTest dxfformattest)
+
+add_executable( pathstest
+ pathstest.c
+ )
+
+target_link_libraries(pathstest
+ dynstring
+ ${LIBS})
+
+add_test(PathsTest pathstest)
+
+add_executable( defaultstest
+ defaultstest.c
+ )
+
+target_link_libraries(defaultstest
+ ${LIBS})
+
+add_test(DefaultsTest defaultstest)
diff --git a/app/bin/unittest/defaultstest.c b/app/bin/unittest/defaultstest.c
new file mode 100644
index 0000000..d877f46
--- /dev/null
+++ b/app/bin/unittest/defaultstest.c
@@ -0,0 +1,110 @@
+/** \file PathsTest.c
+* Unit tests for the paths module
+*/
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdio.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include "common.h"
+
+#include "../appdefaults.c"
+
+struct appDefault tests[] = {
+ {"akey"},
+ {"hkey"},
+ {"mkey"},
+ {"zkey"}
+};
+
+
+const char *libDir ="Parameter/directory/";
+
+/**
+ * A dummy for the real MakePath function
+ */
+
+void
+MakeFullpath( char **result, ...)
+{
+ *result = libDir;
+}
+
+#define TESTARRAYSIZE (sizeof(tests) / sizeof(tests[0]) )
+
+struct appDefault test1[] = {
+ { "akey" }
+};
+
+#define TEST1ARRAYSIZE (sizeof(test1) / sizeof(test1[0]) )
+
+
+int
+wPrefGetIntegerBasic(const char *section, const char *name, long *result, long defaultValue)
+{
+ *result = defaultValue;
+ return(TRUE);
+}
+
+int
+wPrefGetFloatBasic(const char *section, const char *name, double *result, double defaultValue)
+{
+ *result = defaultValue;
+ return(TRUE);
+}
+
+const char * wPrefGetStringBasic(const char *section, const char *name)
+{
+ return(NULL);
+}
+
+static void BinarySearch(void **state)
+{
+ int result;
+ (void)state;
+
+ result = binarySearch(tests, 0, TESTARRAYSIZE-1, "nokey");
+ assert_int_equal(result, -1);
+
+ result = binarySearch(tests, 0, TESTARRAYSIZE-1, "akey");
+ assert_int_equal(result, 0);
+
+ result = binarySearch(tests, 1, TESTARRAYSIZE-1, "mkey");
+ assert_int_equal(result, 2);
+
+ result = binarySearch(tests, 0, TESTARRAYSIZE-1, "zkey");
+ assert_int_equal(result, 3);
+
+ result = binarySearch(test1, 0, TEST1ARRAYSIZE-1, "akey");
+ assert_int_equal(result, 0);
+
+ result = binarySearch(test1, 0, TEST1ARRAYSIZE-1, "zkey");
+ assert_int_equal(result, -1);
+
+}
+
+static void GetDefaults(void **state)
+{
+ double value = 0.0;
+ long intValue = 0;
+ (void)state;
+
+ wPrefGetIntegerExt("DialogItem", "cmdopt-preselect", &intValue, 2);
+ assert_int_equal(intValue, 1);
+
+ wPrefGetIntegerExt("DialogItem", "cmdopt-preselect", &intValue, 2);
+ assert_int_equal(intValue, 2);
+
+}
+
+int main(void)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(BinarySearch),
+ cmocka_unit_test(GetDefaults)
+ };
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/app/bin/unittest/pathstest.c b/app/bin/unittest/pathstest.c
new file mode 100644
index 0000000..b7e792e
--- /dev/null
+++ b/app/bin/unittest/pathstest.c
@@ -0,0 +1,121 @@
+/** \file PathsTest.c
+* Unit tests for the paths module
+*/
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdio.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include <dynstring.h>
+#include "../paths.h"
+
+#ifdef WINDOWS
+#define TESTPATH "C:\\Test\\Path"
+#define TESTFILENAME "file.test"
+#define TESTFILE TESTPATH "\\" TESTFILENAME
+#define TESTPATH2 "D:\\Root"
+#define TESTFILE2 TESTPATH2 "\\file2."
+
+#define TESTRELATIVEPATH "Test\\Path"
+#define DEFAULTPATH "C:\\Default\\Path"
+#else
+#define TESTPATH "/Test/Path"
+#define TESTFILENAME "file.test"
+#define TESTFILE TESTPATH "/" TESTFILENAME
+#define TESTPATH2 "/Root"
+#define TESTFILE2 TESTPATH2 "/file2."
+
+#define TESTRELATIVEPATH "Test/Path"
+#define DEFAULTPATH "/Default/Path"
+
+#endif //WINDOWS
+void
+wPrefSetString(const char *section, const char *key, const char *value)
+{}
+
+char *wPrefGetStringExt(const char *section, const char *key)
+{
+ return(NULL);
+}
+
+const char *wGetUserHomeDir(void)
+{
+ return(DEFAULTPATH);
+}
+
+#include "../paths.c"
+
+static void SetGetPath(void **state)
+{
+ char *string;
+ (void)state;
+
+ string = GetCurrentPath("Test");
+ assert_string_equal(string, DEFAULTPATH);
+
+ SetCurrentPath("Test", TESTFILE );
+ string = GetCurrentPath("Test");
+ assert_string_equal(string, TESTPATH);
+
+ SetCurrentPath("Test", TESTFILE2);
+ string = GetCurrentPath("Test");
+ assert_string_equal(string, TESTPATH2);
+}
+
+static void Makepath(void **state)
+{
+ (void)state;
+ char *path;
+
+#ifdef WINDOWS
+ MakeFullpath(&path,
+ "C:",
+ TESTRELATIVEPATH,
+ TESTFILENAME,
+ NULL);
+
+ assert_string_equal(path, "C:" TESTRELATIVEPATH "\\" TESTFILENAME);
+#else
+ MakeFullpath(&path,
+ TESTRELATIVEPATH,
+ TESTFILENAME,
+ NULL);
+
+ assert_string_equal(path, TESTRELATIVEPATH "/" TESTFILENAME);
+#endif // WINDOWS
+
+ free(path);
+
+#ifdef WINDOWS
+ MakeFullpath(&path,
+ "C:",
+ "test",
+ "\\subdir",
+ TESTFILENAME,
+ NULL);
+ assert_string_equal(path, "C:test\\subdir\\" TESTFILENAME);
+#else
+ MakeFullpath(&path,
+ "test",
+ "/subdir",
+ TESTFILENAME,
+ NULL);
+ assert_string_equal(path, "test/subdir/" TESTFILENAME);
+
+#endif // WINDOWS
+
+
+ free(path);
+}
+
+int main(void)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(SetGetPath),
+ cmocka_unit_test(Makepath),
+ };
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/app/bin/uthash.h b/app/bin/uthash.h
new file mode 100644
index 0000000..39fd891
--- /dev/null
+++ b/app/bin/uthash.h
@@ -0,0 +1,960 @@
+/*
+Copyright (c) 2003-2014, Troy D. Hanson http://troydhanson.github.com/uthash/
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef UTHASH_H
+#define UTHASH_H
+
+#include <string.h> /* memcmp,strlen */
+#include <stddef.h> /* ptrdiff_t */
+#include <stdlib.h> /* exit() */
+
+/* These macros use decltype or the earlier __typeof GNU extension.
+ As decltype is only available in newer compilers (VS2010 or gcc 4.3+
+ when compiling c++ source) this code uses whatever method is needed
+ or, for VS2008 where neither is available, uses casting workarounds. */
+#if defined(_MSC_VER) /* MS compiler */
+#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */
+#define DECLTYPE(x) (decltype(x))
+#else /* VS2008 or older (or VS2010 in C mode) */
+#define NO_DECLTYPE
+#define DECLTYPE(x)
+#endif
+#elif defined(__BORLANDC__) || defined(__LCC__) || defined(__WATCOMC__)
+#define NO_DECLTYPE
+#define DECLTYPE(x)
+#else /* GNU, Sun and other compilers */
+#define DECLTYPE(x) (__typeof(x))
+#endif
+
+#ifdef NO_DECLTYPE
+#define DECLTYPE_ASSIGN(dst,src) \
+do { \
+ char **_da_dst = (char**)(&(dst)); \
+ *_da_dst = (char*)(src); \
+} while(0)
+#else
+#define DECLTYPE_ASSIGN(dst,src) \
+do { \
+ (dst) = DECLTYPE(dst)(src); \
+} while(0)
+#endif
+
+/* a number of the hash function use uint32_t which isn't defined on Pre VS2010 */
+#if defined (_WIN32)
+#if defined(_MSC_VER) && _MSC_VER >= 1600
+#include <stdint.h>
+#elif defined(__WATCOMC__)
+#include <stdint.h>
+#else
+typedef unsigned int uint32_t;
+typedef unsigned char uint8_t;
+#endif
+#else
+#include <stdint.h>
+#endif
+
+#define UTHASH_VERSION 1.9.9
+
+#ifndef uthash_fatal
+#define uthash_fatal(msg) exit(-1) /* fatal error (out of memory,etc) */
+#endif
+#ifndef uthash_malloc
+#define uthash_malloc(sz) malloc(sz) /* malloc fcn */
+#endif
+#ifndef uthash_free
+#define uthash_free(ptr,sz) free(ptr) /* free fcn */
+#endif
+
+#ifndef uthash_noexpand_fyi
+#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */
+#endif
+#ifndef uthash_expand_fyi
+#define uthash_expand_fyi(tbl) /* can be defined to log expands */
+#endif
+
+/* initial number of buckets */
+#define HASH_INITIAL_NUM_BUCKETS 32 /* initial number of buckets */
+#define HASH_INITIAL_NUM_BUCKETS_LOG2 5 /* lg2 of initial number of buckets */
+#define HASH_BKT_CAPACITY_THRESH 10 /* expand when bucket count reaches */
+
+/* calculate the element whose hash handle address is hhe */
+#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho)))
+
+#define HASH_FIND(hh,head,keyptr,keylen,out) \
+do { \
+ out=NULL; \
+ if (head) { \
+ unsigned _hf_bkt,_hf_hashv; \
+ HASH_FCN(keyptr,keylen, (head)->hh.tbl->num_buckets, _hf_hashv, _hf_bkt); \
+ if (HASH_BLOOM_TEST((head)->hh.tbl, _hf_hashv)) { \
+ HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], \
+ keyptr,keylen,out); \
+ } \
+ } \
+} while (0)
+
+#ifdef HASH_BLOOM
+#define HASH_BLOOM_BITLEN (1ULL << HASH_BLOOM)
+#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8) + ((HASH_BLOOM_BITLEN%8) ? 1:0)
+#define HASH_BLOOM_MAKE(tbl) \
+do { \
+ (tbl)->bloom_nbits = HASH_BLOOM; \
+ (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \
+ if (!((tbl)->bloom_bv)) { uthash_fatal( "out of memory"); } \
+ memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN); \
+ (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \
+} while (0)
+
+#define HASH_BLOOM_FREE(tbl) \
+do { \
+ uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \
+} while (0)
+
+#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8] |= (1U << ((idx)%8)))
+#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8] & (1U << ((idx)%8)))
+
+#define HASH_BLOOM_ADD(tbl,hashv) \
+ HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1)))
+
+#define HASH_BLOOM_TEST(tbl,hashv) \
+ HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1)))
+
+#else
+#define HASH_BLOOM_MAKE(tbl)
+#define HASH_BLOOM_FREE(tbl)
+#define HASH_BLOOM_ADD(tbl,hashv)
+#define HASH_BLOOM_TEST(tbl,hashv) (1)
+#define HASH_BLOOM_BYTELEN 0
+#endif
+
+#define HASH_MAKE_TABLE(hh,head) \
+do { \
+ (head)->hh.tbl = (UT_hash_table*)uthash_malloc( \
+ sizeof(UT_hash_table)); \
+ if (!((head)->hh.tbl)) { uthash_fatal( "out of memory"); } \
+ memset((head)->hh.tbl, 0, sizeof(UT_hash_table)); \
+ (head)->hh.tbl->tail = &((head)->hh); \
+ (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \
+ (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \
+ (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \
+ (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \
+ HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \
+ if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); } \
+ memset((head)->hh.tbl->buckets, 0, \
+ HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \
+ HASH_BLOOM_MAKE((head)->hh.tbl); \
+ (head)->hh.tbl->signature = HASH_SIGNATURE; \
+} while(0)
+
+#define HASH_ADD(hh,head,fieldname,keylen_in,add) \
+ HASH_ADD_KEYPTR(hh,head,&((add)->fieldname),keylen_in,add)
+
+#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \
+do { \
+ replaced=NULL; \
+ HASH_FIND(hh,head,&((add)->fieldname),keylen_in,replaced); \
+ if (replaced!=NULL) { \
+ HASH_DELETE(hh,head,replaced); \
+ } \
+ HASH_ADD(hh,head,fieldname,keylen_in,add); \
+} while(0)
+
+#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \
+do { \
+ unsigned _ha_bkt; \
+ (add)->hh.next = NULL; \
+ (add)->hh.key = (char*)(keyptr); \
+ (add)->hh.keylen = (unsigned)(keylen_in); \
+ if (!(head)) { \
+ head = (add); \
+ (head)->hh.prev = NULL; \
+ HASH_MAKE_TABLE(hh,head); \
+ } else { \
+ (head)->hh.tbl->tail->next = (add); \
+ (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \
+ (head)->hh.tbl->tail = &((add)->hh); \
+ } \
+ (head)->hh.tbl->num_items++; \
+ (add)->hh.tbl = (head)->hh.tbl; \
+ HASH_FCN(keyptr,keylen_in, (head)->hh.tbl->num_buckets, \
+ (add)->hh.hashv, _ha_bkt); \
+ HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt],&(add)->hh); \
+ HASH_BLOOM_ADD((head)->hh.tbl,(add)->hh.hashv); \
+ HASH_EMIT_KEY(hh,head,keyptr,keylen_in); \
+ HASH_FSCK(hh,head); \
+} while(0)
+
+#define HASH_TO_BKT( hashv, num_bkts, bkt ) \
+do { \
+ bkt = ((hashv) & ((num_bkts) - 1)); \
+} while(0)
+
+/* delete "delptr" from the hash table.
+ * "the usual" patch-up process for the app-order doubly-linked-list.
+ * The use of _hd_hh_del below deserves special explanation.
+ * These used to be expressed using (delptr) but that led to a bug
+ * if someone used the same symbol for the head and deletee, like
+ * HASH_DELETE(hh,users,users);
+ * We want that to work, but by changing the head (users) below
+ * we were forfeiting our ability to further refer to the deletee (users)
+ * in the patch-up process. Solution: use scratch space to
+ * copy the deletee pointer, then the latter references are via that
+ * scratch pointer rather than through the repointed (users) symbol.
+ */
+#define HASH_DELETE(hh,head,delptr) \
+do { \
+ struct UT_hash_handle *_hd_hh_del; \
+ if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) ) { \
+ uthash_free((head)->hh.tbl->buckets, \
+ (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \
+ HASH_BLOOM_FREE((head)->hh.tbl); \
+ uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \
+ head = NULL; \
+ } else { \
+ unsigned _hd_bkt; \
+ _hd_hh_del = &((delptr)->hh); \
+ if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) { \
+ (head)->hh.tbl->tail = \
+ (UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \
+ (head)->hh.tbl->hho); \
+ } \
+ if ((delptr)->hh.prev) { \
+ ((UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \
+ (head)->hh.tbl->hho))->next = (delptr)->hh.next; \
+ } else { \
+ DECLTYPE_ASSIGN(head,(delptr)->hh.next); \
+ } \
+ if (_hd_hh_del->next) { \
+ ((UT_hash_handle*)((ptrdiff_t)_hd_hh_del->next + \
+ (head)->hh.tbl->hho))->prev = \
+ _hd_hh_del->prev; \
+ } \
+ HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \
+ HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \
+ (head)->hh.tbl->num_items--; \
+ } \
+ HASH_FSCK(hh,head); \
+} while (0)
+
+
+/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */
+#define HASH_FIND_STR(head,findstr,out) \
+ HASH_FIND(hh,head,findstr,(unsigned)strlen(findstr),out)
+#define HASH_ADD_STR(head,strfield,add) \
+ HASH_ADD(hh,head,strfield[0],strlen(add->strfield),add)
+#define HASH_REPLACE_STR(head,strfield,add,replaced) \
+ HASH_REPLACE(hh,head,strfield[0],(unsigned)strlen(add->strfield),add,replaced)
+#define HASH_FIND_INT(head,findint,out) \
+ HASH_FIND(hh,head,findint,sizeof(int),out)
+#define HASH_ADD_INT(head,intfield,add) \
+ HASH_ADD(hh,head,intfield,sizeof(int),add)
+#define HASH_REPLACE_INT(head,intfield,add,replaced) \
+ HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced)
+#define HASH_FIND_PTR(head,findptr,out) \
+ HASH_FIND(hh,head,findptr,sizeof(void *),out)
+#define HASH_ADD_PTR(head,ptrfield,add) \
+ HASH_ADD(hh,head,ptrfield,sizeof(void *),add)
+#define HASH_REPLACE_PTR(head,ptrfield,add,replaced) \
+ HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced)
+#define HASH_DEL(head,delptr) \
+ HASH_DELETE(hh,head,delptr)
+
+/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined.
+ * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined.
+ */
+#ifdef HASH_DEBUG
+#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0)
+#define HASH_FSCK(hh,head) \
+do { \
+ struct UT_hash_handle *_thh; \
+ if (head) { \
+ unsigned _bkt_i; \
+ unsigned _count; \
+ char *_prev; \
+ _count = 0; \
+ for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) { \
+ unsigned _bkt_count = 0; \
+ _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \
+ _prev = NULL; \
+ while (_thh) { \
+ if (_prev != (char*)(_thh->hh_prev)) { \
+ HASH_OOPS("invalid hh_prev %p, actual %p\n", \
+ _thh->hh_prev, _prev ); \
+ } \
+ _bkt_count++; \
+ _prev = (char*)(_thh); \
+ _thh = _thh->hh_next; \
+ } \
+ _count += _bkt_count; \
+ if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \
+ HASH_OOPS("invalid bucket count %u, actual %u\n", \
+ (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \
+ } \
+ } \
+ if (_count != (head)->hh.tbl->num_items) { \
+ HASH_OOPS("invalid hh item count %u, actual %u\n", \
+ (head)->hh.tbl->num_items, _count ); \
+ } \
+ /* traverse hh in app order; check next/prev integrity, count */ \
+ _count = 0; \
+ _prev = NULL; \
+ _thh = &(head)->hh; \
+ while (_thh) { \
+ _count++; \
+ if (_prev !=(char*)(_thh->prev)) { \
+ HASH_OOPS("invalid prev %p, actual %p\n", \
+ _thh->prev, _prev ); \
+ } \
+ _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \
+ _thh = ( _thh->next ? (UT_hash_handle*)((char*)(_thh->next) + \
+ (head)->hh.tbl->hho) : NULL ); \
+ } \
+ if (_count != (head)->hh.tbl->num_items) { \
+ HASH_OOPS("invalid app item count %u, actual %u\n", \
+ (head)->hh.tbl->num_items, _count ); \
+ } \
+ } \
+} while (0)
+#else
+#define HASH_FSCK(hh,head)
+#endif
+
+/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to
+ * the descriptor to which this macro is defined for tuning the hash function.
+ * The app can #include <unistd.h> to get the prototype for write(2). */
+#ifdef HASH_EMIT_KEYS
+#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \
+do { \
+ unsigned _klen = fieldlen; \
+ write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \
+ write(HASH_EMIT_KEYS, keyptr, fieldlen); \
+} while (0)
+#else
+#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)
+#endif
+
+/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */
+#ifdef HASH_FUNCTION
+#define HASH_FCN HASH_FUNCTION
+#else
+#define HASH_FCN HASH_JEN
+#endif
+
+/* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */
+#define HASH_BER(key,keylen,num_bkts,hashv,bkt) \
+do { \
+ unsigned _hb_keylen=keylen; \
+ const char *_hb_key=(const char*)(key); \
+ (hashv) = 0; \
+ while (_hb_keylen--) { (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++; } \
+ bkt = (hashv) & (num_bkts-1); \
+} while (0)
+
+
+/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at
+ * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */
+#define HASH_SAX(key,keylen,num_bkts,hashv,bkt) \
+do { \
+ unsigned _sx_i; \
+ const char *_hs_key=(const char*)(key); \
+ hashv = 0; \
+ for(_sx_i=0; _sx_i < keylen; _sx_i++) \
+ hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \
+ bkt = hashv & (num_bkts-1); \
+} while (0)
+/* FNV-1a variation */
+#define HASH_FNV(key,keylen,num_bkts,hashv,bkt) \
+do { \
+ unsigned _fn_i; \
+ const char *_hf_key=(const char*)(key); \
+ hashv = 2166136261UL; \
+ for(_fn_i=0; _fn_i < keylen; _fn_i++) { \
+ hashv = hashv ^ _hf_key[_fn_i]; \
+ hashv = hashv * 16777619; \
+ } \
+ bkt = hashv & (num_bkts-1); \
+} while(0)
+
+#define HASH_OAT(key,keylen,num_bkts,hashv,bkt) \
+do { \
+ unsigned _ho_i; \
+ const char *_ho_key=(const char*)(key); \
+ hashv = 0; \
+ for(_ho_i=0; _ho_i < keylen; _ho_i++) { \
+ hashv += _ho_key[_ho_i]; \
+ hashv += (hashv << 10); \
+ hashv ^= (hashv >> 6); \
+ } \
+ hashv += (hashv << 3); \
+ hashv ^= (hashv >> 11); \
+ hashv += (hashv << 15); \
+ bkt = hashv & (num_bkts-1); \
+} while(0)
+
+#define HASH_JEN_MIX(a,b,c) \
+do { \
+ a -= b; a -= c; a ^= ( c >> 13 ); \
+ b -= c; b -= a; b ^= ( a << 8 ); \
+ c -= a; c -= b; c ^= ( b >> 13 ); \
+ a -= b; a -= c; a ^= ( c >> 12 ); \
+ b -= c; b -= a; b ^= ( a << 16 ); \
+ c -= a; c -= b; c ^= ( b >> 5 ); \
+ a -= b; a -= c; a ^= ( c >> 3 ); \
+ b -= c; b -= a; b ^= ( a << 10 ); \
+ c -= a; c -= b; c ^= ( b >> 15 ); \
+} while (0)
+
+#define HASH_JEN(key,keylen,num_bkts,hashv,bkt) \
+do { \
+ unsigned _hj_i,_hj_j,_hj_k; \
+ unsigned const char *_hj_key=(unsigned const char*)(key); \
+ hashv = 0xfeedbeef; \
+ _hj_i = _hj_j = 0x9e3779b9; \
+ _hj_k = (unsigned)(keylen); \
+ while (_hj_k >= 12) { \
+ _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \
+ + ( (unsigned)_hj_key[2] << 16 ) \
+ + ( (unsigned)_hj_key[3] << 24 ) ); \
+ _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \
+ + ( (unsigned)_hj_key[6] << 16 ) \
+ + ( (unsigned)_hj_key[7] << 24 ) ); \
+ hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \
+ + ( (unsigned)_hj_key[10] << 16 ) \
+ + ( (unsigned)_hj_key[11] << 24 ) ); \
+ \
+ HASH_JEN_MIX(_hj_i, _hj_j, hashv); \
+ \
+ _hj_key += 12; \
+ _hj_k -= 12; \
+ } \
+ hashv += keylen; \
+ switch ( _hj_k ) { \
+ case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); \
+ case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); \
+ case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); \
+ case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); \
+ case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); \
+ case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); \
+ case 5: _hj_j += _hj_key[4]; \
+ case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); \
+ case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); \
+ case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); \
+ case 1: _hj_i += _hj_key[0]; \
+ } \
+ HASH_JEN_MIX(_hj_i, _hj_j, hashv); \
+ bkt = hashv & (num_bkts-1); \
+} while(0)
+
+/* The Paul Hsieh hash function */
+#undef get16bits
+#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \
+ || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
+#define get16bits(d) (*((const uint16_t *) (d)))
+#endif
+
+#if !defined (get16bits)
+#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \
+ +(uint32_t)(((const uint8_t *)(d))[0]) )
+#endif
+#define HASH_SFH(key,keylen,num_bkts,hashv,bkt) \
+do { \
+ unsigned const char *_sfh_key=(unsigned const char*)(key); \
+ uint32_t _sfh_tmp, _sfh_len = keylen; \
+ \
+ int _sfh_rem = _sfh_len & 3; \
+ _sfh_len >>= 2; \
+ hashv = 0xcafebabe; \
+ \
+ /* Main loop */ \
+ for (;_sfh_len > 0; _sfh_len--) { \
+ hashv += get16bits (_sfh_key); \
+ _sfh_tmp = (uint32_t)(get16bits (_sfh_key+2)) << 11 ^ hashv; \
+ hashv = (hashv << 16) ^ _sfh_tmp; \
+ _sfh_key += 2*sizeof (uint16_t); \
+ hashv += hashv >> 11; \
+ } \
+ \
+ /* Handle end cases */ \
+ switch (_sfh_rem) { \
+ case 3: hashv += get16bits (_sfh_key); \
+ hashv ^= hashv << 16; \
+ hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)] << 18); \
+ hashv += hashv >> 11; \
+ break; \
+ case 2: hashv += get16bits (_sfh_key); \
+ hashv ^= hashv << 11; \
+ hashv += hashv >> 17; \
+ break; \
+ case 1: hashv += *_sfh_key; \
+ hashv ^= hashv << 10; \
+ hashv += hashv >> 1; \
+ } \
+ \
+ /* Force "avalanching" of final 127 bits */ \
+ hashv ^= hashv << 3; \
+ hashv += hashv >> 5; \
+ hashv ^= hashv << 4; \
+ hashv += hashv >> 17; \
+ hashv ^= hashv << 25; \
+ hashv += hashv >> 6; \
+ bkt = hashv & (num_bkts-1); \
+} while(0)
+
+#ifdef HASH_USING_NO_STRICT_ALIASING
+/* The MurmurHash exploits some CPU's (x86,x86_64) tolerance for unaligned reads.
+ * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error.
+ * MurmurHash uses the faster approach only on CPU's where we know it's safe.
+ *
+ * Note the preprocessor built-in defines can be emitted using:
+ *
+ * gcc -m64 -dM -E - < /dev/null (on gcc)
+ * cc -## a.c (where a.c is a simple test file) (Sun Studio)
+ */
+#if (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86))
+#define MUR_GETBLOCK(p,i) p[i]
+#else /* non intel */
+#define MUR_PLUS0_ALIGNED(p) (((unsigned long)p & 0x3) == 0)
+#define MUR_PLUS1_ALIGNED(p) (((unsigned long)p & 0x3) == 1)
+#define MUR_PLUS2_ALIGNED(p) (((unsigned long)p & 0x3) == 2)
+#define MUR_PLUS3_ALIGNED(p) (((unsigned long)p & 0x3) == 3)
+#define WP(p) ((uint32_t*)((unsigned long)(p) & ~3UL))
+#if (defined(__BIG_ENDIAN__) || defined(SPARC) || defined(__ppc__) || defined(__ppc64__))
+#define MUR_THREE_ONE(p) ((((*WP(p))&0x00ffffff) << 8) | (((*(WP(p)+1))&0xff000000) >> 24))
+#define MUR_TWO_TWO(p) ((((*WP(p))&0x0000ffff) <<16) | (((*(WP(p)+1))&0xffff0000) >> 16))
+#define MUR_ONE_THREE(p) ((((*WP(p))&0x000000ff) <<24) | (((*(WP(p)+1))&0xffffff00) >> 8))
+#else /* assume little endian non-intel */
+#define MUR_THREE_ONE(p) ((((*WP(p))&0xffffff00) >> 8) | (((*(WP(p)+1))&0x000000ff) << 24))
+#define MUR_TWO_TWO(p) ((((*WP(p))&0xffff0000) >>16) | (((*(WP(p)+1))&0x0000ffff) << 16))
+#define MUR_ONE_THREE(p) ((((*WP(p))&0xff000000) >>24) | (((*(WP(p)+1))&0x00ffffff) << 8))
+#endif
+#define MUR_GETBLOCK(p,i) (MUR_PLUS0_ALIGNED(p) ? ((p)[i]) : \
+ (MUR_PLUS1_ALIGNED(p) ? MUR_THREE_ONE(p) : \
+ (MUR_PLUS2_ALIGNED(p) ? MUR_TWO_TWO(p) : \
+ MUR_ONE_THREE(p))))
+#endif
+#define MUR_ROTL32(x,r) (((x) << (r)) | ((x) >> (32 - (r))))
+#define MUR_FMIX(_h) \
+do { \
+ _h ^= _h >> 16; \
+ _h *= 0x85ebca6b; \
+ _h ^= _h >> 13; \
+ _h *= 0xc2b2ae35l; \
+ _h ^= _h >> 16; \
+} while(0)
+
+#define HASH_MUR(key,keylen,num_bkts,hashv,bkt) \
+do { \
+ const uint8_t *_mur_data = (const uint8_t*)(key); \
+ const int _mur_nblocks = (keylen) / 4; \
+ uint32_t _mur_h1 = 0xf88D5353; \
+ uint32_t _mur_c1 = 0xcc9e2d51; \
+ uint32_t _mur_c2 = 0x1b873593; \
+ uint32_t _mur_k1 = 0; \
+ const uint8_t *_mur_tail; \
+ const uint32_t *_mur_blocks = (const uint32_t*)(_mur_data+_mur_nblocks*4); \
+ int _mur_i; \
+ for(_mur_i = -_mur_nblocks; _mur_i; _mur_i++) { \
+ _mur_k1 = MUR_GETBLOCK(_mur_blocks,_mur_i); \
+ _mur_k1 *= _mur_c1; \
+ _mur_k1 = MUR_ROTL32(_mur_k1,15); \
+ _mur_k1 *= _mur_c2; \
+ \
+ _mur_h1 ^= _mur_k1; \
+ _mur_h1 = MUR_ROTL32(_mur_h1,13); \
+ _mur_h1 = _mur_h1*5+0xe6546b64; \
+ } \
+ _mur_tail = (const uint8_t*)(_mur_data + _mur_nblocks*4); \
+ _mur_k1=0; \
+ switch((keylen) & 3) { \
+ case 3: _mur_k1 ^= _mur_tail[2] << 16; \
+ case 2: _mur_k1 ^= _mur_tail[1] << 8; \
+ case 1: _mur_k1 ^= _mur_tail[0]; \
+ _mur_k1 *= _mur_c1; \
+ _mur_k1 = MUR_ROTL32(_mur_k1,15); \
+ _mur_k1 *= _mur_c2; \
+ _mur_h1 ^= _mur_k1; \
+ } \
+ _mur_h1 ^= (keylen); \
+ MUR_FMIX(_mur_h1); \
+ hashv = _mur_h1; \
+ bkt = hashv & (num_bkts-1); \
+} while(0)
+#endif /* HASH_USING_NO_STRICT_ALIASING */
+
+/* key comparison function; return 0 if keys equal */
+#define HASH_KEYCMP(a,b,len) memcmp(a,b,len)
+
+/* iterate over items in a known bucket to find desired item */
+#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,out) \
+do { \
+ if (head.hh_head) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,head.hh_head)); \
+ else out=NULL; \
+ while (out) { \
+ if ((out)->hh.keylen == keylen_in) { \
+ if ((HASH_KEYCMP((out)->hh.key,keyptr,keylen_in)) == 0) break; \
+ } \
+ if ((out)->hh.hh_next) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,(out)->hh.hh_next)); \
+ else out = NULL; \
+ } \
+} while(0)
+
+/* add an item to a bucket */
+#define HASH_ADD_TO_BKT(head,addhh) \
+do { \
+ head.count++; \
+ (addhh)->hh_next = head.hh_head; \
+ (addhh)->hh_prev = NULL; \
+ if (head.hh_head) { (head).hh_head->hh_prev = (addhh); } \
+ (head).hh_head=addhh; \
+ if (head.count >= ((head.expand_mult+1) * HASH_BKT_CAPACITY_THRESH) \
+ && (addhh)->tbl->noexpand != 1) { \
+ HASH_EXPAND_BUCKETS((addhh)->tbl); \
+ } \
+} while(0)
+
+/* remove an item from a given bucket */
+#define HASH_DEL_IN_BKT(hh,head,hh_del) \
+ (head).count--; \
+ if ((head).hh_head == hh_del) { \
+ (head).hh_head = hh_del->hh_next; \
+ } \
+ if (hh_del->hh_prev) { \
+ hh_del->hh_prev->hh_next = hh_del->hh_next; \
+ } \
+ if (hh_del->hh_next) { \
+ hh_del->hh_next->hh_prev = hh_del->hh_prev; \
+ }
+
+/* Bucket expansion has the effect of doubling the number of buckets
+ * and redistributing the items into the new buckets. Ideally the
+ * items will distribute more or less evenly into the new buckets
+ * (the extent to which this is true is a measure of the quality of
+ * the hash function as it applies to the key domain).
+ *
+ * With the items distributed into more buckets, the chain length
+ * (item count) in each bucket is reduced. Thus by expanding buckets
+ * the hash keeps a bound on the chain length. This bounded chain
+ * length is the essence of how a hash provides constant time lookup.
+ *
+ * The calculation of tbl->ideal_chain_maxlen below deserves some
+ * explanation. First, keep in mind that we're calculating the ideal
+ * maximum chain length based on the *new* (doubled) bucket count.
+ * In fractions this is just n/b (n=number of items,b=new num buckets).
+ * Since the ideal chain length is an integer, we want to calculate
+ * ceil(n/b). We don't depend on floating point arithmetic in this
+ * hash, so to calculate ceil(n/b) with integers we could write
+ *
+ * ceil(n/b) = (n/b) + ((n%b)?1:0)
+ *
+ * and in fact a previous version of this hash did just that.
+ * But now we have improved things a bit by recognizing that b is
+ * always a power of two. We keep its base 2 log handy (call it lb),
+ * so now we can write this with a bit shift and logical AND:
+ *
+ * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0)
+ *
+ */
+#define HASH_EXPAND_BUCKETS(tbl) \
+do { \
+ unsigned _he_bkt; \
+ unsigned _he_bkt_i; \
+ struct UT_hash_handle *_he_thh, *_he_hh_nxt; \
+ UT_hash_bucket *_he_new_buckets, *_he_newbkt; \
+ _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \
+ 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \
+ if (!_he_new_buckets) { uthash_fatal( "out of memory"); } \
+ memset(_he_new_buckets, 0, \
+ 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \
+ tbl->ideal_chain_maxlen = \
+ (tbl->num_items >> (tbl->log2_num_buckets+1)) + \
+ ((tbl->num_items & ((tbl->num_buckets*2)-1)) ? 1 : 0); \
+ tbl->nonideal_items = 0; \
+ for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++) \
+ { \
+ _he_thh = tbl->buckets[ _he_bkt_i ].hh_head; \
+ while (_he_thh) { \
+ _he_hh_nxt = _he_thh->hh_next; \
+ HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2, _he_bkt); \
+ _he_newbkt = &(_he_new_buckets[ _he_bkt ]); \
+ if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) { \
+ tbl->nonideal_items++; \
+ _he_newbkt->expand_mult = _he_newbkt->count / \
+ tbl->ideal_chain_maxlen; \
+ } \
+ _he_thh->hh_prev = NULL; \
+ _he_thh->hh_next = _he_newbkt->hh_head; \
+ if (_he_newbkt->hh_head) _he_newbkt->hh_head->hh_prev = \
+ _he_thh; \
+ _he_newbkt->hh_head = _he_thh; \
+ _he_thh = _he_hh_nxt; \
+ } \
+ } \
+ uthash_free( tbl->buckets, tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \
+ tbl->num_buckets *= 2; \
+ tbl->log2_num_buckets++; \
+ tbl->buckets = _he_new_buckets; \
+ tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ? \
+ (tbl->ineff_expands+1) : 0; \
+ if (tbl->ineff_expands > 1) { \
+ tbl->noexpand=1; \
+ uthash_noexpand_fyi(tbl); \
+ } \
+ uthash_expand_fyi(tbl); \
+} while(0)
+
+
+/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */
+/* Note that HASH_SORT assumes the hash handle name to be hh.
+ * HASH_SRT was added to allow the hash handle name to be passed in. */
+#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn)
+#define HASH_SRT(hh,head,cmpfcn) \
+do { \
+ unsigned _hs_i; \
+ unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \
+ struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \
+ if (head) { \
+ _hs_insize = 1; \
+ _hs_looping = 1; \
+ _hs_list = &((head)->hh); \
+ while (_hs_looping) { \
+ _hs_p = _hs_list; \
+ _hs_list = NULL; \
+ _hs_tail = NULL; \
+ _hs_nmerges = 0; \
+ while (_hs_p) { \
+ _hs_nmerges++; \
+ _hs_q = _hs_p; \
+ _hs_psize = 0; \
+ for ( _hs_i = 0; _hs_i < _hs_insize; _hs_i++ ) { \
+ _hs_psize++; \
+ _hs_q = (UT_hash_handle*)((_hs_q->next) ? \
+ ((void*)((char*)(_hs_q->next) + \
+ (head)->hh.tbl->hho)) : NULL); \
+ if (! (_hs_q) ) break; \
+ } \
+ _hs_qsize = _hs_insize; \
+ while ((_hs_psize > 0) || ((_hs_qsize > 0) && _hs_q )) { \
+ if (_hs_psize == 0) { \
+ _hs_e = _hs_q; \
+ _hs_q = (UT_hash_handle*)((_hs_q->next) ? \
+ ((void*)((char*)(_hs_q->next) + \
+ (head)->hh.tbl->hho)) : NULL); \
+ _hs_qsize--; \
+ } else if ( (_hs_qsize == 0) || !(_hs_q) ) { \
+ _hs_e = _hs_p; \
+ if (_hs_p){ \
+ _hs_p = (UT_hash_handle*)((_hs_p->next) ? \
+ ((void*)((char*)(_hs_p->next) + \
+ (head)->hh.tbl->hho)) : NULL); \
+ } \
+ _hs_psize--; \
+ } else if (( \
+ cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \
+ DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \
+ ) <= 0) { \
+ _hs_e = _hs_p; \
+ if (_hs_p){ \
+ _hs_p = (UT_hash_handle*)((_hs_p->next) ? \
+ ((void*)((char*)(_hs_p->next) + \
+ (head)->hh.tbl->hho)) : NULL); \
+ } \
+ _hs_psize--; \
+ } else { \
+ _hs_e = _hs_q; \
+ _hs_q = (UT_hash_handle*)((_hs_q->next) ? \
+ ((void*)((char*)(_hs_q->next) + \
+ (head)->hh.tbl->hho)) : NULL); \
+ _hs_qsize--; \
+ } \
+ if ( _hs_tail ) { \
+ _hs_tail->next = ((_hs_e) ? \
+ ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL); \
+ } else { \
+ _hs_list = _hs_e; \
+ } \
+ if (_hs_e) { \
+ _hs_e->prev = ((_hs_tail) ? \
+ ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL); \
+ } \
+ _hs_tail = _hs_e; \
+ } \
+ _hs_p = _hs_q; \
+ } \
+ if (_hs_tail){ \
+ _hs_tail->next = NULL; \
+ } \
+ if ( _hs_nmerges <= 1 ) { \
+ _hs_looping=0; \
+ (head)->hh.tbl->tail = _hs_tail; \
+ DECLTYPE_ASSIGN(head,ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \
+ } \
+ _hs_insize *= 2; \
+ } \
+ HASH_FSCK(hh,head); \
+ } \
+} while (0)
+
+/* This function selects items from one hash into another hash.
+ * The end result is that the selected items have dual presence
+ * in both hashes. There is no copy of the items made; rather
+ * they are added into the new hash through a secondary hash
+ * hash handle that must be present in the structure. */
+#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \
+do { \
+ unsigned _src_bkt, _dst_bkt; \
+ void *_last_elt=NULL, *_elt; \
+ UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \
+ ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \
+ if (src) { \
+ for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \
+ for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \
+ _src_hh; \
+ _src_hh = _src_hh->hh_next) { \
+ _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \
+ if (cond(_elt)) { \
+ _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \
+ _dst_hh->key = _src_hh->key; \
+ _dst_hh->keylen = _src_hh->keylen; \
+ _dst_hh->hashv = _src_hh->hashv; \
+ _dst_hh->prev = _last_elt; \
+ _dst_hh->next = NULL; \
+ if (_last_elt_hh) { _last_elt_hh->next = _elt; } \
+ if (!dst) { \
+ DECLTYPE_ASSIGN(dst,_elt); \
+ HASH_MAKE_TABLE(hh_dst,dst); \
+ } else { \
+ _dst_hh->tbl = (dst)->hh_dst.tbl; \
+ } \
+ HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \
+ HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh); \
+ (dst)->hh_dst.tbl->num_items++; \
+ _last_elt = _elt; \
+ _last_elt_hh = _dst_hh; \
+ } \
+ } \
+ } \
+ } \
+ HASH_FSCK(hh_dst,dst); \
+} while (0)
+
+#define HASH_CLEAR(hh,head) \
+do { \
+ if (head) { \
+ uthash_free((head)->hh.tbl->buckets, \
+ (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \
+ HASH_BLOOM_FREE((head)->hh.tbl); \
+ uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \
+ (head)=NULL; \
+ } \
+} while(0)
+
+#define HASH_OVERHEAD(hh,head) \
+ ((head) ? ( \
+ (size_t)((((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \
+ ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \
+ (sizeof(UT_hash_table)) + \
+ (HASH_BLOOM_BYTELEN)))) : 0)
+
+#ifdef NO_DECLTYPE
+#define HASH_ITER(hh,head,el,tmp) \
+for((el)=(head), (*(char**)(&(tmp)))=(char*)((head)?(head)->hh.next:NULL); \
+ el; (el)=(tmp),(*(char**)(&(tmp)))=(char*)((tmp)?(tmp)->hh.next:NULL))
+#else
+#define HASH_ITER(hh,head,el,tmp) \
+for((el)=(head),(tmp)=DECLTYPE(el)((head)?(head)->hh.next:NULL); \
+ el; (el)=(tmp),(tmp)=DECLTYPE(el)((tmp)?(tmp)->hh.next:NULL))
+#endif
+
+/* obtain a count of items in the hash */
+#define HASH_COUNT(head) HASH_CNT(hh,head)
+#define HASH_CNT(hh,head) ((head)?((head)->hh.tbl->num_items):0)
+
+typedef struct UT_hash_bucket {
+ struct UT_hash_handle *hh_head;
+ unsigned count;
+
+ /* expand_mult is normally set to 0. In this situation, the max chain length
+ * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If
+ * the bucket's chain exceeds this length, bucket expansion is triggered).
+ * However, setting expand_mult to a non-zero value delays bucket expansion
+ * (that would be triggered by additions to this particular bucket)
+ * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH.
+ * (The multiplier is simply expand_mult+1). The whole idea of this
+ * multiplier is to reduce bucket expansions, since they are expensive, in
+ * situations where we know that a particular bucket tends to be overused.
+ * It is better to let its chain length grow to a longer yet-still-bounded
+ * value, than to do an O(n) bucket expansion too often.
+ */
+ unsigned expand_mult;
+
+} UT_hash_bucket;
+
+/* random signature used only to find hash tables in external analysis */
+#define HASH_SIGNATURE 0xa0111fe1
+#define HASH_BLOOM_SIGNATURE 0xb12220f2
+
+typedef struct UT_hash_table {
+ UT_hash_bucket *buckets;
+ unsigned num_buckets, log2_num_buckets;
+ unsigned num_items;
+ struct UT_hash_handle *tail; /* tail hh in app order, for fast append */
+ ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */
+
+ /* in an ideal situation (all buckets used equally), no bucket would have
+ * more than ceil(#items/#buckets) items. that's the ideal chain length. */
+ unsigned ideal_chain_maxlen;
+
+ /* nonideal_items is the number of items in the hash whose chain position
+ * exceeds the ideal chain maxlen. these items pay the penalty for an uneven
+ * hash distribution; reaching them in a chain traversal takes >ideal steps */
+ unsigned nonideal_items;
+
+ /* ineffective expands occur when a bucket doubling was performed, but
+ * afterward, more than half the items in the hash had nonideal chain
+ * positions. If this happens on two consecutive expansions we inhibit any
+ * further expansion, as it's not helping; this happens when the hash
+ * function isn't a good fit for the key domain. When expansion is inhibited
+ * the hash will still work, albeit no longer in constant time. */
+ unsigned ineff_expands, noexpand;
+
+ uint32_t signature; /* used only to find hash tables in external analysis */
+#ifdef HASH_BLOOM
+ uint32_t bloom_sig; /* used only to test bloom exists in external analysis */
+ uint8_t *bloom_bv;
+ char bloom_nbits;
+#endif
+
+} UT_hash_table;
+
+typedef struct UT_hash_handle {
+ struct UT_hash_table *tbl;
+ void *prev; /* prev element in app order */
+ void *next; /* next element in app order */
+ struct UT_hash_handle *hh_prev; /* previous hh in bucket order */
+ struct UT_hash_handle *hh_next; /* next hh in bucket order */
+ void *key; /* ptr to enclosing struct's key */
+ unsigned keylen; /* enclosing struct's key len */
+ unsigned hashv; /* result of hash-fcn(key) */
+} UT_hash_handle;
+
+#endif /* UTHASH_H */
diff --git a/app/bin/utility.c b/app/bin/utility.c
index 9708ac4..d1f798c 100644
--- a/app/bin/utility.c
+++ b/app/bin/utility.c
@@ -82,6 +82,22 @@ double NormalizeAngle( double a )
return a;
}
+double DifferenceBetweenAngles(double a, double b) {
+ double difference = b - a;
+ while (difference < -180) difference += 360;
+ while (difference > 180) difference -= 360;
+ return difference;
+}
+
+int AngleInRange(double a, double start, double size) {
+ if (DifferenceBetweenAngles(start+size,a)<=0.0) {
+ if (DifferenceBetweenAngles(start,a)>=0.0)
+ return 0;
+ else return 1;
+ }
+ return -1;
+}
+
int IsAligned( double a1, double a2 )
diff --git a/app/bin/utility.h b/app/bin/utility.h
index ccf85e4..8666e6b 100644
--- a/app/bin/utility.h
+++ b/app/bin/utility.h
@@ -1,5 +1,5 @@
-/*
- * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/utility.h,v 1.1 2005-12-07 15:47:39 rc-flyer Exp $
+/** \file utility.h
+ * Prototypes for misc utility functions
*/
/* XTrkCad - Model Railroad CAD
@@ -23,6 +23,8 @@
#ifndef UTILITY_H
#define UTILITY_H
+#include "common.h"
+
#ifndef TRUE
#define TRUE 1
#define FALSE 0
@@ -43,6 +45,8 @@ double min( double a, double b );
#endif
double FindDistance( coOrd p0, coOrd p1 );
double NormalizeAngle( double a );
+double DifferenceBetweenAngles(double a, double b);
+int AngleInRange(double a, double start, double size);
int IsAligned( double a1, double a2 );
double D2R( double D );
double R2D( double R );
diff --git a/app/bin/version.h b/app/bin/version.h
index 3441687..2b2e6ff 100644
--- a/app/bin/version.h
+++ b/app/bin/version.h
@@ -1,4 +1,5 @@
-/* $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/version.h,v 1.9 2008-01-29 04:10:23 tshead Exp $
+/** \file version.h
+ *
*/
/* XTrkCad - Model Railroad CAD
@@ -19,6 +20,8 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
+#ifndef HAVE_VERSION_H
+#define HAVE_VERSION_H
#ifdef XTRKCAD_CMAKE_BUILD
#include "xtrkcad-config.h"
@@ -36,4 +39,4 @@
#define MINPARAMVERSION (1)
#endif
-
+#endif //HAVE_VERSION_H