From d1a8285f818eb7e5c3d6a05709ea21a808490b8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Mon, 19 Mar 2018 19:55:58 +0100 Subject: New upstream version 5.1.0 --- app/bin/CMakeLists.txt | 30 +- app/bin/ChangeLog | 495 ----- app/bin/acclkeys.h | 3 + app/bin/appdefaults.c | 435 ++++ app/bin/bdf2xtp.c | 10 +- app/bin/bitmaps/bezier.xpm | 23 + app/bin/bitmaps/dbezier.xpm | 22 + app/bin/bitmaps/ecornu.xpm | 23 + app/bin/bitmaps/eltsharp.xpm | 21 + app/bin/bitmaps/helix.xpm | 21 - app/bin/bitmaps/map.xpm | 22 + app/bin/bitmaps/pan.xpm | 22 + app/bin/cbezier.c | 1153 ++++++++++ app/bin/cbezier.h | 53 + app/bin/cblock.c | 31 +- app/bin/ccontrol.c | 25 +- app/bin/ccornu.c | 1240 +++++++++++ app/bin/ccornu.h | 23 + app/bin/ccurve.c | 241 +- app/bin/ccurve.h | 18 +- app/bin/cdraw.c | 204 +- app/bin/celev.c | 18 +- app/bin/cgroup.c | 52 +- app/bin/chndldto.c | 18 +- app/bin/chotbar.c | 47 +- app/bin/cjoin.c | 105 +- app/bin/cjoin.h | 15 +- app/bin/cmisc.c | 774 ++++--- app/bin/cmodify.c | 153 +- app/bin/cnote.c | 638 +++--- app/bin/common.h | 23 +- app/bin/compound.c | 253 ++- app/bin/compound.h | 10 +- app/bin/cparalle.c | 15 +- app/bin/cprint.c | 101 +- app/bin/cprofile.c | 18 +- app/bin/cpull.c | 102 +- app/bin/cruler.c | 7 +- app/bin/cselect.c | 261 ++- app/bin/cselect.h | 12 +- app/bin/csensor.c | 27 +- app/bin/csignal.c | 38 +- app/bin/csnap.c | 16 +- app/bin/csplit.c | 22 +- app/bin/cstraigh.c | 80 +- app/bin/cstraigh.h | 12 +- app/bin/cstruct.c | 28 +- app/bin/cswitchmotor.c | 23 +- app/bin/ctext.c | 94 +- app/bin/ctodesgn.c | 20 +- app/bin/ctrain.c | 4680 +++++++++++++++++++++------------------ app/bin/ctrain.h | 10 +- app/bin/cturnout.c | 168 +- app/bin/cturntbl.c | 64 +- app/bin/cundo.c | 17 +- app/bin/cundo.h | 12 +- app/bin/custom.c | 16 +- app/bin/custom.h | 18 +- app/bin/dbench.c | 9 +- app/bin/dbitmap.c | 29 +- app/bin/dcar.c | 45 +- app/bin/dcmpnd.c | 14 +- app/bin/dcontmgm.c | 13 +- app/bin/dcustmgm.c | 25 +- app/bin/dease.c | 110 +- app/bin/denum.c | 31 +- app/bin/dlayer.c | 1538 +++++++------ app/bin/doption.c | 221 +- app/bin/dpricels.c | 21 +- app/bin/dprmfile.c | 65 +- app/bin/draw.c | 684 ++++-- app/bin/draw.h | 14 +- app/bin/drawgeom.c | 306 ++- app/bin/drawgeom.h | 20 +- app/bin/dxfformat.c | 2 +- app/bin/dxfformat.h | 24 + app/bin/dxfoutput.c | 16 +- app/bin/elev.c | 15 +- app/bin/fileio.c | 229 +- app/bin/fileio.h | 19 +- app/bin/i18n.c | 7 +- app/bin/layout.c | 375 ++++ app/bin/layout.h | 56 + app/bin/lprintf.c | 16 +- app/bin/macro.c | 123 +- app/bin/misc.c | 375 ++-- app/bin/misc.h | 30 +- app/bin/misc2.c | 98 +- app/bin/misc2.h | 30 +- app/bin/param.c | 391 ++-- app/bin/param.h | 13 +- app/bin/paths.c | 220 ++ app/bin/paths.h | 32 + app/bin/shrtpath.c | 4 +- app/bin/shrtpath.h | 10 +- app/bin/smalldlg.c | 32 +- app/bin/smalldlg.h | 4 +- app/bin/tbezier.c | 1629 ++++++++++++++ app/bin/tbezier.h | 57 + app/bin/tcornu.c | 1321 +++++++++++ app/bin/tcornu.h | 66 + app/bin/tcurve.c | 170 +- app/bin/tease.c | 36 +- app/bin/track.c | 270 +-- app/bin/track.h | 161 +- app/bin/trackx.h | 9 +- app/bin/trkseg.c | 599 ++++- app/bin/tstraigh.c | 55 +- app/bin/unittest/CMakeLists.txt | 21 +- app/bin/unittest/defaultstest.c | 110 + app/bin/unittest/pathstest.c | 121 + app/bin/uthash.h | 960 ++++++++ app/bin/utility.c | 16 + app/bin/utility.h | 8 +- app/bin/version.h | 7 +- 115 files changed, 16642 insertions(+), 6322 deletions(-) delete mode 100644 app/bin/ChangeLog create mode 100644 app/bin/appdefaults.c create mode 100644 app/bin/bitmaps/bezier.xpm create mode 100644 app/bin/bitmaps/dbezier.xpm create mode 100644 app/bin/bitmaps/ecornu.xpm create mode 100644 app/bin/bitmaps/eltsharp.xpm delete mode 100644 app/bin/bitmaps/helix.xpm create mode 100644 app/bin/bitmaps/map.xpm create mode 100644 app/bin/bitmaps/pan.xpm create mode 100644 app/bin/cbezier.c create mode 100644 app/bin/cbezier.h create mode 100644 app/bin/ccornu.c create mode 100644 app/bin/ccornu.h create mode 100644 app/bin/layout.c create mode 100644 app/bin/layout.h create mode 100644 app/bin/paths.c create mode 100644 app/bin/paths.h create mode 100644 app/bin/tbezier.c create mode 100644 app/bin/tbezier.h create mode 100644 app/bin/tcornu.c create mode 100644 app/bin/tcornu.h create mode 100644 app/bin/unittest/defaultstest.c create mode 100644 app/bin/unittest/pathstest.c create mode 100644 app/bin/uthash.h (limited to 'app/bin') 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 - custom.c, misc.c, param.c, param.h, smalldlg.c smalldlg.h: - New 'About' dialog - -Sep 16, 2009 - FIX: Martin Fischer - cblock.c, cswitchmotor.c: remove some unused locals - -Aug 16, 2009 - FIX: Martin Fischer - 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 - custom.c cturnout.c: Code cleanup - -Jul 30, 2009 - FIX: Martin Fischer - dcustmgm.c: set locale when exporting to parameter - -Jul 24, 2009 - ENH: Martin Fischer - misc.c: add command line option for configuration - file selection - -Jul 10, 2009 - ENH: Martin Fischer - misc.c: use getopt() for access to command line arguments - -Jul 09, 2009 - FIX: Martin Fischer - custom.c, misc.c, denum.c, doption.c: remove some - obsolete flags - -Jul 08, 2009 - FIX: Martin Fischer - 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 - track.c: Bug fix #2816663: Block gaps are not printed - -Jul 01, 2009 - FIX: Martin Fischer - CMakeList.txt: remove dependency from unneeded cmisc2.c - -Jun 26, 2009 - FIX: Martin Fischer - custom.c: correct handling of export file extensions - -Jun 20, 2009 - FIX: Martin Fischer - ctodesgn.c: convert roadbed width as necessary - (Robert Myers) - -Jun 15, 2009 - FIX: Martin Fischer - tcurve.c, drawgeom.c: fix variable initialization - problems. - -Jun 14, 2009 - FIX: Martin Fischer - macro.c: make demos work again with new dialogs - -Jun 13, 2009 - FIX: Martin Fischer - dlayer.c: "changed" state of layout is updated with - layer changes. (DonDASA) - -Version 4.0.3 -============= - -Jun 10, 2009 - FIX: Martin Fischer - ctodesgn.c: remove unneeded local variables - -Jun 08, 2009 - FIX: Martin Fischer - draw.c: no tooltip for the main drawing area - -Jun 06, 2009 - FIX: Martin Fischer - draw.c: fix compiler warning - -May 26, 2009 - ENH: Martin Fischer - ctrain.c: update icons - -May 25, 2009 - ENH: Martin Fischer - ctrain.c: change default for train running to 'Go' - beautify throttle slider - -May 25, 2009 - ENH: Martin Fischer - 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 - macro.c, misc.c: more message boxes with icon - -May 08, 2009 - ENH: Martin Fischer - fileio.c, misc.c: use new message box with icon - - -Oct 11, 2008 - FIX: Martin Fischer - draw.h: fixed prototype for DoZoom as suggested by - Stefan Gruenendahl - -Sep 05, 2008 - ENH: Martin Fischer - misc.c, cselect.c, track.c: create full partlist - when no track is selected - -Sep 01, 2008 - ENH: Martin Fischer - misc.c, common.h: add new toolbar icons for file ops - -Aug 29, 2008 - FIX: Martin Fischer - draw.c: fixed bug #1821257: no zoom larger than 1:1 - -Jul 11, 2008 - FIX: Martin Fischer - misc.c: update map on loading initial layout - -Jul 10, 2008 - ENH: Martin Fischer - misc.c, misc.h, draw.c: allow user to cancel close request - -Jun 04, 2008 - FIX: Martin Fischer - cselect.c: Rescale dialog wasn't updated correctly - misc2.c: fixed bug when rescale same piece several times - -Jun 03. 2008 - FIX: Martin Fischer - CMakeLists.txt: find getext on Win32 - -Jun 03, 2008 - FIX: Martin Fischer - 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 - 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 - CMakeLists.txt: Fix missing icon problem for Windows exe - -Feb 04, 2008 - FIX: Mikko Nissinen - misc.c: Fixed an internationalization bug in MenuPlayback. - -Feb 04, 2008 - FIX: Mikko Nissinen - cnote.c: Minor fix to internationalization. - -Feb 03, 2008 - ENH: Martin Fischer - cprint.c: printout of date is correctly localized now. - -Feb 03, 2008 - ENH: Martin Fischer - 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 - misc.c: Product name changed in font selection dialog. - -Jan 28, 2008 - FIX: Mikko Nissinen - common.c: Dynamically allocate and form some global translatable - strings. - -Jan 27, 2008 - FIX: Mikko Nissinen - macro.c: String XTrkCad changed to XTrackCAD. - -Jan 27, 2008 - FIX: Martin Fischer - misc.c, fileio.c: fixed product name - -Jan 27, 2008 - FIX: Martin Fischer - dcar.c: corrected problem in CarPartWrite() - -Jan 25, 2008 - FIX: Martin Fischer - custom.c, version.h: Changed product name to XTrackCAD and version - to 4.1.0b1 - -Jan 23, 2008 - FIX: Mikko Nissinen - 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 - fileio.c: increase precision for roomsize to 6 digits . - -Jan 23, 2008 - FIX: Mikko Nissinen - param.c: ParamPlayback(): If parameter type is PD_FLOAT, then use the - locale "C" during atof(). - -Jan 22, 2008 - ENH: Mikko Nissinen - misc.c: Save user locale when program initializes. - macro.c: Gettext support added. - -Jan 21, 2008 - ENH: Mikko Nissinen - 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 - dcar.c: CarInvSaveText() Car list text file is now created to - selected path instead of current working directory. - -Jan 15, 2008 - IMPROVEMENT: Mikko Nissinen - 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 - fileio.c: fixed segfault when locale is saved - -Dec. 12. 2007 - FIX: Martin Fischer - 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 - xtrkcad.ico: create a new color icon - -Dec. 01, 2007 - BUGFIX: Martin Fischer - 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 - dlayer.c: Shortened button text to 'Defaults' - -Oct 10, 2007 - BUGFIX: Martin Fischer - csnap.c cprint.c, misc.c: Accelerator keys for Print and - Snap Grid Dialog work again. - -Oct 10, 2007 - BUGFIX: Martin Fischer - acclkeys.h: Revert and Redo used the same accelerator key. - Fixed, Revert doesn't have an acclerator now. - -Sep 28, 2007 - IMPROVEMENT: Martin Fischer - 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 - 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 - misc.c: XTrkCad now has a real splash window during startup - -Jul 22, 2007 - IMPROVEMENT: Martin Fischer - draw.c: the mouse wheel can be used for zooming in and out - -Jun 27, 2007 - IMPROVEMENT: Martin Fischer - dlayer.c: some cleanup and modified layer buttons. Also all - layer buttons where moved to the bitmaps directory. - -Jun 16, 2007 - IMPROVEMENT: Martin Fischer - fileio.c: default directory for storing files is the user's - home directory now. - -Jun 15, 2007 - BUGFIX: Martin Fischer - dlayer.c: fixed function prototype for Windows compile - -Jun 15, 2007 - IMPROVEMENT: Martin Fischer - dlayer.c: layer buttons now are push buttons that are in - 'pressed' state when layer is visible. - -Jun 15, 2007 - IMPROVEMENT: Martin Fischer - 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 - draw.c misc.c: disable zoom up and zoom down buttons when - end of list is reached - -Apr 30, 2007 - - IMPROVEMENT: Martin Fischer - 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 - draw.c: changed layout of status bar to include labels. - Part count is no longer shown. - -Feb 23, 2007 - BUGFIX: Martin Fischer - cmisc.c, cselect.c rescale / resize works again. UI change to - allow changing scale and gauge independently - -Feb 16, 2007 - - IMPROVEMENT: Martin Fischer - Recently used files list is only updated after successful load - - -Version 4.0.1 -============= - -May 26th, 2006 - - IMPROVEMENT: Martin Fischer - Visual Studio C++ 2005 Express is now supported under Windows - -Mar 26th, 2006 - - IMPROVEMENT: Martin Fischer - 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 - misc2.c prevent warning in DoSetScaleDesc - -Mar 02nd, 2006 - - IMPROVEMENT: Martin Fischer - cturnout.c Improvements to the select turnout dialog, new turnout is drawn - blue - -Feb. 26th, 2006 - - NEW FEATURE: Martin Fischer - misc.c, cselect.c, 'Select orphaned track' command added to set all - unconnected track pieces. - -Feb, 22nd, 2006 - - NEW FEATURE: Martin Fischer - misc.c, misc2.c, doption.c Scale and gauge are two independant seetings - now. - - NEW FEATURE: Martin Fischer - 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 - misc.c Add new function 'Revert' to main menu, implemented in ChkRevert - acclkeys.h Added Ctrl-r as accelerator for 'Revert' - - IMPROVEMENT: Martin Fischer - cselect.c Optimized performance for 'Select Connected' operation - - IMPROVEMENT: Martin Fischer - bllnhelp.c: removed inconsistencies in usage of 'track' and 'object' - - IMPROVEMENT: Martin Fischer - misc.c: moved 'Join' command to 'Change' menu - - BUGFIX: Martin Fischer - 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 +#include +#include +#include +#include + +#ifdef WINDOWS +#include +#include +#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 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; ibezSegs.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>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 -#include "track.h" -#include "trackx.h" +#include +#include + #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 -#include "track.h" -#include "trackx.h" +#include + #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; ibezSegs.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],¶ms)) 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=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;ir_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 +#include + #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 +#include +#include + #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 +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 +#include -#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 -#include "track.h" +#include +#include + #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= 1 && logTable(log_group).level > log_group ) { for ( pinx=0; pinxtrk), 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 + #include "ccurve.h" -#include "cstraigh.h" #include "cjoin.h" #include "compound.h" -#include +#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 -#include "track.h" -#include "compound.h" - #include +#include + +#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= 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 + #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; inxtype].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; inxtype].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; inxtype != 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; inxcontrol0, 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; inxtype != 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; inxcontrol0, 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 + #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 -#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, ¬eTextData } }; + { PD_TEXT, NULL, "text", PDO_DLGRESIZE, ¬eTextData } +}; 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( ¬ePG, 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(¬ePG, 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"), ¬eData.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"), ¬eData.pos }, + /*LY*/ { DESC_LAYER, N_("Layer"), ¬eData.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( ¬ePG ); - AddMenuButton( menu, CmdNote, "cmdNote", _("Note"), wIconCreatePixMap(cnote_xpm), LEVEL0_50, IC_POPUP2, ACCL_NOTE, NULL ); + ParamRegister(¬ePG); + 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( ¬eCmds ); + note_bm = wDrawBitMapCreate(mainD.d, note_width, note_width, 8, 8, note_bits); + T_NOTE = InitObject(¬eCmds); } 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 + #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 -#include "track.h" -#include "compound.h" -#include "shrtpath.h" +#include +#include + + +#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 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;isegCnt; 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 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 + #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 #include #include -#include "track.h" -#include "i18n.h" +#include +#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 -#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 -#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; ep10 && (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 +#include + #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>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 -#include "track.h" -#include "trackx.h" +#include + #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 -#include "track.h" -#include "trackx.h" +#include + #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 +#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 -#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 -#include "track.h" +#include +#include +#include + #include "compound.h" +#include "cundo.h" +#include "custom.h" +#include "fileio.h" #include "i18n.h" - -#include +#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 -#include "track.h" -#include "trackx.h" +#include + #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 - #include -#include "track.h" +#include +#include +#include + #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 #endif #include +#include +#include #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; inxtrain == 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 ) - 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) { + 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<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<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 -#include "track.h" +#include +#include +#include + #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 +#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; inxsegCnt; 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; iendCnt; 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 +#include + #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, ¢er, &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; *eppos, 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 #include #include +#include + +#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 #include -#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 +#include +#include +#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 + +#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 #endif +#include #include - +#include #include +#include -#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 -#include "track.h" +#include + #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 -#include "i18n.h" +#include #ifdef WINDOWS #include @@ -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 #include -#include "i18n.h" +#include #ifdef WINDOWS #include #define F_OK (0) #define W_OK (2) #define access _access +#else +#include #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 -#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 +#include #include -#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 - -#include "track.h" -#include "i18n.h" - +#include #include +#include +#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_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;inx0?'+':'-', 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= 0 && inx < NUM_LAYERS ) - layers[inx].objCount++; - } + int inx; + track_p trk; + + for (inx=0; 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 (layerCurrent0) { + 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_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_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 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 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; inx0; - 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 -#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, ¢erDrawMode, "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 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 -#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 -#include "track.h" -#include "i18n.h" - +#include #include +#include +#include -#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 #include +#include + #ifdef HAVE_MALLOC_C #include #endif @@ -40,12 +40,16 @@ #include #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= 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_x1) 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 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 #include -#include "track.h" +#include + #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 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 #include - #if _MSC_VER >=1400 - #define strdup _strdup - #endif #else #include #endif @@ -36,11 +33,18 @@ #include #include #include -#include "track.h" -#include "i18n.h" + #include +#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 -#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 #include - #if _MSC_VER >=1400 - #define strdup _strdup - #endif + //#if _MSC_VER >=1400 + // #define strdup _strdup + //#endif #else #endif #include @@ -48,14 +48,23 @@ #include -#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(¶mFileName, dirName, fileName, NULL); } else { - strcpy( paramFileName, fileName ); + MakeFullpath(¶mFileName, 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(¶mFileName, dirName, fileName, NULL); } else { - strcpy( paramFileName, fileName ); + MakeFullpath(¶mFileName, 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, ¶mLine[7], TITLEMAXLEN ); - Title1[ TITLEMAXLEN - 1 ] = '\0'; - /*wStringSetValue( title1PD.control, Title1 );*/ + SetLayoutTitle(paramLine + 7); } else if (strncmp( paramLine, "TITLE2 ", 7 ) == 0) { - strncpy( Title2, ¶mLine[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 + +#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 #include +#include + +#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 +#include + +#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 #include #include +#include #include #ifndef WINDOWS #include @@ -30,8 +31,14 @@ #include #include #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 #include #include #ifndef WINDOWS @@ -47,14 +48,20 @@ #include -#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 #endif +#include #include - #include -#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 +#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 ( inxwidth) { + 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 + #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 -#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 -#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 +#include #include #ifndef WINDOWS #include @@ -45,12 +46,17 @@ #include #include #include -#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<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) /** +#include +#include +#include + +#ifdef WINDOWS +#include +#endif + +#include +#include +#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 + #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 #include -#if _MSC_VER >1300 - #define strdup _strdup -#endif #else #include #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;itype == 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],¶ms); + 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],¶ms); + 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],¶ms); + 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],¶ms); + bezData.radius[0] = params.arcR; + bezData.center[0] = params.arcP; + GetTrackParams(PARAMS_CORNU,trk,xx->bezierData.pos[3],¶ms); + 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;ibezierData.arcSegs.cnt;i++) { + segProcData.distance.pos1 = * p; + SegProc(SEGPROC_DISTANCE,&DYNARR_N(trkSeg_t,xx->bezierData.arcSegs,i),&segProcData); + if (segProcData.distance.ddbezierData.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->scalebezierData.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;ibezierData.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 && inxbezierData.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;ibezierData.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 && inxbezSegs.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;ibezSegs.cnt;i++) { + segProcData.distance.pos1 = p0; + SegProc(SEGPROC_DISTANCE,&(DYNARR_N(trkSeg_t,segPtr->bezSegs,i)),&segProcData); + d = segProcData.distance.dd; + if (ddistance.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;ibezSegs.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= 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;icornuData.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;icornuData.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.ddbezierData.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->scalecornuData.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;icornuData.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 && inxcornuData.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;icornuData.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;icornuData.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 +#include + #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, ¶ms->arcP, ¶ms->arcR); GetCurveAngles( ¶ms->arcA0, ¶ms->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 -#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 #include #include #include #include -#include "track.h" +#include + #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; iendCnt; 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 -#include -#include -#ifndef WINDOWS -#include -#endif -#include -#ifdef HAVE_MALLOC_H -#include -#endif -#include - -#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,17 +192,29 @@ 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; @@ -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 #include #include +#include #include #include "track.h" +#include +#include +#include + +#include + #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->xx; + hi->y = hi->yy; + } + 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; inxu.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;ibezSegs.cnt;i++) { + segProcData2.distance.pos1 = pt; + SegProc(SEGPROC_DISTANCE,&DYNARR_N(trkSeg_t,segPtr->bezSegs,i),&segProcData2); + if (segProcData2.distance.ddu.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) 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; inxu.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 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 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;jbezSegs.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;jbezSegs.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; ju.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;ju.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;icnt;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 + #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 +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include + +#include +#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 /* memcmp,strlen */ +#include /* ptrdiff_t */ +#include /* 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 +#elif defined(__WATCOMC__) +#include +#else +typedef unsigned int uint32_t; +typedef unsigned char uint8_t; +#endif +#else +#include +#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 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 -- cgit v1.2.3