From 6c61179fe09af2f5366d50f10aadbf5f83438087 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Tue, 10 Jul 2007 19:20:20 +0000 Subject: [PATCH] Initial revision --- Build | 67 ++ dejavu-12.font | 1341 ++++++++++++++++++++++++++ dejavu-12.png | Bin 0 -> 8990 bytes dejavu-20.font | 1339 +++++++++++++++++++++++++ dejavu-20.png | Bin 0 -> 16467 bytes digitalreadout-16.font | 669 +++++++++++++ digitalreadout-16.png | Bin 0 -> 3312 bytes engineer.res | 226 +++++ gui.blend | Bin 0 -> 512024 bytes gui.png | Bin 0 -> 3432 bytes source/3d/layout.cpp | 132 +++ source/3d/layout.h | 32 + source/3d/misc.h | 15 + source/3d/track.cpp | 253 +++++ source/3d/track.h | 40 + source/designer/designer.cpp | 667 +++++++++++++ source/designer/designer.h | 90 ++ source/designer/input.cpp | 87 ++ source/designer/input.h | 26 + source/designer/manipulator.cpp | 479 +++++++++ source/designer/manipulator.h | 89 ++ source/designer/measure.cpp | 120 +++ source/designer/measure.h | 42 + source/designer/selection.cpp | 89 ++ source/designer/selection.h | 28 + source/engineer/engineer.cpp | 485 ++++++++++ source/engineer/engineer.h | 65 ++ source/engineer/guicomponent.h | 44 + source/engineer/guiresources.h | 4 + source/engineer/mainpanel.cpp | 70 ++ source/engineer/mainpanel.h | 24 + source/engineer/trainpanel.cpp | 36 + source/engineer/trainpanel.h | 25 + source/libmarklin/catalogue.cpp | 63 ++ source/libmarklin/catalogue.h | 35 + source/libmarklin/command.h | 26 + source/libmarklin/constants.h | 52 + source/libmarklin/control.cpp | 329 +++++++ source/libmarklin/control.h | 59 ++ source/libmarklin/geometry.h | 18 + source/libmarklin/layout.cpp | 134 +++ source/libmarklin/layout.h | 49 + source/libmarklin/locomotive.cpp | 116 +++ source/libmarklin/locomotive.h | 39 + source/libmarklin/route.cpp | 7 + source/libmarklin/route.h | 28 + source/libmarklin/section.cpp | 183 ++++ source/libmarklin/section.h | 51 + source/libmarklin/sensor.cpp | 24 + source/libmarklin/sensor.h | 32 + source/libmarklin/track.cpp | 374 +++++++ source/libmarklin/track.h | 121 +++ source/libmarklin/trafficmanager.cpp | 71 ++ source/libmarklin/trafficmanager.h | 31 + source/libmarklin/train.cpp | 121 +++ source/libmarklin/train.h | 45 + source/libmarklin/turnout.cpp | 77 ++ source/libmarklin/turnout.h | 38 + source/shoppinglist/main.cpp | 138 +++ tracks.dat | 425 ++++++++ 60 files changed, 9270 insertions(+) create mode 100644 Build create mode 100644 dejavu-12.font create mode 100644 dejavu-12.png create mode 100644 dejavu-20.font create mode 100644 dejavu-20.png create mode 100644 digitalreadout-16.font create mode 100644 digitalreadout-16.png create mode 100644 engineer.res create mode 100644 gui.blend create mode 100644 gui.png create mode 100644 source/3d/layout.cpp create mode 100644 source/3d/layout.h create mode 100644 source/3d/misc.h create mode 100644 source/3d/track.cpp create mode 100644 source/3d/track.h create mode 100644 source/designer/designer.cpp create mode 100644 source/designer/designer.h create mode 100644 source/designer/input.cpp create mode 100644 source/designer/input.h create mode 100644 source/designer/manipulator.cpp create mode 100644 source/designer/manipulator.h create mode 100644 source/designer/measure.cpp create mode 100644 source/designer/measure.h create mode 100644 source/designer/selection.cpp create mode 100644 source/designer/selection.h create mode 100644 source/engineer/engineer.cpp create mode 100644 source/engineer/engineer.h create mode 100644 source/engineer/guicomponent.h create mode 100644 source/engineer/guiresources.h create mode 100644 source/engineer/mainpanel.cpp create mode 100644 source/engineer/mainpanel.h create mode 100644 source/engineer/trainpanel.cpp create mode 100644 source/engineer/trainpanel.h create mode 100644 source/libmarklin/catalogue.cpp create mode 100644 source/libmarklin/catalogue.h create mode 100644 source/libmarklin/command.h create mode 100644 source/libmarklin/constants.h create mode 100644 source/libmarklin/control.cpp create mode 100644 source/libmarklin/control.h create mode 100644 source/libmarklin/geometry.h create mode 100644 source/libmarklin/layout.cpp create mode 100644 source/libmarklin/layout.h create mode 100644 source/libmarklin/locomotive.cpp create mode 100644 source/libmarklin/locomotive.h create mode 100644 source/libmarklin/route.cpp create mode 100644 source/libmarklin/route.h create mode 100644 source/libmarklin/section.cpp create mode 100644 source/libmarklin/section.h create mode 100644 source/libmarklin/sensor.cpp create mode 100644 source/libmarklin/sensor.h create mode 100644 source/libmarklin/track.cpp create mode 100644 source/libmarklin/track.h create mode 100644 source/libmarklin/trafficmanager.cpp create mode 100644 source/libmarklin/trafficmanager.h create mode 100644 source/libmarklin/train.cpp create mode 100644 source/libmarklin/train.h create mode 100644 source/libmarklin/turnout.cpp create mode 100644 source/libmarklin/turnout.h create mode 100644 source/shoppinglist/main.cpp create mode 100644 tracks.dat diff --git a/Build b/Build new file mode 100644 index 0000000..df11fe0 --- /dev/null +++ b/Build @@ -0,0 +1,67 @@ +package "märklin" +{ + require "mspcore"; + require "sigc++-2.0"; + + program "shoppinglist" + { + source "source/shoppinglist"; + require "mspparser"; + }; + + library "marklin" + { + source "source/libmarklin"; + require "mspparser"; + }; + + library "marklin3d" + { + source "source/3d"; + require "mspgl"; + build_info + { + incpath "source"; + library "marklin"; + libpath "."; + }; + }; + + /*program "newconsole" + { + source "source/newconsole"; + build_info + { + library "marklincontrol"; + incpath "source"; + libpath "."; + }; + };*/ + + program "designer" + { + source "source/designer"; + require "mspstrings"; + require "sdl"; + build_info + { + incpath "source"; + library "marklin3d"; + libpath "."; + }; + }; + + program "engineer" + { + source "source/engineer"; + require "mspstrings"; + require "mspgltk"; + require "sdl"; + build_info + { + incpath "source"; + library "marklin3d"; + libpath "."; + }; + }; +}; diff --git a/dejavu-12.font b/dejavu-12.font new file mode 100644 index 0000000..8d7e902 --- /dev/null +++ b/dejavu-12.font @@ -0,0 +1,1341 @@ +texture "dejavu-12.png"; +default_size 12; +ascent 1.167; +descent 0.083; +glyph 32 +{ + texcoords 0.035156 0.945312 0.035156 0.945312; + size 0.000 0.000; + offset 0.000 0.000; + advance 0.333; +}; +glyph 33 +{ + texcoords 0.101562 0.945312 0.105469 0.980469; + size 0.083 0.750; + offset 0.167 0.000; + advance 0.417; +}; +glyph 34 +{ + texcoords 0.167969 0.968750 0.179688 0.980469; + size 0.250 0.250; + offset 0.083 0.500; + advance 0.417; +}; +glyph 35 +{ + texcoords 0.230469 0.945312 0.261719 0.976562; + size 0.667 0.667; + offset 0.083 0.000; + advance 0.833; +}; +glyph 36 +{ + texcoords 0.304688 0.937500 0.328125 0.980469; + size 0.500 0.917; + offset 0.167 -0.167; + advance 0.667; +}; +glyph 37 +{ + texcoords 0.367188 0.945312 0.406250 0.980469; + size 0.833 0.750; + offset 0.000 0.000; + advance 0.917; +}; +glyph 38 +{ + texcoords 0.437500 0.945312 0.472656 0.980469; + size 0.750 0.750; + offset 0.083 0.000; + advance 0.833; +}; +glyph 39 +{ + texcoords 0.523438 0.968750 0.527344 0.980469; + size 0.083 0.250; + offset 0.083 0.500; + advance 0.250; +}; +glyph 40 +{ + texcoords 0.589844 0.941406 0.601562 0.984375; + size 0.250 0.917; + offset 0.083 -0.083; + advance 0.417; +}; +glyph 41 +{ + texcoords 0.660156 0.941406 0.671875 0.984375; + size 0.250 0.917; + offset 0.083 -0.083; + advance 0.417; +}; +glyph 42 +{ + texcoords 0.726562 0.957031 0.746094 0.980469; + size 0.417 0.500; + offset 0.083 0.250; + advance 0.500; +}; +glyph 43 +{ + texcoords 0.792969 0.945312 0.820312 0.972656; + size 0.583 0.583; + offset 0.083 0.000; + advance 0.833; +}; +glyph 44 +{ + texcoords 0.875000 0.941406 0.878906 0.953125; + size 0.083 0.250; + offset 0.083 -0.083; + advance 0.333; +}; +glyph 45 +{ + texcoords 0.941406 0.957031 0.953125 0.960938; + size 0.250 0.083; + offset 0.083 0.250; + advance 0.333; +}; +glyph 46 +{ + texcoords 0.031250 0.875000 0.035156 0.882812; + size 0.083 0.167; + offset 0.083 0.000; + advance 0.333; +}; +glyph 47 +{ + texcoords 0.097656 0.871094 0.113281 0.910156; + size 0.333 0.833; + offset 0.000 -0.083; + advance 0.333; +}; +glyph 48 +{ + texcoords 0.164062 0.875000 0.187500 0.910156; + size 0.500 0.750; + offset 0.083 0.000; + advance 0.667; +}; +glyph 49 +{ + texcoords 0.234375 0.875000 0.253906 0.910156; + size 0.417 0.750; + offset 0.083 0.000; + advance 0.667; +}; +glyph 50 +{ + texcoords 0.300781 0.875000 0.328125 0.910156; + size 0.583 0.750; + offset 0.083 0.000; + advance 0.667; +}; +glyph 51 +{ + texcoords 0.375000 0.875000 0.398438 0.910156; + size 0.500 0.750; + offset 0.083 0.000; + advance 0.667; +}; +glyph 52 +{ + texcoords 0.445312 0.875000 0.468750 0.910156; + size 0.500 0.750; + offset 0.083 0.000; + advance 0.667; +}; +glyph 53 +{ + texcoords 0.515625 0.875000 0.539062 0.910156; + size 0.500 0.750; + offset 0.083 0.000; + advance 0.667; +}; +glyph 54 +{ + texcoords 0.585938 0.875000 0.609375 0.910156; + size 0.500 0.750; + offset 0.083 0.000; + advance 0.667; +}; +glyph 55 +{ + texcoords 0.656250 0.875000 0.679688 0.910156; + size 0.500 0.750; + offset 0.083 0.000; + advance 0.667; +}; +glyph 56 +{ + texcoords 0.726562 0.875000 0.750000 0.910156; + size 0.500 0.750; + offset 0.083 0.000; + advance 0.667; +}; +glyph 57 +{ + texcoords 0.796875 0.875000 0.820312 0.910156; + size 0.500 0.750; + offset 0.083 0.000; + advance 0.667; +}; +glyph 58 +{ + texcoords 0.875000 0.875000 0.878906 0.898438; + size 0.083 0.500; + offset 0.083 0.000; + advance 0.333; +}; +glyph 59 +{ + texcoords 0.945312 0.871094 0.949219 0.898438; + size 0.083 0.583; + offset 0.083 -0.083; + advance 0.333; +}; +glyph 60 +{ + texcoords 0.019531 0.808594 0.050781 0.832031; + size 0.667 0.500; + offset 0.083 0.083; + advance 0.833; +}; +glyph 61 +{ + texcoords 0.089844 0.812500 0.121094 0.824219; + size 0.667 0.250; + offset 0.083 0.167; + advance 0.833; +}; +glyph 62 +{ + texcoords 0.160156 0.808594 0.191406 0.832031; + size 0.667 0.500; + offset 0.083 0.083; + advance 0.833; +}; +glyph 63 +{ + texcoords 0.234375 0.804688 0.253906 0.839844; + size 0.417 0.750; + offset 0.000 0.000; + advance 0.500; +}; +glyph 64 +{ + texcoords 0.292969 0.796875 0.335938 0.839844; + size 0.917 0.917; + offset 0.083 -0.167; + advance 1.083; +}; +glyph 65 +{ + texcoords 0.371094 0.804688 0.402344 0.839844; + size 0.667 0.750; + offset 0.000 0.000; + advance 0.667; +}; +glyph 66 +{ + texcoords 0.445312 0.804688 0.468750 0.839844; + size 0.500 0.750; + offset 0.083 0.000; + advance 0.667; +}; +glyph 67 +{ + texcoords 0.515625 0.804688 0.539062 0.839844; + size 0.500 0.750; + offset 0.083 0.000; + advance 0.667; +}; +glyph 68 +{ + texcoords 0.582031 0.804688 0.609375 0.839844; + size 0.583 0.750; + offset 0.083 0.000; + advance 0.750; +}; +glyph 69 +{ + texcoords 0.656250 0.804688 0.679688 0.839844; + size 0.500 0.750; + offset 0.083 0.000; + advance 0.667; +}; +glyph 70 +{ + texcoords 0.726562 0.804688 0.746094 0.839844; + size 0.417 0.750; + offset 0.083 0.000; + advance 0.583; +}; +glyph 71 +{ + texcoords 0.792969 0.804688 0.820312 0.839844; + size 0.583 0.750; + offset 0.083 0.000; + advance 0.750; +}; +glyph 72 +{ + texcoords 0.863281 0.804688 0.890625 0.839844; + size 0.583 0.750; + offset 0.083 0.000; + advance 0.750; +}; +glyph 73 +{ + texcoords 0.945312 0.804688 0.949219 0.839844; + size 0.083 0.750; + offset 0.083 0.000; + advance 0.250; +}; +glyph 74 +{ + texcoords 0.027344 0.726562 0.039062 0.769531; + size 0.250 0.917; + offset -0.083 -0.167; + advance 0.250; +}; +glyph 75 +{ + texcoords 0.089844 0.734375 0.117188 0.769531; + size 0.583 0.750; + offset 0.083 0.000; + advance 0.583; +}; +glyph 76 +{ + texcoords 0.164062 0.734375 0.183594 0.769531; + size 0.417 0.750; + offset 0.083 0.000; + advance 0.500; +}; +glyph 77 +{ + texcoords 0.230469 0.734375 0.261719 0.769531; + size 0.667 0.750; + offset 0.083 0.000; + advance 0.833; +}; +glyph 78 +{ + texcoords 0.300781 0.734375 0.328125 0.769531; + size 0.583 0.750; + offset 0.083 0.000; + advance 0.750; +}; +glyph 79 +{ + texcoords 0.371094 0.734375 0.398438 0.769531; + size 0.583 0.750; + offset 0.083 0.000; + advance 0.750; +}; +glyph 80 +{ + texcoords 0.445312 0.734375 0.468750 0.769531; + size 0.500 0.750; + offset 0.083 0.000; + advance 0.667; +}; +glyph 81 +{ + texcoords 0.511719 0.726562 0.539062 0.769531; + size 0.583 0.917; + offset 0.083 -0.167; + advance 0.750; +}; +glyph 82 +{ + texcoords 0.582031 0.734375 0.609375 0.769531; + size 0.583 0.750; + offset 0.083 0.000; + advance 0.667; +}; +glyph 83 +{ + texcoords 0.656250 0.734375 0.679688 0.769531; + size 0.500 0.750; + offset 0.083 0.000; + advance 0.667; +}; +glyph 84 +{ + texcoords 0.722656 0.734375 0.750000 0.769531; + size 0.583 0.750; + offset 0.000 0.000; + advance 0.583; +}; +glyph 85 +{ + texcoords 0.792969 0.734375 0.820312 0.769531; + size 0.583 0.750; + offset 0.083 0.000; + advance 0.750; +}; +glyph 86 +{ + texcoords 0.859375 0.734375 0.898438 0.769531; + size 0.833 0.750; + offset -0.083 0.000; + advance 0.667; +}; +glyph 87 +{ + texcoords 0.925781 0.734375 0.968750 0.769531; + size 0.917 0.750; + offset 0.000 0.000; + advance 0.917; +}; +glyph 88 +{ + texcoords 0.019531 0.664062 0.046875 0.699219; + size 0.583 0.750; + offset 0.000 0.000; + advance 0.583; +}; +glyph 89 +{ + texcoords 0.089844 0.664062 0.117188 0.699219; + size 0.583 0.750; + offset 0.000 0.000; + advance 0.583; +}; +glyph 90 +{ + texcoords 0.160156 0.664062 0.187500 0.699219; + size 0.583 0.750; + offset 0.083 0.000; + advance 0.750; +}; +glyph 91 +{ + texcoords 0.242188 0.656250 0.250000 0.699219; + size 0.167 0.917; + offset 0.167 -0.167; + advance 0.417; +}; +glyph 92 +{ + texcoords 0.308594 0.660156 0.324219 0.699219; + size 0.333 0.833; + offset 0.000 -0.083; + advance 0.333; +}; +glyph 93 +{ + texcoords 0.382812 0.656250 0.390625 0.699219; + size 0.167 0.917; + offset 0.083 -0.167; + advance 0.417; +}; +glyph 94 +{ + texcoords 0.441406 0.687500 0.472656 0.699219; + size 0.667 0.250; + offset 0.083 0.500; + advance 0.833; +}; +glyph 95 +{ + texcoords 0.515625 0.652344 0.539062 0.656250; + size 0.500 0.083; + offset 0.000 -0.250; + advance 0.500; +}; +glyph 96 +{ + texcoords 0.589844 0.695312 0.601562 0.703125; + size 0.250 0.167; + offset 0.083 0.667; + advance 0.500; +}; +glyph 97 +{ + texcoords 0.656250 0.664062 0.679688 0.691406; + size 0.500 0.583; + offset 0.083 0.000; + advance 0.667; +}; +glyph 98 +{ + texcoords 0.726562 0.664062 0.750000 0.703125; + size 0.500 0.833; + offset 0.083 0.000; + advance 0.667; +}; +glyph 99 +{ + texcoords 0.796875 0.664062 0.816406 0.691406; + size 0.417 0.583; + offset 0.083 0.000; + advance 0.583; +}; +glyph 100 +{ + texcoords 0.867188 0.664062 0.890625 0.703125; + size 0.500 0.833; + offset 0.083 0.000; + advance 0.667; +}; +glyph 101 +{ + texcoords 0.937500 0.664062 0.960938 0.691406; + size 0.500 0.583; + offset 0.083 0.000; + advance 0.667; +}; +glyph 102 +{ + texcoords 0.027344 0.593750 0.042969 0.632812; + size 0.333 0.833; + offset 0.000 0.000; + advance 0.333; +}; +glyph 103 +{ + texcoords 0.093750 0.582031 0.117188 0.621094; + size 0.500 0.833; + offset 0.083 -0.250; + advance 0.667; +}; +glyph 104 +{ + texcoords 0.164062 0.593750 0.187500 0.632812; + size 0.500 0.833; + offset 0.083 0.000; + advance 0.667; +}; +glyph 105 +{ + texcoords 0.242188 0.593750 0.246094 0.628906; + size 0.083 0.750; + offset 0.083 0.000; + advance 0.250; +}; +glyph 106 +{ + texcoords 0.312500 0.582031 0.320312 0.628906; + size 0.167 1.000; + offset 0.000 -0.250; + advance 0.250; +}; +glyph 107 +{ + texcoords 0.375000 0.593750 0.398438 0.632812; + size 0.500 0.833; + offset 0.083 0.000; + advance 0.583; +}; +glyph 108 +{ + texcoords 0.453125 0.593750 0.457031 0.632812; + size 0.083 0.833; + offset 0.083 0.000; + advance 0.250; +}; +glyph 109 +{ + texcoords 0.507812 0.593750 0.542969 0.621094; + size 0.750 0.583; + offset 0.083 0.000; + advance 0.917; +}; +glyph 110 +{ + texcoords 0.585938 0.593750 0.609375 0.621094; + size 0.500 0.583; + offset 0.083 0.000; + advance 0.667; +}; +glyph 111 +{ + texcoords 0.656250 0.593750 0.679688 0.621094; + size 0.500 0.583; + offset 0.083 0.000; + advance 0.667; +}; +glyph 112 +{ + texcoords 0.726562 0.582031 0.750000 0.621094; + size 0.500 0.833; + offset 0.083 -0.250; + advance 0.667; +}; +glyph 113 +{ + texcoords 0.796875 0.582031 0.820312 0.621094; + size 0.500 0.833; + offset 0.083 -0.250; + advance 0.667; +}; +glyph 114 +{ + texcoords 0.871094 0.593750 0.886719 0.621094; + size 0.333 0.583; + offset 0.083 0.000; + advance 0.417; +}; +glyph 115 +{ + texcoords 0.937500 0.593750 0.960938 0.621094; + size 0.500 0.583; + offset 0.083 0.000; + advance 0.583; +}; +glyph 116 +{ + texcoords 0.027344 0.523438 0.042969 0.558594; + size 0.333 0.750; + offset 0.000 0.000; + advance 0.417; +}; +glyph 117 +{ + texcoords 0.093750 0.523438 0.117188 0.550781; + size 0.500 0.583; + offset 0.083 0.000; + advance 0.667; +}; +glyph 118 +{ + texcoords 0.160156 0.523438 0.191406 0.550781; + size 0.667 0.583; + offset -0.083 0.000; + advance 0.500; +}; +glyph 119 +{ + texcoords 0.226562 0.523438 0.261719 0.550781; + size 0.750 0.583; + offset 0.000 0.000; + advance 0.750; +}; +glyph 120 +{ + texcoords 0.304688 0.523438 0.328125 0.550781; + size 0.500 0.583; + offset 0.000 0.000; + advance 0.500; +}; +glyph 121 +{ + texcoords 0.371094 0.511719 0.402344 0.550781; + size 0.667 0.833; + offset -0.083 -0.250; + advance 0.500; +}; +glyph 122 +{ + texcoords 0.445312 0.523438 0.464844 0.550781; + size 0.417 0.583; + offset 0.000 0.000; + advance 0.417; +}; +glyph 123 +{ + texcoords 0.515625 0.515625 0.535156 0.558594; + size 0.417 0.917; + offset 0.167 -0.167; + advance 0.667; +}; +glyph 124 +{ + texcoords 0.593750 0.511719 0.597656 0.558594; + size 0.083 1.000; + offset 0.167 -0.250; + advance 0.333; +}; +glyph 125 +{ + texcoords 0.656250 0.515625 0.675781 0.558594; + size 0.417 0.917; + offset 0.083 -0.167; + advance 0.667; +}; +glyph 126 +{ + texcoords 0.722656 0.535156 0.753906 0.546875; + size 0.667 0.250; + offset 0.083 0.250; + advance 0.833; +}; +glyph 160 +{ + texcoords 0.808594 0.523438 0.808594 0.523438; + size 0.000 0.000; + offset 0.000 0.000; + advance 0.333; +}; +glyph 161 +{ + texcoords 0.875000 0.523438 0.878906 0.558594; + size 0.083 0.750; + offset 0.167 0.000; + advance 0.417; +}; +glyph 162 +{ + texcoords 0.937500 0.515625 0.957031 0.558594; + size 0.417 0.917; + offset 0.083 -0.167; + advance 0.667; +}; +glyph 163 +{ + texcoords 0.023438 0.453125 0.042969 0.488281; + size 0.417 0.750; + offset 0.167 0.000; + advance 0.750; +}; +glyph 164 +{ + texcoords 0.089844 0.453125 0.117188 0.484375; + size 0.583 0.667; + offset 0.000 0.000; + advance 0.667; +}; +glyph 165 +{ + texcoords 0.160156 0.453125 0.187500 0.488281; + size 0.583 0.750; + offset 0.083 0.000; + advance 0.750; +}; +glyph 166 +{ + texcoords 0.242188 0.445312 0.246094 0.484375; + size 0.083 0.833; + offset 0.167 -0.167; + advance 0.333; +}; +glyph 167 +{ + texcoords 0.304688 0.449219 0.324219 0.488281; + size 0.417 0.833; + offset 0.000 -0.083; + advance 0.417; +}; +glyph 168 +{ + texcoords 0.378906 0.484375 0.390625 0.488281; + size 0.250 0.083; + offset 0.167 0.667; + advance 0.583; +}; +glyph 169 +{ + texcoords 0.437500 0.453125 0.472656 0.488281; + size 0.750 0.750; + offset 0.167 0.000; + advance 1.000; +}; +glyph 170 +{ + texcoords 0.515625 0.460938 0.539062 0.488281; + size 0.500 0.583; + offset 0.083 0.167; + advance 0.500; +}; +glyph 171 +{ + texcoords 0.585938 0.457031 0.609375 0.476562; + size 0.500 0.417; + offset 0.083 0.083; + advance 0.583; +}; +glyph 172 +{ + texcoords 0.652344 0.460938 0.683594 0.472656; + size 0.667 0.250; + offset 0.083 0.167; + advance 0.833; +}; +glyph 173 +{ + texcoords 0.730469 0.464844 0.742188 0.468750; + size 0.250 0.083; + offset 0.083 0.250; + advance 0.333; +}; +glyph 174 +{ + texcoords 0.789062 0.453125 0.824219 0.488281; + size 0.750 0.750; + offset 0.167 0.000; + advance 1.000; +}; +glyph 175 +{ + texcoords 0.871094 0.484375 0.886719 0.488281; + size 0.333 0.083; + offset 0.083 0.667; + advance 0.500; +}; +glyph 176 +{ + texcoords 0.941406 0.472656 0.957031 0.488281; + size 0.333 0.333; + offset 0.083 0.417; + advance 0.500; +}; +glyph 177 +{ + texcoords 0.019531 0.382812 0.046875 0.410156; + size 0.583 0.583; + offset 0.167 0.000; + advance 0.833; +}; +glyph 178 +{ + texcoords 0.097656 0.398438 0.109375 0.417969; + size 0.250 0.417; + offset 0.083 0.333; + advance 0.417; +}; +glyph 179 +{ + texcoords 0.167969 0.398438 0.183594 0.417969; + size 0.333 0.417; + offset 0.083 0.333; + advance 0.417; +}; +glyph 180 +{ + texcoords 0.238281 0.414062 0.250000 0.421875; + size 0.250 0.167; + offset 0.167 0.667; + advance 0.500; +}; +glyph 181 +{ + texcoords 0.300781 0.371094 0.328125 0.410156; + size 0.583 0.833; + offset 0.083 -0.250; + advance 0.667; +}; +glyph 182 +{ + texcoords 0.375000 0.378906 0.394531 0.417969; + size 0.417 0.833; + offset 0.083 -0.083; + advance 0.667; +}; +glyph 183 +{ + texcoords 0.453125 0.394531 0.457031 0.402344; + size 0.083 0.167; + offset 0.083 0.250; + advance 0.333; +}; +glyph 184 +{ + texcoords 0.523438 0.375000 0.531250 0.382812; + size 0.167 0.167; + offset 0.167 -0.167; + advance 0.500; +}; +glyph 185 +{ + texcoords 0.589844 0.398438 0.601562 0.417969; + size 0.250 0.417; + offset 0.083 0.333; + advance 0.417; +}; +glyph 186 +{ + texcoords 0.656250 0.390625 0.675781 0.417969; + size 0.417 0.583; + offset 0.083 0.167; + advance 0.500; +}; +glyph 187 +{ + texcoords 0.726562 0.386719 0.750000 0.406250; + size 0.500 0.417; + offset 0.083 0.083; + advance 0.583; +}; +glyph 188 +{ + texcoords 0.785156 0.382812 0.828125 0.417969; + size 0.917 0.750; + offset 0.083 0.000; + advance 1.000; +}; +glyph 189 +{ + texcoords 0.859375 0.382812 0.898438 0.417969; + size 0.833 0.750; + offset 0.083 0.000; + advance 1.000; +}; +glyph 190 +{ + texcoords 0.925781 0.382812 0.968750 0.417969; + size 0.917 0.750; + offset 0.083 0.000; + advance 1.000; +}; +glyph 191 +{ + texcoords 0.023438 0.312500 0.042969 0.347656; + size 0.417 0.750; + offset 0.083 0.000; + advance 0.500; +}; +glyph 192 +{ + texcoords 0.089844 0.312500 0.121094 0.359375; + size 0.667 1.000; + offset 0.000 0.000; + advance 0.667; +}; +glyph 193 +{ + texcoords 0.160156 0.312500 0.191406 0.359375; + size 0.667 1.000; + offset 0.000 0.000; + advance 0.667; +}; +glyph 194 +{ + texcoords 0.230469 0.312500 0.261719 0.359375; + size 0.667 1.000; + offset 0.000 0.000; + advance 0.667; +}; +glyph 195 +{ + texcoords 0.300781 0.312500 0.332031 0.359375; + size 0.667 1.000; + offset 0.000 0.000; + advance 0.667; +}; +glyph 196 +{ + texcoords 0.371094 0.312500 0.402344 0.355469; + size 0.667 0.917; + offset 0.000 0.000; + advance 0.667; +}; +glyph 197 +{ + texcoords 0.441406 0.312500 0.472656 0.359375; + size 0.667 1.000; + offset 0.000 0.000; + advance 0.667; +}; +glyph 198 +{ + texcoords 0.503906 0.312500 0.546875 0.347656; + size 0.917 0.750; + offset 0.000 0.000; + advance 1.000; +}; +glyph 199 +{ + texcoords 0.585938 0.304688 0.609375 0.347656; + size 0.500 0.917; + offset 0.083 -0.167; + advance 0.667; +}; +glyph 200 +{ + texcoords 0.656250 0.312500 0.679688 0.359375; + size 0.500 1.000; + offset 0.083 0.000; + advance 0.667; +}; +glyph 201 +{ + texcoords 0.726562 0.312500 0.750000 0.359375; + size 0.500 1.000; + offset 0.083 0.000; + advance 0.667; +}; +glyph 202 +{ + texcoords 0.796875 0.312500 0.820312 0.359375; + size 0.500 1.000; + offset 0.083 0.000; + advance 0.667; +}; +glyph 203 +{ + texcoords 0.867188 0.312500 0.890625 0.355469; + size 0.500 0.917; + offset 0.083 0.000; + advance 0.667; +}; +glyph 204 +{ + texcoords 0.941406 0.312500 0.953125 0.359375; + size 0.250 1.000; + offset 0.083 0.000; + advance 0.250; +}; +glyph 205 +{ + texcoords 0.027344 0.242188 0.039062 0.289062; + size 0.250 1.000; + offset 0.083 0.000; + advance 0.250; +}; +glyph 206 +{ + texcoords 0.093750 0.242188 0.113281 0.289062; + size 0.417 1.000; + offset -0.083 0.000; + advance 0.250; +}; +glyph 207 +{ + texcoords 0.167969 0.242188 0.179688 0.285156; + size 0.250 0.917; + offset 0.000 0.000; + advance 0.250; +}; +glyph 208 +{ + texcoords 0.230469 0.242188 0.261719 0.277344; + size 0.667 0.750; + offset 0.000 0.000; + advance 0.750; +}; +glyph 209 +{ + texcoords 0.300781 0.242188 0.328125 0.289062; + size 0.583 1.000; + offset 0.083 0.000; + advance 0.750; +}; +glyph 210 +{ + texcoords 0.371094 0.242188 0.398438 0.289062; + size 0.583 1.000; + offset 0.083 0.000; + advance 0.750; +}; +glyph 211 +{ + texcoords 0.441406 0.242188 0.468750 0.289062; + size 0.583 1.000; + offset 0.083 0.000; + advance 0.750; +}; +glyph 212 +{ + texcoords 0.511719 0.242188 0.539062 0.289062; + size 0.583 1.000; + offset 0.083 0.000; + advance 0.750; +}; +glyph 213 +{ + texcoords 0.582031 0.242188 0.609375 0.289062; + size 0.583 1.000; + offset 0.083 0.000; + advance 0.750; +}; +glyph 214 +{ + texcoords 0.652344 0.242188 0.679688 0.285156; + size 0.583 0.917; + offset 0.083 0.000; + advance 0.750; +}; +glyph 215 +{ + texcoords 0.722656 0.242188 0.750000 0.269531; + size 0.583 0.583; + offset 0.167 0.000; + advance 0.833; +}; +glyph 216 +{ + texcoords 0.789062 0.238281 0.824219 0.281250; + size 0.750 0.917; + offset 0.000 -0.083; + advance 0.750; +}; +glyph 217 +{ + texcoords 0.863281 0.242188 0.890625 0.289062; + size 0.583 1.000; + offset 0.083 0.000; + advance 0.750; +}; +glyph 218 +{ + texcoords 0.933594 0.242188 0.960938 0.289062; + size 0.583 1.000; + offset 0.083 0.000; + advance 0.750; +}; +glyph 219 +{ + texcoords 0.019531 0.171875 0.046875 0.218750; + size 0.583 1.000; + offset 0.083 0.000; + advance 0.750; +}; +glyph 220 +{ + texcoords 0.089844 0.171875 0.117188 0.214844; + size 0.583 0.917; + offset 0.083 0.000; + advance 0.750; +}; +glyph 221 +{ + texcoords 0.160156 0.171875 0.187500 0.218750; + size 0.583 1.000; + offset 0.000 0.000; + advance 0.583; +}; +glyph 222 +{ + texcoords 0.234375 0.171875 0.257812 0.207031; + size 0.500 0.750; + offset 0.083 0.000; + advance 0.667; +}; +glyph 223 +{ + texcoords 0.304688 0.171875 0.328125 0.210938; + size 0.500 0.833; + offset 0.083 0.000; + advance 0.667; +}; +glyph 224 +{ + texcoords 0.375000 0.171875 0.398438 0.210938; + size 0.500 0.833; + offset 0.083 0.000; + advance 0.667; +}; +glyph 225 +{ + texcoords 0.445312 0.171875 0.468750 0.210938; + size 0.500 0.833; + offset 0.083 0.000; + advance 0.667; +}; +glyph 226 +{ + texcoords 0.515625 0.171875 0.539062 0.210938; + size 0.500 0.833; + offset 0.083 0.000; + advance 0.667; +}; +glyph 227 +{ + texcoords 0.585938 0.171875 0.609375 0.210938; + size 0.500 0.833; + offset 0.083 0.000; + advance 0.667; +}; +glyph 228 +{ + texcoords 0.656250 0.171875 0.679688 0.207031; + size 0.500 0.750; + offset 0.083 0.000; + advance 0.667; +}; +glyph 229 +{ + texcoords 0.726562 0.171875 0.750000 0.218750; + size 0.500 1.000; + offset 0.083 0.000; + advance 0.667; +}; +glyph 230 +{ + texcoords 0.789062 0.171875 0.828125 0.199219; + size 0.833 0.583; + offset 0.083 0.000; + advance 1.000; +}; +glyph 231 +{ + texcoords 0.867188 0.164062 0.886719 0.199219; + size 0.417 0.750; + offset 0.083 -0.167; + advance 0.583; +}; +glyph 232 +{ + texcoords 0.937500 0.171875 0.960938 0.210938; + size 0.500 0.833; + offset 0.083 0.000; + advance 0.667; +}; +glyph 233 +{ + texcoords 0.023438 0.101562 0.046875 0.140625; + size 0.500 0.833; + offset 0.083 0.000; + advance 0.667; +}; +glyph 234 +{ + texcoords 0.093750 0.101562 0.117188 0.140625; + size 0.500 0.833; + offset 0.083 0.000; + advance 0.667; +}; +glyph 235 +{ + texcoords 0.164062 0.101562 0.187500 0.136719; + size 0.500 0.750; + offset 0.083 0.000; + advance 0.667; +}; +glyph 236 +{ + texcoords 0.238281 0.101562 0.250000 0.140625; + size 0.250 0.833; + offset 0.000 0.000; + advance 0.250; +}; +glyph 237 +{ + texcoords 0.308594 0.101562 0.320312 0.140625; + size 0.250 0.833; + offset 0.083 0.000; + advance 0.250; +}; +glyph 238 +{ + texcoords 0.375000 0.101562 0.394531 0.140625; + size 0.417 0.833; + offset -0.083 0.000; + advance 0.250; +}; +glyph 239 +{ + texcoords 0.449219 0.101562 0.460938 0.136719; + size 0.250 0.750; + offset 0.000 0.000; + advance 0.250; +}; +glyph 240 +{ + texcoords 0.515625 0.101562 0.539062 0.140625; + size 0.500 0.833; + offset 0.083 0.000; + advance 0.667; +}; +glyph 241 +{ + texcoords 0.585938 0.101562 0.609375 0.140625; + size 0.500 0.833; + offset 0.083 0.000; + advance 0.667; +}; +glyph 242 +{ + texcoords 0.656250 0.101562 0.679688 0.140625; + size 0.500 0.833; + offset 0.083 0.000; + advance 0.667; +}; +glyph 243 +{ + texcoords 0.726562 0.101562 0.750000 0.140625; + size 0.500 0.833; + offset 0.083 0.000; + advance 0.667; +}; +glyph 244 +{ + texcoords 0.796875 0.101562 0.820312 0.140625; + size 0.500 0.833; + offset 0.083 0.000; + advance 0.667; +}; +glyph 245 +{ + texcoords 0.867188 0.101562 0.890625 0.140625; + size 0.500 0.833; + offset 0.083 0.000; + advance 0.667; +}; +glyph 246 +{ + texcoords 0.937500 0.101562 0.960938 0.136719; + size 0.500 0.750; + offset 0.083 0.000; + advance 0.667; +}; +glyph 247 +{ + texcoords 0.019531 0.035156 0.046875 0.054688; + size 0.583 0.417; + offset 0.083 0.083; + advance 0.833; +}; +glyph 248 +{ + texcoords 0.089844 0.027344 0.121094 0.062500; + size 0.667 0.750; + offset 0.000 -0.083; + advance 0.667; +}; +glyph 249 +{ + texcoords 0.164062 0.031250 0.187500 0.070312; + size 0.500 0.833; + offset 0.083 0.000; + advance 0.667; +}; +glyph 250 +{ + texcoords 0.234375 0.031250 0.257812 0.070312; + size 0.500 0.833; + offset 0.083 0.000; + advance 0.667; +}; +glyph 251 +{ + texcoords 0.304688 0.031250 0.328125 0.070312; + size 0.500 0.833; + offset 0.083 0.000; + advance 0.667; +}; +glyph 252 +{ + texcoords 0.375000 0.031250 0.398438 0.066406; + size 0.500 0.750; + offset 0.083 0.000; + advance 0.667; +}; +glyph 253 +{ + texcoords 0.441406 0.019531 0.472656 0.070312; + size 0.667 1.083; + offset -0.083 -0.250; + advance 0.500; +}; +glyph 254 +{ + texcoords 0.515625 0.019531 0.539062 0.070312; + size 0.500 1.083; + offset 0.083 -0.250; + advance 0.667; +}; +glyph 255 +{ + texcoords 0.582031 0.019531 0.613281 0.066406; + size 0.667 1.000; + offset -0.083 -0.250; + advance 0.500; +}; diff --git a/dejavu-12.png b/dejavu-12.png new file mode 100644 index 0000000000000000000000000000000000000000..66fc386e4f42850ba7b8c206ee66840850286aa4 GIT binary patch literal 8990 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 Tm+x=i3qV~Nq*S9|6ZXFVW&K0~ literal 0 HcmV?d00001 diff --git a/dejavu-20.font b/dejavu-20.font new file mode 100644 index 0000000..17170ce --- /dev/null +++ b/dejavu-20.font @@ -0,0 +1,1339 @@ +texture "dejavu-20.png"; +default_size 20.000000; +glyph 32 +{ + texcoords 0.023438 0.906250 0.023438 0.906250; + size 0.000000 0.000000; + offset 0.000000 0.000000; + advance 0.300000; +}; +glyph 33 +{ + texcoords 0.070312 0.906250 0.074219 0.964844; + size 0.100000 0.750000; + offset 0.150000 0.000000; + advance 0.400000; +}; +glyph 34 +{ + texcoords 0.115234 0.945312 0.126953 0.964844; + size 0.300000 0.250000; + offset 0.100000 0.500000; + advance 0.500000; +}; +glyph 35 +{ + texcoords 0.156250 0.906250 0.183594 0.964844; + size 0.700000 0.750000; + offset 0.100000 0.000000; + advance 0.850000; +}; +glyph 36 +{ + texcoords 0.208984 0.894531 0.228516 0.964844; + size 0.500000 0.900000; + offset 0.100000 -0.150000; + advance 0.650000; +}; +glyph 37 +{ + texcoords 0.251953 0.906250 0.285156 0.964844; + size 0.850000 0.750000; + offset 0.000000 0.000000; + advance 0.950000; +}; +glyph 38 +{ + texcoords 0.302734 0.906250 0.330078 0.964844; + size 0.700000 0.750000; + offset 0.050000 0.000000; + advance 0.750000; +}; +glyph 39 +{ + texcoords 0.363281 0.945312 0.367188 0.964844; + size 0.100000 0.250000; + offset 0.100000 0.500000; + advance 0.300000; +}; +glyph 40 +{ + texcoords 0.410156 0.894531 0.417969 0.964844; + size 0.200000 0.900000; + offset 0.100000 -0.150000; + advance 0.400000; +}; +glyph 41 +{ + texcoords 0.458984 0.894531 0.466797 0.964844; + size 0.200000 0.900000; + offset 0.100000 -0.150000; + advance 0.400000; +}; +glyph 42 +{ + texcoords 0.503906 0.925781 0.521484 0.964844; + size 0.450000 0.500000; + offset 0.000000 0.250000; + advance 0.500000; +}; +glyph 43 +{ + texcoords 0.548828 0.906250 0.572266 0.953125; + size 0.600000 0.600000; + offset 0.100000 0.000000; + advance 0.850000; +}; +glyph 44 +{ + texcoords 0.607422 0.894531 0.613281 0.914062; + size 0.150000 0.250000; + offset 0.050000 -0.150000; + advance 0.300000; +}; +glyph 45 +{ + texcoords 0.654297 0.925781 0.664062 0.933594; + size 0.250000 0.100000; + offset 0.050000 0.250000; + advance 0.350000; +}; +glyph 46 +{ + texcoords 0.705078 0.906250 0.708984 0.914062; + size 0.100000 0.100000; + offset 0.100000 0.000000; + advance 0.300000; +}; +glyph 47 +{ + texcoords 0.750000 0.902344 0.763672 0.964844; + size 0.350000 0.800000; + offset 0.000000 -0.050000; + advance 0.350000; +}; +glyph 48 +{ + texcoords 0.794922 0.906250 0.814453 0.964844; + size 0.500000 0.750000; + offset 0.050000 0.000000; + advance 0.650000; +}; +glyph 49 +{ + texcoords 0.845703 0.906250 0.863281 0.964844; + size 0.450000 0.750000; + offset 0.100000 0.000000; + advance 0.650000; +}; +glyph 50 +{ + texcoords 0.892578 0.906250 0.912109 0.964844; + size 0.500000 0.750000; + offset 0.050000 0.000000; + advance 0.650000; +}; +glyph 51 +{ + texcoords 0.941406 0.906250 0.960938 0.964844; + size 0.500000 0.750000; + offset 0.050000 0.000000; + advance 0.650000; +}; +glyph 52 +{ + texcoords 0.013672 0.808594 0.035156 0.867188; + size 0.550000 0.750000; + offset 0.050000 0.000000; + advance 0.650000; +}; +glyph 53 +{ + texcoords 0.064453 0.808594 0.082031 0.867188; + size 0.450000 0.750000; + offset 0.050000 0.000000; + advance 0.650000; +}; +glyph 54 +{ + texcoords 0.111328 0.808594 0.130859 0.867188; + size 0.500000 0.750000; + offset 0.050000 0.000000; + advance 0.650000; +}; +glyph 55 +{ + texcoords 0.160156 0.808594 0.179688 0.867188; + size 0.500000 0.750000; + offset 0.050000 0.000000; + advance 0.650000; +}; +glyph 56 +{ + texcoords 0.208984 0.808594 0.228516 0.867188; + size 0.500000 0.750000; + offset 0.050000 0.000000; + advance 0.650000; +}; +glyph 57 +{ + texcoords 0.257812 0.808594 0.277344 0.867188; + size 0.500000 0.750000; + offset 0.050000 0.000000; + advance 0.650000; +}; +glyph 58 +{ + texcoords 0.314453 0.808594 0.318359 0.847656; + size 0.100000 0.500000; + offset 0.100000 0.000000; + advance 0.350000; +}; +glyph 59 +{ + texcoords 0.363281 0.796875 0.369141 0.847656; + size 0.150000 0.650000; + offset 0.050000 -0.150000; + advance 0.350000; +}; +glyph 60 +{ + texcoords 0.402344 0.812500 0.427734 0.855469; + size 0.650000 0.550000; + offset 0.100000 0.050000; + advance 0.850000; +}; +glyph 61 +{ + texcoords 0.451172 0.820312 0.476562 0.843750; + size 0.650000 0.300000; + offset 0.100000 0.150000; + advance 0.850000; +}; +glyph 62 +{ + texcoords 0.500000 0.812500 0.525391 0.855469; + size 0.650000 0.550000; + offset 0.100000 0.050000; + advance 0.850000; +}; +glyph 63 +{ + texcoords 0.552734 0.808594 0.568359 0.867188; + size 0.400000 0.750000; + offset 0.050000 0.000000; + advance 0.550000; +}; +glyph 64 +{ + texcoords 0.591797 0.796875 0.626953 0.867188; + size 0.900000 0.900000; + offset 0.050000 -0.150000; + advance 1.000000; +}; +glyph 65 +{ + texcoords 0.646484 0.808594 0.671875 0.867188; + size 0.650000 0.750000; + offset 0.000000 0.000000; + advance 0.700000; +}; +glyph 66 +{ + texcoords 0.697266 0.808594 0.716797 0.867188; + size 0.500000 0.750000; + offset 0.100000 0.000000; + advance 0.700000; +}; +glyph 67 +{ + texcoords 0.744141 0.808594 0.767578 0.867188; + size 0.600000 0.750000; + offset 0.050000 0.000000; + advance 0.700000; +}; +glyph 68 +{ + texcoords 0.792969 0.808594 0.816406 0.867188; + size 0.600000 0.750000; + offset 0.100000 0.000000; + advance 0.750000; +}; +glyph 69 +{ + texcoords 0.845703 0.808594 0.863281 0.867188; + size 0.450000 0.750000; + offset 0.100000 0.000000; + advance 0.650000; +}; +glyph 70 +{ + texcoords 0.894531 0.808594 0.910156 0.867188; + size 0.400000 0.750000; + offset 0.100000 0.000000; + advance 0.600000; +}; +glyph 71 +{ + texcoords 0.939453 0.808594 0.964844 0.867188; + size 0.650000 0.750000; + offset 0.050000 0.000000; + advance 0.800000; +}; +glyph 72 +{ + texcoords 0.013672 0.710938 0.035156 0.769531; + size 0.550000 0.750000; + offset 0.100000 0.000000; + advance 0.750000; +}; +glyph 73 +{ + texcoords 0.070312 0.710938 0.074219 0.769531; + size 0.100000 0.750000; + offset 0.100000 0.000000; + advance 0.300000; +}; +glyph 74 +{ + texcoords 0.117188 0.695312 0.126953 0.769531; + size 0.250000 0.950000; + offset -0.050000 -0.200000; + advance 0.300000; +}; +glyph 75 +{ + texcoords 0.158203 0.710938 0.181641 0.769531; + size 0.600000 0.750000; + offset 0.100000 0.000000; + advance 0.650000; +}; +glyph 76 +{ + texcoords 0.210938 0.710938 0.228516 0.769531; + size 0.450000 0.750000; + offset 0.100000 0.000000; + advance 0.550000; +}; +glyph 77 +{ + texcoords 0.255859 0.710938 0.281250 0.769531; + size 0.650000 0.750000; + offset 0.100000 0.000000; + advance 0.850000; +}; +glyph 78 +{ + texcoords 0.306641 0.710938 0.328125 0.769531; + size 0.550000 0.750000; + offset 0.100000 0.000000; + advance 0.750000; +}; +glyph 79 +{ + texcoords 0.351562 0.710938 0.378906 0.769531; + size 0.700000 0.750000; + offset 0.050000 0.000000; + advance 0.800000; +}; +glyph 80 +{ + texcoords 0.406250 0.710938 0.423828 0.769531; + size 0.450000 0.750000; + offset 0.100000 0.000000; + advance 0.600000; +}; +glyph 81 +{ + texcoords 0.449219 0.699219 0.476562 0.769531; + size 0.700000 0.900000; + offset 0.050000 -0.150000; + advance 0.800000; +}; +glyph 82 +{ + texcoords 0.501953 0.710938 0.523438 0.769531; + size 0.550000 0.750000; + offset 0.100000 0.000000; + advance 0.700000; +}; +glyph 83 +{ + texcoords 0.550781 0.710938 0.572266 0.769531; + size 0.550000 0.750000; + offset 0.050000 0.000000; + advance 0.650000; +}; +glyph 84 +{ + texcoords 0.597656 0.710938 0.621094 0.769531; + size 0.600000 0.750000; + offset 0.000000 0.000000; + advance 0.600000; +}; +glyph 85 +{ + texcoords 0.648438 0.710938 0.669922 0.769531; + size 0.550000 0.750000; + offset 0.100000 0.000000; + advance 0.750000; +}; +glyph 86 +{ + texcoords 0.695312 0.710938 0.720703 0.769531; + size 0.650000 0.750000; + offset 0.000000 0.000000; + advance 0.700000; +}; +glyph 87 +{ + texcoords 0.738281 0.710938 0.773438 0.769531; + size 0.900000 0.750000; + offset 0.050000 0.000000; + advance 1.000000; +}; +glyph 88 +{ + texcoords 0.792969 0.710938 0.816406 0.769531; + size 0.600000 0.750000; + offset 0.050000 0.000000; + advance 0.700000; +}; +glyph 89 +{ + texcoords 0.841797 0.710938 0.865234 0.769531; + size 0.600000 0.750000; + offset 0.000000 0.000000; + advance 0.600000; +}; +glyph 90 +{ + texcoords 0.890625 0.710938 0.914062 0.769531; + size 0.600000 0.750000; + offset 0.050000 0.000000; + advance 0.700000; +}; +glyph 91 +{ + texcoords 0.947266 0.699219 0.955078 0.769531; + size 0.200000 0.900000; + offset 0.100000 -0.150000; + advance 0.400000; +}; +glyph 92 +{ + texcoords 0.017578 0.609375 0.031250 0.671875; + size 0.350000 0.800000; + offset 0.000000 -0.050000; + advance 0.350000; +}; +glyph 93 +{ + texcoords 0.068359 0.601562 0.076172 0.671875; + size 0.200000 0.900000; + offset 0.100000 -0.150000; + advance 0.400000; +}; +glyph 94 +{ + texcoords 0.109375 0.652344 0.134766 0.671875; + size 0.650000 0.250000; + offset 0.100000 0.500000; + advance 0.850000; +}; +glyph 95 +{ + texcoords 0.160156 0.593750 0.179688 0.601562; + size 0.500000 0.100000; + offset 0.000000 -0.250000; + advance 0.500000; +}; +glyph 96 +{ + texcoords 0.214844 0.660156 0.224609 0.675781; + size 0.250000 0.200000; + offset 0.050000 0.600000; + advance 0.500000; +}; +glyph 97 +{ + texcoords 0.259766 0.613281 0.277344 0.656250; + size 0.450000 0.550000; + offset 0.050000 0.000000; + advance 0.600000; +}; +glyph 98 +{ + texcoords 0.306641 0.613281 0.326172 0.671875; + size 0.500000 0.750000; + offset 0.100000 0.000000; + advance 0.650000; +}; +glyph 99 +{ + texcoords 0.357422 0.613281 0.375000 0.656250; + size 0.450000 0.550000; + offset 0.050000 0.000000; + advance 0.550000; +}; +glyph 100 +{ + texcoords 0.404297 0.613281 0.423828 0.671875; + size 0.500000 0.750000; + offset 0.050000 0.000000; + advance 0.650000; +}; +glyph 101 +{ + texcoords 0.453125 0.613281 0.472656 0.656250; + size 0.500000 0.550000; + offset 0.050000 0.000000; + advance 0.600000; +}; +glyph 102 +{ + texcoords 0.505859 0.613281 0.519531 0.671875; + size 0.350000 0.750000; + offset 0.000000 0.000000; + advance 0.350000; +}; +glyph 103 +{ + texcoords 0.550781 0.597656 0.570312 0.656250; + size 0.500000 0.750000; + offset 0.050000 -0.200000; + advance 0.650000; +}; +glyph 104 +{ + texcoords 0.601562 0.613281 0.619141 0.671875; + size 0.450000 0.750000; + offset 0.100000 0.000000; + advance 0.650000; +}; +glyph 105 +{ + texcoords 0.656250 0.613281 0.660156 0.671875; + size 0.100000 0.750000; + offset 0.100000 0.000000; + advance 0.300000; +}; +glyph 106 +{ + texcoords 0.703125 0.597656 0.710938 0.671875; + size 0.200000 0.950000; + offset 0.000000 -0.200000; + advance 0.300000; +}; +glyph 107 +{ + texcoords 0.746094 0.613281 0.765625 0.671875; + size 0.500000 0.750000; + offset 0.100000 0.000000; + advance 0.600000; +}; +glyph 108 +{ + texcoords 0.802734 0.613281 0.806641 0.671875; + size 0.100000 0.750000; + offset 0.100000 0.000000; + advance 0.300000; +}; +glyph 109 +{ + texcoords 0.837891 0.613281 0.869141 0.656250; + size 0.800000 0.550000; + offset 0.100000 0.000000; + advance 1.000000; +}; +glyph 110 +{ + texcoords 0.894531 0.613281 0.912109 0.656250; + size 0.450000 0.550000; + offset 0.100000 0.000000; + advance 0.650000; +}; +glyph 111 +{ + texcoords 0.941406 0.613281 0.960938 0.656250; + size 0.500000 0.550000; + offset 0.050000 0.000000; + advance 0.600000; +}; +glyph 112 +{ + texcoords 0.013672 0.500000 0.033203 0.558594; + size 0.500000 0.750000; + offset 0.100000 -0.200000; + advance 0.650000; +}; +glyph 113 +{ + texcoords 0.062500 0.500000 0.082031 0.558594; + size 0.500000 0.750000; + offset 0.050000 -0.200000; + advance 0.650000; +}; +glyph 114 +{ + texcoords 0.115234 0.515625 0.126953 0.558594; + size 0.300000 0.550000; + offset 0.100000 0.000000; + advance 0.400000; +}; +glyph 115 +{ + texcoords 0.162109 0.515625 0.179688 0.558594; + size 0.450000 0.550000; + offset 0.050000 0.000000; + advance 0.500000; +}; +glyph 116 +{ + texcoords 0.212891 0.515625 0.226562 0.570312; + size 0.350000 0.700000; + offset 0.050000 0.000000; + advance 0.400000; +}; +glyph 117 +{ + texcoords 0.259766 0.515625 0.277344 0.558594; + size 0.450000 0.550000; + offset 0.100000 0.000000; + advance 0.650000; +}; +glyph 118 +{ + texcoords 0.306641 0.515625 0.328125 0.558594; + size 0.550000 0.550000; + offset 0.000000 0.000000; + advance 0.550000; +}; +glyph 119 +{ + texcoords 0.351562 0.515625 0.380859 0.558594; + size 0.750000 0.550000; + offset 0.050000 0.000000; + advance 0.850000; +}; +glyph 120 +{ + texcoords 0.404297 0.515625 0.425781 0.558594; + size 0.550000 0.550000; + offset 0.000000 0.000000; + advance 0.550000; +}; +glyph 121 +{ + texcoords 0.453125 0.500000 0.474609 0.558594; + size 0.550000 0.750000; + offset 0.000000 -0.200000; + advance 0.550000; +}; +glyph 122 +{ + texcoords 0.503906 0.515625 0.521484 0.558594; + size 0.450000 0.550000; + offset 0.050000 0.000000; + advance 0.550000; +}; +glyph 123 +{ + texcoords 0.552734 0.503906 0.568359 0.574219; + size 0.400000 0.900000; + offset 0.150000 -0.150000; + advance 0.650000; +}; +glyph 124 +{ + texcoords 0.607422 0.496094 0.611328 0.574219; + size 0.100000 1.000000; + offset 0.150000 -0.250000; + advance 0.350000; +}; +glyph 125 +{ + texcoords 0.650391 0.503906 0.666016 0.574219; + size 0.400000 0.900000; + offset 0.100000 -0.150000; + advance 0.650000; +}; +glyph 126 +{ + texcoords 0.695312 0.531250 0.720703 0.550781; + size 0.650000 0.250000; + offset 0.100000 0.200000; + advance 0.850000; +}; +glyph 160 +{ + texcoords 0.755859 0.515625 0.755859 0.515625; + size 0.000000 0.000000; + offset 0.000000 0.000000; + advance 0.300000; +}; +glyph 161 +{ + texcoords 0.802734 0.515625 0.806641 0.574219; + size 0.100000 0.750000; + offset 0.150000 0.000000; + advance 0.400000; +}; +glyph 162 +{ + texcoords 0.845703 0.503906 0.863281 0.570312; + size 0.450000 0.850000; + offset 0.100000 -0.150000; + advance 0.650000; +}; +glyph 163 +{ + texcoords 0.894531 0.515625 0.912109 0.574219; + size 0.450000 0.750000; + offset 0.100000 0.000000; + advance 0.650000; +}; +glyph 164 +{ + texcoords 0.941406 0.519531 0.962891 0.562500; + size 0.550000 0.550000; + offset 0.000000 0.050000; + advance 0.650000; +}; +glyph 165 +{ + texcoords 0.011719 0.417969 0.035156 0.476562; + size 0.600000 0.750000; + offset 0.000000 0.000000; + advance 0.650000; +}; +glyph 166 +{ + texcoords 0.070312 0.406250 0.074219 0.472656; + size 0.100000 0.850000; + offset 0.150000 -0.150000; + advance 0.350000; +}; +glyph 167 +{ + texcoords 0.113281 0.410156 0.128906 0.476562; + size 0.400000 0.850000; + offset 0.050000 -0.100000; + advance 0.500000; +}; +glyph 168 +{ + texcoords 0.164062 0.468750 0.175781 0.476562; + size 0.300000 0.100000; + offset 0.100000 0.650000; + advance 0.500000; +}; +glyph 169 +{ + texcoords 0.205078 0.417969 0.232422 0.476562; + size 0.700000 0.750000; + offset 0.150000 0.000000; + advance 1.000000; +}; +glyph 170 +{ + texcoords 0.259766 0.437500 0.277344 0.476562; + size 0.450000 0.500000; + offset 0.050000 0.250000; + advance 0.450000; +}; +glyph 171 +{ + texcoords 0.306641 0.421875 0.326172 0.457031; + size 0.500000 0.450000; + offset 0.100000 0.050000; + advance 0.600000; +}; +glyph 172 +{ + texcoords 0.353516 0.425781 0.378906 0.449219; + size 0.650000 0.300000; + offset 0.100000 0.100000; + advance 0.850000; +}; +glyph 173 +{ + texcoords 0.410156 0.437500 0.419922 0.445312; + size 0.250000 0.100000; + offset 0.050000 0.250000; + advance 0.350000; +}; +glyph 174 +{ + texcoords 0.449219 0.417969 0.478516 0.476562; + size 0.750000 0.750000; + offset 0.150000 0.000000; + advance 1.000000; +}; +glyph 175 +{ + texcoords 0.505859 0.468750 0.517578 0.476562; + size 0.300000 0.100000; + offset 0.100000 0.650000; + advance 0.500000; +}; +glyph 176 +{ + texcoords 0.554688 0.453125 0.566406 0.476562; + size 0.300000 0.300000; + offset 0.100000 0.450000; + advance 0.500000; +}; +glyph 177 +{ + texcoords 0.597656 0.417969 0.621094 0.468750; + size 0.600000 0.650000; + offset 0.150000 0.000000; + advance 0.850000; +}; +glyph 178 +{ + texcoords 0.652344 0.445312 0.664062 0.476562; + size 0.300000 0.400000; + offset 0.050000 0.350000; + advance 0.400000; +}; +glyph 179 +{ + texcoords 0.701172 0.445312 0.712891 0.476562; + size 0.300000 0.400000; + offset 0.050000 0.350000; + advance 0.400000; +}; +glyph 180 +{ + texcoords 0.751953 0.464844 0.761719 0.480469; + size 0.250000 0.200000; + offset 0.150000 0.600000; + advance 0.500000; +}; +glyph 181 +{ + texcoords 0.794922 0.402344 0.814453 0.460938; + size 0.500000 0.750000; + offset 0.100000 -0.200000; + advance 0.650000; +}; +glyph 182 +{ + texcoords 0.845703 0.410156 0.863281 0.476562; + size 0.450000 0.850000; + offset 0.100000 -0.100000; + advance 0.650000; +}; +glyph 183 +{ + texcoords 0.900391 0.441406 0.904297 0.449219; + size 0.100000 0.100000; + offset 0.100000 0.300000; + advance 0.300000; +}; +glyph 184 +{ + texcoords 0.947266 0.402344 0.955078 0.417969; + size 0.200000 0.200000; + offset 0.150000 -0.200000; + advance 0.500000; +}; +glyph 185 +{ + texcoords 0.019531 0.347656 0.029297 0.378906; + size 0.250000 0.400000; + offset 0.100000 0.350000; + advance 0.400000; +}; +glyph 186 +{ + texcoords 0.064453 0.339844 0.080078 0.378906; + size 0.400000 0.500000; + offset 0.050000 0.250000; + advance 0.450000; +}; +glyph 187 +{ + texcoords 0.111328 0.324219 0.130859 0.359375; + size 0.500000 0.450000; + offset 0.100000 0.050000; + advance 0.600000; +}; +glyph 188 +{ + texcoords 0.154297 0.320312 0.185547 0.378906; + size 0.800000 0.750000; + offset 0.100000 0.000000; + advance 0.950000; +}; +glyph 189 +{ + texcoords 0.203125 0.320312 0.234375 0.378906; + size 0.800000 0.750000; + offset 0.100000 0.000000; + advance 0.950000; +}; +glyph 190 +{ + texcoords 0.251953 0.320312 0.285156 0.378906; + size 0.850000 0.750000; + offset 0.050000 0.000000; + advance 0.950000; +}; +glyph 191 +{ + texcoords 0.308594 0.320312 0.324219 0.378906; + size 0.400000 0.750000; + offset 0.050000 0.000000; + advance 0.550000; +}; +glyph 192 +{ + texcoords 0.353516 0.320312 0.378906 0.394531; + size 0.650000 0.950000; + offset 0.000000 0.000000; + advance 0.700000; +}; +glyph 193 +{ + texcoords 0.402344 0.320312 0.427734 0.394531; + size 0.650000 0.950000; + offset 0.000000 0.000000; + advance 0.700000; +}; +glyph 194 +{ + texcoords 0.451172 0.320312 0.476562 0.394531; + size 0.650000 0.950000; + offset 0.000000 0.000000; + advance 0.700000; +}; +glyph 195 +{ + texcoords 0.500000 0.320312 0.525391 0.390625; + size 0.650000 0.900000; + offset 0.000000 0.000000; + advance 0.700000; +}; +glyph 196 +{ + texcoords 0.548828 0.320312 0.574219 0.394531; + size 0.650000 0.950000; + offset 0.000000 0.000000; + advance 0.700000; +}; +glyph 197 +{ + texcoords 0.597656 0.320312 0.623047 0.394531; + size 0.650000 0.950000; + offset 0.000000 0.000000; + advance 0.700000; +}; +glyph 198 +{ + texcoords 0.640625 0.320312 0.675781 0.378906; + size 0.900000 0.750000; + offset 0.000000 0.000000; + advance 1.000000; +}; +glyph 199 +{ + texcoords 0.695312 0.304688 0.718750 0.378906; + size 0.600000 0.950000; + offset 0.050000 -0.200000; + advance 0.700000; +}; +glyph 200 +{ + texcoords 0.748047 0.320312 0.765625 0.394531; + size 0.450000 0.950000; + offset 0.100000 0.000000; + advance 0.650000; +}; +glyph 201 +{ + texcoords 0.796875 0.320312 0.814453 0.394531; + size 0.450000 0.950000; + offset 0.100000 0.000000; + advance 0.650000; +}; +glyph 202 +{ + texcoords 0.845703 0.320312 0.863281 0.394531; + size 0.450000 0.950000; + offset 0.100000 0.000000; + advance 0.650000; +}; +glyph 203 +{ + texcoords 0.894531 0.320312 0.912109 0.394531; + size 0.450000 0.950000; + offset 0.100000 0.000000; + advance 0.650000; +}; +glyph 204 +{ + texcoords 0.947266 0.320312 0.955078 0.394531; + size 0.200000 0.950000; + offset 0.050000 0.000000; + advance 0.300000; +}; +glyph 205 +{ + texcoords 0.019531 0.222656 0.027344 0.296875; + size 0.200000 0.950000; + offset 0.100000 0.000000; + advance 0.300000; +}; +glyph 206 +{ + texcoords 0.066406 0.222656 0.078125 0.296875; + size 0.300000 0.950000; + offset 0.000000 0.000000; + advance 0.300000; +}; +glyph 207 +{ + texcoords 0.115234 0.222656 0.126953 0.296875; + size 0.300000 0.950000; + offset 0.000000 0.000000; + advance 0.300000; +}; +glyph 208 +{ + texcoords 0.156250 0.222656 0.183594 0.281250; + size 0.700000 0.750000; + offset 0.000000 0.000000; + advance 0.750000; +}; +glyph 209 +{ + texcoords 0.208984 0.222656 0.230469 0.292969; + size 0.550000 0.900000; + offset 0.100000 0.000000; + advance 0.750000; +}; +glyph 210 +{ + texcoords 0.253906 0.222656 0.281250 0.296875; + size 0.700000 0.950000; + offset 0.050000 0.000000; + advance 0.800000; +}; +glyph 211 +{ + texcoords 0.302734 0.222656 0.330078 0.296875; + size 0.700000 0.950000; + offset 0.050000 0.000000; + advance 0.800000; +}; +glyph 212 +{ + texcoords 0.351562 0.222656 0.378906 0.296875; + size 0.700000 0.950000; + offset 0.050000 0.000000; + advance 0.800000; +}; +glyph 213 +{ + texcoords 0.400391 0.222656 0.427734 0.292969; + size 0.700000 0.900000; + offset 0.050000 0.000000; + advance 0.800000; +}; +glyph 214 +{ + texcoords 0.449219 0.222656 0.476562 0.296875; + size 0.700000 0.950000; + offset 0.050000 0.000000; + advance 0.800000; +}; +glyph 215 +{ + texcoords 0.501953 0.226562 0.523438 0.269531; + size 0.550000 0.550000; + offset 0.150000 0.050000; + advance 0.850000; +}; +glyph 216 +{ + texcoords 0.544922 0.218750 0.576172 0.285156; + size 0.800000 0.850000; + offset 0.000000 -0.050000; + advance 0.800000; +}; +glyph 217 +{ + texcoords 0.599609 0.222656 0.621094 0.296875; + size 0.550000 0.950000; + offset 0.100000 0.000000; + advance 0.750000; +}; +glyph 218 +{ + texcoords 0.648438 0.222656 0.669922 0.296875; + size 0.550000 0.950000; + offset 0.100000 0.000000; + advance 0.750000; +}; +glyph 219 +{ + texcoords 0.697266 0.222656 0.718750 0.296875; + size 0.550000 0.950000; + offset 0.100000 0.000000; + advance 0.750000; +}; +glyph 220 +{ + texcoords 0.746094 0.222656 0.767578 0.296875; + size 0.550000 0.950000; + offset 0.100000 0.000000; + advance 0.750000; +}; +glyph 221 +{ + texcoords 0.792969 0.222656 0.816406 0.296875; + size 0.600000 0.950000; + offset 0.000000 0.000000; + advance 0.600000; +}; +glyph 222 +{ + texcoords 0.845703 0.222656 0.863281 0.281250; + size 0.450000 0.750000; + offset 0.100000 0.000000; + advance 0.600000; +}; +glyph 223 +{ + texcoords 0.892578 0.222656 0.912109 0.281250; + size 0.500000 0.750000; + offset 0.100000 0.000000; + advance 0.650000; +}; +glyph 224 +{ + texcoords 0.943359 0.222656 0.960938 0.285156; + size 0.450000 0.800000; + offset 0.050000 0.000000; + advance 0.600000; +}; +glyph 225 +{ + texcoords 0.015625 0.125000 0.033203 0.187500; + size 0.450000 0.800000; + offset 0.050000 0.000000; + advance 0.600000; +}; +glyph 226 +{ + texcoords 0.064453 0.125000 0.082031 0.187500; + size 0.450000 0.800000; + offset 0.050000 0.000000; + advance 0.600000; +}; +glyph 227 +{ + texcoords 0.113281 0.125000 0.130859 0.187500; + size 0.450000 0.800000; + offset 0.050000 0.000000; + advance 0.600000; +}; +glyph 228 +{ + texcoords 0.162109 0.125000 0.179688 0.183594; + size 0.450000 0.750000; + offset 0.050000 0.000000; + advance 0.600000; +}; +glyph 229 +{ + texcoords 0.210938 0.125000 0.228516 0.191406; + size 0.450000 0.850000; + offset 0.050000 0.000000; + advance 0.600000; +}; +glyph 230 +{ + texcoords 0.251953 0.125000 0.285156 0.167969; + size 0.850000 0.550000; + offset 0.050000 0.000000; + advance 0.950000; +}; +glyph 231 +{ + texcoords 0.308594 0.109375 0.326172 0.167969; + size 0.450000 0.750000; + offset 0.050000 -0.200000; + advance 0.550000; +}; +glyph 232 +{ + texcoords 0.355469 0.125000 0.375000 0.187500; + size 0.500000 0.800000; + offset 0.050000 0.000000; + advance 0.600000; +}; +glyph 233 +{ + texcoords 0.404297 0.125000 0.423828 0.187500; + size 0.500000 0.800000; + offset 0.050000 0.000000; + advance 0.600000; +}; +glyph 234 +{ + texcoords 0.453125 0.125000 0.472656 0.187500; + size 0.500000 0.800000; + offset 0.050000 0.000000; + advance 0.600000; +}; +glyph 235 +{ + texcoords 0.501953 0.125000 0.521484 0.183594; + size 0.500000 0.750000; + offset 0.050000 0.000000; + advance 0.600000; +}; +glyph 236 +{ + texcoords 0.556641 0.125000 0.566406 0.187500; + size 0.250000 0.800000; + offset -0.050000 0.000000; + advance 0.300000; +}; +glyph 237 +{ + texcoords 0.605469 0.125000 0.615234 0.187500; + size 0.250000 0.800000; + offset 0.050000 0.000000; + advance 0.300000; +}; +glyph 238 +{ + texcoords 0.652344 0.125000 0.664062 0.187500; + size 0.300000 0.800000; + offset 0.000000 0.000000; + advance 0.300000; +}; +glyph 239 +{ + texcoords 0.701172 0.125000 0.712891 0.183594; + size 0.300000 0.750000; + offset 0.000000 0.000000; + advance 0.300000; +}; +glyph 240 +{ + texcoords 0.746094 0.125000 0.765625 0.183594; + size 0.500000 0.750000; + offset 0.050000 0.000000; + advance 0.600000; +}; +glyph 241 +{ + texcoords 0.796875 0.125000 0.814453 0.187500; + size 0.450000 0.800000; + offset 0.100000 0.000000; + advance 0.650000; +}; +glyph 242 +{ + texcoords 0.843750 0.125000 0.863281 0.187500; + size 0.500000 0.800000; + offset 0.050000 0.000000; + advance 0.600000; +}; +glyph 243 +{ + texcoords 0.892578 0.125000 0.912109 0.187500; + size 0.500000 0.800000; + offset 0.050000 0.000000; + advance 0.600000; +}; +glyph 244 +{ + texcoords 0.941406 0.125000 0.960938 0.187500; + size 0.500000 0.800000; + offset 0.050000 0.000000; + advance 0.600000; +}; +glyph 245 +{ + texcoords 0.013672 0.027344 0.033203 0.089844; + size 0.500000 0.800000; + offset 0.050000 0.000000; + advance 0.600000; +}; +glyph 246 +{ + texcoords 0.062500 0.027344 0.082031 0.085938; + size 0.500000 0.750000; + offset 0.050000 0.000000; + advance 0.600000; +}; +glyph 247 +{ + texcoords 0.109375 0.031250 0.132812 0.070312; + size 0.600000 0.500000; + offset 0.100000 0.050000; + advance 0.850000; +}; +glyph 248 +{ + texcoords 0.158203 0.023438 0.181641 0.074219; + size 0.600000 0.650000; + offset 0.000000 -0.050000; + advance 0.600000; +}; +glyph 249 +{ + texcoords 0.210938 0.027344 0.228516 0.089844; + size 0.450000 0.800000; + offset 0.100000 0.000000; + advance 0.650000; +}; +glyph 250 +{ + texcoords 0.259766 0.027344 0.277344 0.089844; + size 0.450000 0.800000; + offset 0.100000 0.000000; + advance 0.650000; +}; +glyph 251 +{ + texcoords 0.308594 0.027344 0.326172 0.089844; + size 0.450000 0.800000; + offset 0.100000 0.000000; + advance 0.650000; +}; +glyph 252 +{ + texcoords 0.357422 0.027344 0.375000 0.085938; + size 0.450000 0.750000; + offset 0.100000 0.000000; + advance 0.650000; +}; +glyph 253 +{ + texcoords 0.404297 0.011719 0.425781 0.089844; + size 0.550000 1.000000; + offset 0.000000 -0.200000; + advance 0.550000; +}; +glyph 254 +{ + texcoords 0.453125 0.011719 0.472656 0.085938; + size 0.500000 0.950000; + offset 0.100000 -0.200000; + advance 0.650000; +}; +glyph 255 +{ + texcoords 0.501953 0.011719 0.523438 0.085938; + size 0.550000 0.950000; + offset 0.000000 -0.200000; + advance 0.550000; +}; diff --git a/dejavu-20.png b/dejavu-20.png new file mode 100644 index 0000000000000000000000000000000000000000..08251db997af2f3ff6e361c41db6580b8764bb75 GIT binary patch literal 16467 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∈YA+!PD% zN%n&PVW+qMm|ZECG2%6qEAU-;eHb7(iHAB=>3S0lY=JYchF>VvQ%vkC_Nu%n~at z*Y|6ZJWPuq%FF_cFT1meLJ!27-cGgGE)Kh(txK(x(!oe%4R>SuHrmrl`rbD#J8-9y zY{I)#KLU>LPkf0VB=aoS5ZmLc9p7I}zHh|`ga=O9yU3fDAAfhZpPpFEU5s&=nv@u@ zUVZQ^Ja`+zjPA4o(HS;xGCKhF>vdh-Ci>WEsOo{}(8s0td9^BDgExWjp+eq@g;A8O^gb z#HJCHX*4xIR&S&_T>bo?=c;$m=q3ENp#f|x&FUEI^pPm0k7je@2P?9FBo`H><1grz zBNHk#wHb#!S8;`k%W4tswu_d&+$Dr*4d11YaLF_a=H;p5#~x1Ks$%y?FZm=iV9UY4 zOJSs1xiVKe(z+0c-F|#gk6xZp!H2I278*>)LeospsYGfuY$Ig$SFl64)7^ZX%J#s$Dx`R1u-{ z8aT*2AlkrzOSbs&B~GXB`N@`tmcBJW|MfR2A}q{|o7US^MXYP>6Re`0m<-wWzVk0g zNT`5+2L#dOqYQFv8|V!GH!rW)42ybcLp6Z8+I;u^42rEd@FDCF>LsfW4_v;#h(O5uNI7C6 zO}kc(R(OYhwk^L^DKUY$t!?DT1WVH6vsmLt*FAtYW`S(#)HOLYF}j>d>5-F;89Eh; zxp#7sLuy|KZ~L}SO870sDk5E4uYI?(IqVH*%Kv8GJ`yE?niE0{ydl&t&Dytl6V}V+ zVWUbK?zOg7-)Kya!2ia??0hzK^41ilnHcypW=533i~i8pFX3S@;SU38MoaES-O^Ch zw@iaO8C3MLREj!^2IqZU<}QB&#jM0z^0WO}m-|RfHr?@tTg$#2|Ko^NH(J{ z5CAa}^HDR&6CjoE6HSf^fBjC>Fda-NSHzi1T|E|@kdai^1x)=HWME3gubtNB8xg14 z1xv?KOVLO0I1?r6VT#JO_8%G^>vFWz-T)o2Xtu4qZV6cws-R(IP)1baagk8FqE_`b zND5ez;57}fEad!N8Fq15>||`dzjwk4h>xbH;qpa0&hBc7-x|Uhc+RbJB}73KtN4B}(@`N9(zQLZz8DIfC2`cXn|OPS38-!Z7KgxK7!WI|f^}nzlm4 zz$-~pm9P?-FZrq#A=Tq^SJd~gPB^b=bFyrq(ZD@tbB!mkdL^8SAoHFKa@gu zmU#pSl`jPlj4FkHezzX5Km}WPyWoL5X9Drk`(g(Ofpo;npg}YBud7Wy@PM;!&*Pnq z!}UdJ4cncmiJsri_vC|E#eb*_4~&dBc56p1RA*;FrUxDEGy;Uim;7nZM+*l6&Na0A zo9L%Wc1(qX<^d$G67ST@jbQEx=XN3;+vr?+T3zq9tp6x|hI92w6+(TsVM28s-uHA6 zcQZkpr8lPTy5jAKX#QWhdlwb3MOZ5$P{bDKvT(_5N#K;~go9FSgx;iTWy(H;fC)tx zuq_n5Jr#$zQT|UVgy_f?`gWw`gZ@ti0MpH2Xyc;Tg%@#&mrTY z^9w67ADq)QY=7RVsrD*{9v>w?5tqe>!`|nnV^Fsk%I*64Z~2D9MQ^%NIkm$TSwz;; z=Y7)%Tc~WYInuZyl&5dgy|#-U&}TAX!5}`Cl$^J4d%5XvB*);$`YV{+?f;x6{f_c( z%c7SgK-lRWXG*tn$i9OQpe^8^g_ zsw~3&con&xZKn1d?^rEFq^mqKO!5UzfRN)E7I^Ahy33=*B^h*9BS?CzKbNG;dk795 z+rvWVHi@5(7fKl+iW;5bogddiDnJLQwearyo01~-zV@|5G#r@~n)?o@c6&8ecq*ow z7{Y4LKr>-txIa2QRvzYVI$Wn1nR&KsWx6;aWP(?Xx2!(>hiX zCkr9^6yw`_6*F|Crd#t!k6Ld}?A9%LjpH&R=zfiFVhWs&adyrPra*oax#(^MY5xcenQ$`sV5xMr^g3|+=Yn6< z*^V{qENu35d5e%$fbh~ZiT-Wa%&6nPTsi~|pL)74GZDO)CkABU4d&sfsfImPk_fC0 zBE0022kGkze)$m~=w-!v5>Rfj`%K4sq(ddi$Vm6Bd>scs_gqpTszfdTFK)3Jrarxy zWudW(1g;T1?NmDR1Eyf~-xd*i@LQ69lny*G0wW=}_HVhgPbAOC4fZX+euoFmk6-zp zw7Ww^V@GX=Ic+aJ8Xxc#HfsCO64L#i2U-%dDRf!cd_m8PT(k*1v!W2NOE=DT_KApD z_SuT!s;jAzwsa`!f3xFD)-)DlDw}UyL=HYN<1L^>fFx`cr>zmv@z`{j?|R~_9jhYM zARpkZuE1!;z(pj_ZMbXns8-w?@wiFYkBjG9- z`addsS4&|AO~`PF-Mho2PydQOVS~{!e$O$*zIRtPNY592%Z5j^Bbi2iSzR8pV$xzR zk+mzcHBR;ymQ|la!{lEUJ=(MI+B@z z4Syp%t#y&n!(G_Jt2xtGznaX%XMmT9zpT0$K{VG&U(L!sHASdAo|rDp4p5SrX{OGk zXAlfE(WiuB!-PzSi>|o9ko!Kk5#(sCjvovc$XWeDE#lghgmj7!f=TQ4X|)>9>__>; z0!nVv{R;`$OFBmXJCVHHVyL_7*fA?3w~5C%z(_kYQBnCXbqnJYeO(+suw@(dy8G z?1m7dW7oA(p7!nu9xfR~zDw>HAXFE8I0|Tmeq$FMv=JU}GK!dYikxd9Di*U}uYi`)$8B3S5bUxNF7(DUw-*t)ErZa=CqEh1^?0eDM%|IIj0; z@NjhEv<}ty{g`UX)JQ#IVO9#ZIJWo>Yx8OcYeiDb&8*}(j5tP{j{GoZY|Ko9T5I>; zk0ua42s9RfEPZvH3c0)&XK8(j1G$K#*OZICAVuskWJioz$r4v0lrHte#YHC7yELx96Do&tA`_ZSJ^$*P}sBbS(Mwa#|gn{Snq zr|6qZD|I8SIDG~xQy)1-)e83=4~#WeEU)>Z#!T(f0*Cae;0LS|!aJ)EhZx+8zB)y& z^>Q?*V!^F{U zRof}hIN?^9^1L1m|0N*k|HHK-o=CVnq7Z`lLVf+7mN?4hh8ao|53^seEGLLl=S>76uAm4JV%O)ZE+uj)ANsr`3;iX;U%^(0owo hCLmdFzXyQ%&wl|p9jH>{=igd@^cMy3N>QV4{}*x*0&D;P literal 0 HcmV?d00001 diff --git a/digitalreadout-16.font b/digitalreadout-16.font new file mode 100644 index 0000000..2828761 --- /dev/null +++ b/digitalreadout-16.font @@ -0,0 +1,669 @@ +texture "digitalreadout-16.png"; +default_size 16; +ascent 0.812; +descent 0.062; +glyph 32 +{ + texcoords 0.031250 0.898438 0.031250 0.898438; + size 0.000 0.000; + offset 0.000 0.000; + advance 0.438; +}; +glyph 33 +{ + texcoords 0.085938 0.898438 0.101562 0.984375; + size 0.250 0.688; + offset 0.000 0.000; + advance 0.125; +}; +glyph 34 +{ + texcoords 0.148438 0.968750 0.160156 0.992188; + size 0.188 0.188; + offset 0.125 0.562; + advance 0.188; +}; +glyph 35 +{ + texcoords 0.199219 0.898438 0.234375 0.992188; + size 0.562 0.750; + offset 0.000 0.000; + advance 0.438; +}; +glyph 36 +{ + texcoords 0.265625 0.882812 0.292969 1.000000; + size 0.438 0.938; + offset 0.062 -0.125; + advance 0.438; +}; +glyph 37 +{ + texcoords 0.324219 0.898438 0.359375 0.992188; + size 0.562 0.750; + offset 0.000 0.000; + advance 0.438; +}; +glyph 38 +{ + texcoords 0.390625 0.898438 0.421875 0.984375; + size 0.500 0.688; + offset 0.000 0.000; + advance 0.438; +}; +glyph 39 +{ + texcoords 0.464844 0.968750 0.472656 0.984375; + size 0.125 0.125; + offset 0.125 0.562; + advance 0.125; +}; +glyph 40 +{ + texcoords 0.519531 0.898438 0.542969 0.984375; + size 0.375 0.688; + offset 0.000 0.000; + advance 0.250; +}; +glyph 41 +{ + texcoords 0.582031 0.898438 0.605469 0.984375; + size 0.375 0.688; + offset 0.000 0.000; + advance 0.250; +}; +glyph 42 +{ + texcoords 0.640625 0.906250 0.667969 0.976562; + size 0.438 0.562; + offset 0.062 0.062; + advance 0.438; +}; +glyph 43 +{ + texcoords 0.707031 0.906250 0.726562 0.976562; + size 0.312 0.562; + offset 0.125 0.062; + advance 0.438; +}; +glyph 44 +{ + texcoords 0.777344 0.890625 0.785156 0.906250; + size 0.125 0.125; + offset 0.000 -0.062; + advance 0.125; +}; +glyph 45 +{ + texcoords 0.832031 0.937500 0.851562 0.945312; + size 0.312 0.062; + offset 0.125 0.312; + advance 0.438; +}; +glyph 46 +{ + texcoords 0.902344 0.898438 0.910156 0.914062; + size 0.125 0.125; + offset 0.000 0.000; + advance 0.125; +}; +glyph 47 +{ + texcoords 0.953125 0.906250 0.980469 0.976562; + size 0.438 0.562; + offset 0.062 0.062; + advance 0.438; +}; +glyph 48 +{ + texcoords 0.011719 0.773438 0.046875 0.859375; + size 0.562 0.688; + offset 0.000 0.000; + advance 0.438; +}; +glyph 49 +{ + texcoords 0.085938 0.781250 0.101562 0.859375; + size 0.250 0.625; + offset 0.312 0.062; + advance 0.438; +}; +glyph 50 +{ + texcoords 0.136719 0.773438 0.171875 0.859375; + size 0.562 0.688; + offset 0.000 0.000; + advance 0.438; +}; +glyph 51 +{ + texcoords 0.203125 0.773438 0.234375 0.859375; + size 0.500 0.688; + offset 0.062 0.000; + advance 0.438; +}; +glyph 52 +{ + texcoords 0.265625 0.781250 0.296875 0.859375; + size 0.500 0.625; + offset 0.062 0.062; + advance 0.438; +}; +glyph 53 +{ + texcoords 0.328125 0.773438 0.355469 0.859375; + size 0.438 0.688; + offset 0.062 0.000; + advance 0.438; +}; +glyph 54 +{ + texcoords 0.390625 0.773438 0.421875 0.859375; + size 0.500 0.688; + offset 0.000 0.000; + advance 0.438; +}; +glyph 55 +{ + texcoords 0.457031 0.781250 0.480469 0.859375; + size 0.375 0.625; + offset 0.188 0.062; + advance 0.438; +}; +glyph 56 +{ + texcoords 0.511719 0.773438 0.546875 0.859375; + size 0.562 0.688; + offset 0.000 0.000; + advance 0.438; +}; +glyph 57 +{ + texcoords 0.578125 0.773438 0.609375 0.859375; + size 0.500 0.688; + offset 0.062 0.000; + advance 0.438; +}; +glyph 58 +{ + texcoords 0.632812 0.773438 0.675781 0.812500; + size 0.688 0.312; + offset -0.250 0.000; + advance 0.125; +}; +glyph 59 +{ + texcoords 0.695312 0.765625 0.738281 0.812500; + size 0.688 0.375; + offset -0.250 -0.062; + advance 0.125; +}; +glyph 60 +{ + texcoords 0.773438 0.781250 0.789062 0.851562; + size 0.250 0.562; + offset 0.250 0.062; + advance 0.438; +}; +glyph 61 +{ + texcoords 0.828125 0.804688 0.855469 0.828125; + size 0.438 0.188; + offset 0.062 0.250; + advance 0.438; +}; +glyph 62 +{ + texcoords 0.898438 0.781250 0.914062 0.851562; + size 0.250 0.562; + offset 0.250 0.062; + advance 0.438; +}; +glyph 63 +{ + texcoords 0.949219 0.773438 0.984375 0.859375; + size 0.562 0.688; + offset 0.000 0.000; + advance 0.438; +}; +glyph 64 +{ + texcoords 0.011719 0.648438 0.046875 0.742188; + size 0.562 0.750; + offset 0.000 0.000; + advance 0.438; +}; +glyph 65 +{ + texcoords 0.074219 0.656250 0.109375 0.734375; + size 0.562 0.625; + offset 0.000 0.062; + advance 0.438; +}; +glyph 66 +{ + texcoords 0.140625 0.648438 0.171875 0.734375; + size 0.500 0.688; + offset 0.062 0.000; + advance 0.438; +}; +glyph 67 +{ + texcoords 0.203125 0.648438 0.234375 0.734375; + size 0.500 0.688; + offset 0.000 0.000; + advance 0.438; +}; +glyph 68 +{ + texcoords 0.265625 0.648438 0.296875 0.734375; + size 0.500 0.688; + offset 0.062 0.000; + advance 0.438; +}; +glyph 69 +{ + texcoords 0.328125 0.648438 0.359375 0.734375; + size 0.500 0.688; + offset 0.000 0.000; + advance 0.438; +}; +glyph 70 +{ + texcoords 0.390625 0.656250 0.421875 0.734375; + size 0.500 0.625; + offset 0.000 0.062; + advance 0.438; +}; +glyph 71 +{ + texcoords 0.453125 0.648438 0.484375 0.734375; + size 0.500 0.688; + offset 0.000 0.000; + advance 0.438; +}; +glyph 72 +{ + texcoords 0.511719 0.656250 0.546875 0.734375; + size 0.562 0.625; + offset 0.000 0.062; + advance 0.438; +}; +glyph 73 +{ + texcoords 0.585938 0.656250 0.597656 0.726562; + size 0.188 0.562; + offset 0.188 0.062; + advance 0.438; +}; +glyph 74 +{ + texcoords 0.636719 0.648438 0.671875 0.734375; + size 0.562 0.688; + offset 0.000 0.000; + advance 0.438; +}; +glyph 75 +{ + texcoords 0.703125 0.648438 0.734375 0.734375; + size 0.500 0.688; + offset 0.000 0.000; + advance 0.438; +}; +glyph 76 +{ + texcoords 0.769531 0.648438 0.792969 0.734375; + size 0.375 0.688; + offset 0.000 0.000; + advance 0.438; +}; +glyph 77 +{ + texcoords 0.824219 0.656250 0.859375 0.734375; + size 0.562 0.625; + offset 0.000 0.062; + advance 0.438; +}; +glyph 78 +{ + texcoords 0.886719 0.656250 0.921875 0.734375; + size 0.562 0.625; + offset 0.000 0.062; + advance 0.438; +}; +glyph 79 +{ + texcoords 0.949219 0.648438 0.984375 0.734375; + size 0.562 0.688; + offset 0.000 0.000; + advance 0.438; +}; +glyph 80 +{ + texcoords 0.011719 0.531250 0.046875 0.609375; + size 0.562 0.625; + offset 0.000 0.062; + advance 0.438; +}; +glyph 81 +{ + texcoords 0.074219 0.523438 0.109375 0.609375; + size 0.562 0.688; + offset 0.000 0.000; + advance 0.438; +}; +glyph 82 +{ + texcoords 0.136719 0.531250 0.171875 0.609375; + size 0.562 0.625; + offset 0.000 0.062; + advance 0.438; +}; +glyph 83 +{ + texcoords 0.203125 0.523438 0.230469 0.609375; + size 0.438 0.688; + offset 0.062 0.000; + advance 0.438; +}; +glyph 84 +{ + texcoords 0.269531 0.531250 0.289062 0.609375; + size 0.312 0.625; + offset 0.188 0.062; + advance 0.438; +}; +glyph 85 +{ + texcoords 0.324219 0.523438 0.359375 0.609375; + size 0.562 0.688; + offset 0.000 0.000; + advance 0.438; +}; +glyph 86 +{ + texcoords 0.390625 0.531250 0.417969 0.609375; + size 0.438 0.625; + offset 0.125 0.062; + advance 0.438; +}; +glyph 87 +{ + texcoords 0.449219 0.531250 0.484375 0.609375; + size 0.562 0.625; + offset 0.000 0.062; + advance 0.438; +}; +glyph 88 +{ + texcoords 0.515625 0.531250 0.542969 0.601562; + size 0.438 0.562; + offset 0.062 0.062; + advance 0.438; +}; +glyph 89 +{ + texcoords 0.578125 0.531250 0.609375 0.609375; + size 0.500 0.625; + offset 0.062 0.062; + advance 0.438; +}; +glyph 90 +{ + texcoords 0.640625 0.523438 0.667969 0.609375; + size 0.438 0.688; + offset 0.062 0.000; + advance 0.438; +}; +glyph 91 +{ + texcoords 0.707031 0.523438 0.730469 0.609375; + size 0.375 0.688; + offset 0.000 0.000; + advance 0.250; +}; +glyph 92 +{ + texcoords 0.769531 0.531250 0.789062 0.601562; + size 0.312 0.562; + offset 0.125 0.062; + advance 0.438; +}; +glyph 93 +{ + texcoords 0.832031 0.523438 0.855469 0.609375; + size 0.375 0.688; + offset 0.000 0.000; + advance 0.250; +}; +glyph 94 +{ + texcoords 0.894531 0.562500 0.914062 0.601562; + size 0.312 0.312; + offset 0.125 0.312; + advance 0.438; +}; +glyph 95 +{ + texcoords 0.949219 0.523438 0.984375 0.617188; + size 0.562 0.750; + offset 0.000 0.000; + advance 0.438; +}; +glyph 96 +{ + texcoords 0.011719 0.398438 0.046875 0.492188; + size 0.562 0.750; + offset 0.000 0.000; + advance 0.438; +}; +glyph 97 +{ + texcoords 0.074219 0.406250 0.109375 0.484375; + size 0.562 0.625; + offset 0.000 0.062; + advance 0.438; +}; +glyph 98 +{ + texcoords 0.140625 0.398438 0.171875 0.484375; + size 0.500 0.688; + offset 0.062 0.000; + advance 0.438; +}; +glyph 99 +{ + texcoords 0.203125 0.398438 0.234375 0.484375; + size 0.500 0.688; + offset 0.000 0.000; + advance 0.438; +}; +glyph 100 +{ + texcoords 0.265625 0.398438 0.296875 0.484375; + size 0.500 0.688; + offset 0.062 0.000; + advance 0.438; +}; +glyph 101 +{ + texcoords 0.328125 0.398438 0.359375 0.484375; + size 0.500 0.688; + offset 0.000 0.000; + advance 0.438; +}; +glyph 102 +{ + texcoords 0.390625 0.406250 0.421875 0.484375; + size 0.500 0.625; + offset 0.000 0.062; + advance 0.438; +}; +glyph 103 +{ + texcoords 0.453125 0.398438 0.484375 0.484375; + size 0.500 0.688; + offset 0.000 0.000; + advance 0.438; +}; +glyph 104 +{ + texcoords 0.511719 0.406250 0.546875 0.484375; + size 0.562 0.625; + offset 0.000 0.062; + advance 0.438; +}; +glyph 105 +{ + texcoords 0.585938 0.406250 0.597656 0.476562; + size 0.188 0.562; + offset 0.188 0.062; + advance 0.438; +}; +glyph 106 +{ + texcoords 0.636719 0.398438 0.671875 0.484375; + size 0.562 0.688; + offset 0.000 0.000; + advance 0.438; +}; +glyph 107 +{ + texcoords 0.703125 0.398438 0.734375 0.484375; + size 0.500 0.688; + offset 0.000 0.000; + advance 0.438; +}; +glyph 108 +{ + texcoords 0.769531 0.398438 0.792969 0.484375; + size 0.375 0.688; + offset 0.000 0.000; + advance 0.438; +}; +glyph 109 +{ + texcoords 0.824219 0.406250 0.859375 0.484375; + size 0.562 0.625; + offset 0.000 0.062; + advance 0.438; +}; +glyph 110 +{ + texcoords 0.886719 0.406250 0.921875 0.484375; + size 0.562 0.625; + offset 0.000 0.062; + advance 0.438; +}; +glyph 111 +{ + texcoords 0.949219 0.398438 0.984375 0.484375; + size 0.562 0.688; + offset 0.000 0.000; + advance 0.438; +}; +glyph 112 +{ + texcoords 0.011719 0.281250 0.046875 0.359375; + size 0.562 0.625; + offset 0.000 0.062; + advance 0.438; +}; +glyph 113 +{ + texcoords 0.074219 0.273438 0.109375 0.359375; + size 0.562 0.688; + offset 0.000 0.000; + advance 0.438; +}; +glyph 114 +{ + texcoords 0.136719 0.281250 0.171875 0.359375; + size 0.562 0.625; + offset 0.000 0.062; + advance 0.438; +}; +glyph 115 +{ + texcoords 0.203125 0.273438 0.230469 0.359375; + size 0.438 0.688; + offset 0.062 0.000; + advance 0.438; +}; +glyph 116 +{ + texcoords 0.269531 0.281250 0.289062 0.359375; + size 0.312 0.625; + offset 0.188 0.062; + advance 0.438; +}; +glyph 117 +{ + texcoords 0.324219 0.273438 0.359375 0.359375; + size 0.562 0.688; + offset 0.000 0.000; + advance 0.438; +}; +glyph 118 +{ + texcoords 0.390625 0.281250 0.417969 0.359375; + size 0.438 0.625; + offset 0.125 0.062; + advance 0.438; +}; +glyph 119 +{ + texcoords 0.449219 0.281250 0.484375 0.359375; + size 0.562 0.625; + offset 0.000 0.062; + advance 0.438; +}; +glyph 120 +{ + texcoords 0.515625 0.281250 0.542969 0.351562; + size 0.438 0.562; + offset 0.062 0.062; + advance 0.438; +}; +glyph 121 +{ + texcoords 0.578125 0.281250 0.609375 0.359375; + size 0.500 0.625; + offset 0.062 0.062; + advance 0.438; +}; +glyph 122 +{ + texcoords 0.640625 0.273438 0.667969 0.359375; + size 0.438 0.688; + offset 0.062 0.000; + advance 0.438; +}; +glyph 123 +{ + texcoords 0.707031 0.273438 0.730469 0.359375; + size 0.375 0.688; + offset 0.000 0.000; + advance 0.250; +}; +glyph 124 +{ + texcoords 0.773438 0.281250 0.789062 0.351562; + size 0.250 0.562; + offset 0.000 0.062; + advance 0.125; +}; +glyph 125 +{ + texcoords 0.832031 0.273438 0.855469 0.359375; + size 0.375 0.688; + offset 0.000 0.000; + advance 0.250; +}; +glyph 126 +{ + texcoords 0.886719 0.273438 0.921875 0.367188; + size 0.562 0.750; + offset 0.000 0.000; + advance 0.438; +}; diff --git a/digitalreadout-16.png b/digitalreadout-16.png new file mode 100644 index 0000000000000000000000000000000000000000..e9f7684d8910241be3672786d3c4a7e1efdec4d8 GIT binary patch literal 3312 zcmcgv`#%%<|KFH1Cgu_%_i=JbbLrxCOi~j?rxdcxC6{9|moUdPx49*ka;b6DX= zggA4*6xv3KVitBrWGuIh&1aqOknbS^==K%nqXb4D>1c_SLHbOE0u&3#?<8gOf*|#g0oQofngh5I7T2jQ!_qa;T zSyf%w*JvQ~X+%zYoc^=3ndj*H%UQn`_Jq7ImLoNutai@Pd(~(nqjXF8Stm~?#Sx<# zzc-r;kx+V`L`!jW`DT!dCQK%+@004=4tPUt13Ksu&^U*Z{!Btat%Bt#$QP7lsQYrV?{)EKzfsY|Kvffa}a1RR1;9 zV72Xm;@yDUcH;tD%jDL(y#Xn*F1SBJr$Ed87T~O%J-5#e?ZuD1UNS(9$+N@G2wMlx+^ca_ttLu>I{VRa;t!PHi)s37 zSZ=QH;ymo3nhj0ML@NA_#kC&Lna<;WeYR^IWqZ|>#i`6(r}hysA~FEebpe$jz$uSLs473w|C{^450S+1 zymwCXX?MAM$@DcMef;OBLDT)m{8MqfWMlF#mEpZz*YT5=+8s1}_Z`)rZ2rk+Rm`p{ zaXz_S3a|f+tP-qu8+^(?z(JJX?=Hy(a1cn6X}XDrqH>PiHn`-77k~s1{0-CqL#o2I zYU==H7#$7wh`723{jS$a$Q#eY72D!~9%N#GZQ*@Eb$PzyyUFxe9-O#dhy{dOzCfq9Mhp{&s6_e+z< zwdfG;c*h{<2^_(S{IY#I7S%f}MfEW|g`nWLXz2t{p}+e~u;(w&B9Ga1A4D10o$Q}I zVJ{N+eQ8MJ%46G_7>zhJi#dyPDb)s@3C$M^UVMA+25=#><`A*)kSsq-h3(bBQ~Gw@ zar897mh8K}Lw;k9OX9R+=}oNX=hDRiNsv7XaWuIGQ^iD*)aI;du5<(6PtiG2Pb7r&$1F}B>(MyuXgu(= z-#Xc9|7axe)wAG`m))`p=dz+t23gzX@e+=^6_mz%cd^~9N>=vet?u)Pj#$b8yEBTx zDy$n4c$3u&QSxsLbrYn#x311KPW7#~85ZrB5Pd3V`ouP(cj5a;93nu-(tnYXHiM_rXJvab5z!&zpI}fsk5v)Fnz8{ zsy827Ls*l{^#5{VwQ?&JtM5#nZl;HD&CwaZw4k3I^hxoyOM3huu)P``Ns`-N6qYhG z&@_sON-|J>ak35RoBBtZ$Ow$gC@5hTL$!Ykp1>}6s9(QOFLeXr56m<%IIUNKOb~cu z_xyTr2PLbB)e`qKHtn9cd(&h0-e3N~bF&kR{vYA9(YZU9bv{)LgK0q_V<$)6tQq$W z7%V*fjYV8kJR5QI$!>9zXq+$c?P_y_KqETV_=hK8ywA*%2H9T z&?w;s1x}mjapr1vh?Xbr5DbNU$Bc~2im!L+M=@(@F>;fR;(qbuRckMG2O zxs!*Dz+}A9%4xUbr5b4tYg}AW+>lc?WSNyK4Z6R2>wUr-A;SqeZvC5z-PCw_{onSt zIq)kLI~n#(A=YFsr;MjDx!1kveC4(m+}mxX5+h%iCqQb`P;sTxDwC%;jjpnjuWM1BUO_5u_H&1dof9|`@^?v=7tB+5pA2W|T ztk`IVug9k!MN8H46H0JfdnvUmv2B!Ocy+#8f9p8=(lmCVJ@bxYNOS3Tsg#S8<>nO* z17O_Evt@%4sLn+4YD5V>IiZ=RGjk{}ffwP=yWUos_h*>Z4dYTxzqJ0?qlxu7j z=yJ{{B8phD^4nXG_|iH(Pxzd4^SDQ!R_1*u`(6v|%C1@V`X^PjULfc|R1%3&MAccL zgG8zHeSF@+hAbT?s|#Ei5Ar^D_S*ePLA=eNt=9#|=t_@~$^*lKT1b@Y_70??Y=m0r zmNh#%F|6&gzK>l&o#Bp7H4$YtH^pn#KrOZ2bm{p}g_pdt%VFCMs)TmB7IU2`2VY

@?Mw>&Sy92>{Imasv0)N3cm-ZwMoT=p6Q z(9_Q7PHf@xL>FpgtiPs?gJl{K8^;LFfeqZzmNKS-Utc4L1xffQ1YC3`Qf|n;IP%Ud zc7S6HsQaR4R;W?Rk*7<|zL8kDl!(B=wdAdAe0F8~jl$BAXL-^C#FG?zt=I~Ugl_SiPVsMt2)uVrfID8`EXgR7AV#a98HYqy|;l_$~Rur`uy?{1#d4FiXTU}t05ka1+P*LY4D9PSP z_BC;NUk0{;JyM~X28m8)fDJ2@-VoVwRGfUY5P-X*hSi*{I)O`rGQ*3hlq&woqi4N`-x)VD~Q?S52WNo=Kf#i+aHt z0%>UOpS1z1l|P~`=371x6hpGm9ecKo5gF5=V0FGiA0kIFB-^+((AXcV^Il(BA7{SZ z$F`1fs6F>RU;R|aw0;TjOgAB0a0P3P5P>y|5J8>fo1@HiYh{7lZOHWjp5{Aher_G- zBeMV0_fe?I_S&YrCv12d(u2>-hcc}k4%4E4yrP{zTjcx?Oh}TfMA*+meLt!2#ZA}1 zG!sbyP!y$UX^TLt2o%cm#hp;|P&vMMnh@~MVr|_uuxMtF-5NM^Ho7TAwD_9{i&1j^ zzYzb(QHkhSNCWXieUi=V-w6QY3Yg|Kih@Yw!ITyNG(CGRApN~o$6I!$2HkHSN}CV> z?MYBXP?q(HuTWoBv?NR<|04=`2(+o(Jq%k1lektO5&s%p6ip%rQIf+d%Beb)>p>!O z*NH;Fg`qf-ivU=Qm-TbZ5Lxw@hprU!R?q&I9!~ dNfG+F%nb_jT7qq!#~!DIN(x zeo#G60FZ^#4)f4_q=>4`-an`FPtvQF2ouqvtZl@g2uVFWbsepa=(LcBY)MglND;^7 hG6DZC7ymOTgwjh2=lNyi*FQcqr%$>%zHsnO`4?0uV|D-l literal 0 HcmV?d00001 diff --git a/engineer.res b/engineer.res new file mode 100644 index 0000000..191663f --- /dev/null +++ b/engineer.res @@ -0,0 +1,226 @@ +font "dejavu-12.font"; +font "digitalreadout-16.font"; + +texture "gui.png"; + +graphic "button" +{ + texture "gui"; + slice 8 112 21 10; + border { top 2; right 3; bottom 4; left 2; }; + shadow { top 0; right 1; bottom 2; left 0; }; +}; + +graphic "buttondown" +{ + texture "gui"; + slice 38 112 21 10; + border { top 2; right 3; bottom 4; left 2; }; + shadow { top 0; right 1; bottom 2; left 0; }; +}; + +graphic "greenbutton" +{ + texture "gui"; + slice 8 97 21 10; + border { top 2; right 3; bottom 4; left 2; }; + shadow { top 0; right 1; bottom 2; left 0; }; +}; + +graphic "greenbuttondown" +{ + texture "gui"; + slice 38 97 21 10; + border { top 2; right 3; bottom 4; left 2; }; + shadow { top 0; right 1; bottom 2; left 0; }; +}; + +graphic "redbutton" +{ + texture "gui"; + slice 8 82 21 10; + border { top 2; right 3; bottom 4; left 2; }; + shadow { top 0; right 1; bottom 2; left 0; }; +}; + +graphic "redbuttondown" +{ + texture "gui"; + slice 38 82 21 10; + border { top 2; right 3; bottom 4; left 2; }; + shadow { top 0; right 1; bottom 2; left 0; }; +}; + +graphic "panel" +{ + texture "gui"; + slice 8 158 30 20; + border { top 2; right 2; bottom 2; left 2; }; +}; + +graphic "greenlamp" +{ + texture "gui"; + slice 87 222 12 12; +}; + +graphic "greenlamplit" +{ + texture "gui"; + slice 87 236 12 12; +}; + +graphic "redlamp" +{ + texture "gui"; + slice 101 222 12 12; +}; + +graphic "redlamplit" +{ + texture "gui"; + slice 101 236 12 12; +}; + +graphic "digidisplay" +{ + texture "gui"; + slice 14 226 48 24; + border { top 5; right 5; bottom 5; left 5; }; +}; + +graphic "hsliderslot" +{ + texture "gui"; + slice 8 188 60 10; + border { top 0; right 5; bottom 0; left 5; }; +}; + +graphic "hslider" +{ + texture "gui"; + slice 73 105 32 13; + shadow { top 0; right 2; bottom 3; left 0; }; +}; + +style "button" "" +{ + font_color 0 0 0; + + part "button" + { + graphic normal "button"; + graphic active "buttondown"; + align 0 -1; + fill false false; + }; + + part "text" + { + align 0 1; + }; +}; + +style "button" "green" +{ + font_color 0 0 0; + + part "button" + { + graphic normal "greenbutton"; + graphic active "greenbuttondown"; + align 0 -1; + fill false false; + }; + + part "text" + { + align 0 1; + }; +}; + +style "button" "red" +{ + font_color 0 0 0; + + part "button" + { + graphic normal "redbutton"; + graphic active "redbuttondown"; + align 0 -1; + fill false false; + }; + + part "text" + { + align 0 1; + }; +}; + +style "panel" "" +{ + part "background" + { + graphic normal "panel"; + }; + part "children"; +}; + +style "indicator" ""; + +style "indicator" "green" +{ + part "lamp" + { + graphic normal "greenlamp"; + graphic active "greenlamplit"; + fill false false; + }; +}; + +style "indicator" "red" +{ + part "lamp" + { + graphic normal "redlamp"; + graphic active "redlamplit"; + fill false false; + }; +}; + +style "label" "" +{ + font_color 0 0 0; + part "text"; +}; + +style "label" "digital" +{ + font "digitalreadout-16"; + font_color 0.3 1 0.3; + + part "background" + { + graphic normal "digidisplay"; + }; + + part "text" + { + fill false false; + align 0 0; + }; +}; + +style "hslider" "" +{ + part "background" + { + graphic normal "hsliderslot"; + fill true false; + }; + + part "slider" + { + graphic normal "hslider"; + }; +}; diff --git a/gui.blend b/gui.blend new file mode 100644 index 0000000000000000000000000000000000000000..f53efdaa83519a4d35cafb958bdf09a9308387d6 GIT binary patch literal 512024 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 literal 0 HcmV?d00001 diff --git a/source/3d/layout.cpp b/source/3d/layout.cpp new file mode 100644 index 0000000..b90f553 --- /dev/null +++ b/source/3d/layout.cpp @@ -0,0 +1,132 @@ +#include +#include +#include +#include +#include +#include +#include "layout.h" + +using namespace std; +using namespace Msp; + +namespace Marklin { + +Layout3D::Layout3D(Layout &l): + layout(l), + quality(4) +{ + layout.signal_track_added.connect(sigc::mem_fun(this, &Layout3D::track_added)); + layout.signal_track_removed.connect(sigc::mem_fun(this, &Layout3D::track_removed)); +} + +Layout3D::~Layout3D() +{ + for(Track3DSeq::iterator i=tracks.begin(); i!=tracks.end(); ++i) + delete *i; +} + +void Layout3D::set_quality(unsigned q) +{ + quality=q; + for(Track3DSeq::iterator i=tracks.begin(); i!=tracks.end(); ++i) + (*i)->set_quality(quality); +} + +void Layout3D::render(bool endpoints) +{ + GL::Texture::unbind(); + glEnable(GL_DEPTH_TEST); + + for(Track3DSeq::iterator i=tracks.begin(); i!=tracks.end(); ++i) + (*i)->render(); + + if(endpoints) + { + glDepthMask(false); + for(Track3DSeq::iterator i=tracks.begin(); i!=tracks.end(); ++i) + (*i)->render_endpoints(); + glDepthMask(true); + } +} + +Track3D *Layout3D::get_track(const Track *t) +{ + for(Track3DSeq::iterator i=tracks.begin(); i!=tracks.end(); ++i) + if(&(*i)->get_track()==t) + return *i; + + return 0; +} + +Track3D *Layout3D::pick_track(float x, float y, float size) +{ + vector select_buf; + GL::select_buffer(select_buf); + GL::render_mode(GL::SELECT); + + glPushMatrix(); + glLoadIdentity(); + + double clip[4]; + clip[0]=1; + clip[1]=0; + clip[2]=x-size; + clip[3]=0; + glClipPlane(GL_CLIP_PLANE0, clip); + glEnable(GL_CLIP_PLANE0); + + clip[0]=-1; + clip[2]=-(x+size); + glClipPlane(GL_CLIP_PLANE1, clip); + glEnable(GL_CLIP_PLANE1); + + clip[0]=0; + clip[1]=1; + clip[2]=y-size; + glClipPlane(GL_CLIP_PLANE2, clip); + glEnable(GL_CLIP_PLANE2); + + clip[1]=-1; + clip[2]=-(y+size); + glClipPlane(GL_CLIP_PLANE3, clip); + glEnable(GL_CLIP_PLANE3); + + glPopMatrix(); + + render(); + + glDisable(GL_CLIP_PLANE0); + glDisable(GL_CLIP_PLANE1); + glDisable(GL_CLIP_PLANE2); + glDisable(GL_CLIP_PLANE3); + + GL::render_mode(GL::RENDER); + Track3D *track=0; + unsigned track_depth=numeric_limits::max(); + for(vector::iterator i=select_buf.begin(); i!=select_buf.end(); ++i) + if(i->min_depth(i->names.back()); + track_depth=i->min_depth; + } + + return track; +} + +void Layout3D::track_added(Track *t) +{ + tracks.push_back(new Track3D(*t, quality)); +} + +void Layout3D::track_removed(Track *t) +{ + for(Track3DSeq::iterator i=tracks.begin(); i!=tracks.end(); ++i) + if(&(*i)->get_track()==t) + { + delete *i; + tracks.erase(i); + return; + } +} + +} // namespace Marklin diff --git a/source/3d/layout.h b/source/3d/layout.h new file mode 100644 index 0000000..09189ed --- /dev/null +++ b/source/3d/layout.h @@ -0,0 +1,32 @@ +#ifndef MARKLIN3D_LAYOUT_H_ +#define MARKLIN3D_LAYOUT_H_ + +#include "libmarklin/layout.h" +#include "track.h" + +namespace Marklin { + +class Layout3D +{ +public: + Layout3D(Layout &); + ~Layout3D(); + + void set_quality(unsigned); + const Track3DSeq &get_tracks() const { return tracks; } + void render(bool =false); + Track3D *get_track(const Track *); + Track3D *pick_track(float, float, float); +private: + Layout &layout; + Track3DSeq tracks; + unsigned quality; + + void track_added(Track *); + void track_removed(Track *); +}; + +} // namespace Marklin + +#endif + diff --git a/source/3d/misc.h b/source/3d/misc.h new file mode 100644 index 0000000..8d77ee2 --- /dev/null +++ b/source/3d/misc.h @@ -0,0 +1,15 @@ +#ifndef MARKLIN3D_MISC_H_ +#define MARKLIN3D_MISC_H_ + +namespace Marklin { + +struct Color +{ + float r, g, b; + + Color(float r_, float g_, float b_): r(r_), g(g_), b(b_) { } +}; + +} // namespace Marklin + +#endif diff --git a/source/3d/track.cpp b/source/3d/track.cpp new file mode 100644 index 0000000..e64f42e --- /dev/null +++ b/source/3d/track.cpp @@ -0,0 +1,253 @@ +#include +#include +#include +#include "track.h" + +using namespace std; +using namespace Msp; + +#include + +namespace Marklin { + +Track3D::Track3D(Track &t, unsigned q): + track(t), + color(1, 1, 1), + varray((GL::NORMAL3, GL::VERTEX3)), + quality(q) +{ + build_object(); +} + +void Track3D::set_quality(unsigned q) +{ + quality=q; + build_object(); +} + +void Track3D::get_bounds(float angle, Point &minp, Point &maxp) const +{ + const Point &pos=track.get_position(); + float rot=track.get_rotation(); + + float c=cos(-angle); + float s=sin(-angle); + + minp.x=maxp.x=c*pos.x-s*pos.y; + minp.y=maxp.y=s*pos.x+c*pos.y; + + float c2=cos(rot-angle); + float s2=sin(rot-angle); + + for(vector::const_iterator i=border.begin(); i!=border.end(); ++i) + { + float x=c*pos.x-s*pos.y + c2*i->x-s2*i->y; + float y=s*pos.x+c*pos.y + s2*i->x+c2*i->y; + + minp.x=min(minp.x, x); + minp.y=min(minp.y, y); + maxp.x=max(maxp.x, x); + maxp.y=max(maxp.y, y); + } +} + +void Track3D::render() +{ + prepare_render(); + + glPushName((unsigned)this); + + varray.apply(); + glColor4f(0.25*color.r, 0.25*color.g, 0.25*color.b, 1); + glDrawElements(GL_QUADS, base_seq.size(), GL_UNSIGNED_INT, &base_seq[0]); + if(quality>1) + { + glColor4f(0.85*color.r, 0.85*color.g, 0.85*color.b, 1); + glDrawElements(GL_QUADS, rail_seq.size(), GL_UNSIGNED_INT, &rail_seq[0]); + } + + glPopName(); + glPopMatrix(); +} + +void Track3D::render_endpoints() +{ + prepare_render(); + + const Point &pos=track.get_position(); + const Track::EndpointSeq &endpoints=track.get_endpoints(); + for(Track::EndpointSeq::const_iterator i=endpoints.begin(); i!=endpoints.end(); ++i) + { + GL::set(GL_CULL_FACE, i->link); + if(i->link) + glColor4f(0.5, 0, 1, 0.5); + else + glColor4f(1, 0, 0.5, 0.5); + + float c=cos(i->rot); + float s=sin(i->rot); + + glBegin(GL_QUADS); + glVertex3f(i->pos.x-s*0.025, i->pos.y+c*0.025, 0); + glVertex3f(i->pos.x+s*0.025, i->pos.y-c*0.025, 0); + glVertex3f(i->pos.x+s*0.025, i->pos.y-c*0.025, pos.z+i->pos.z+0.02); + glVertex3f(i->pos.x-s*0.025, i->pos.y+c*0.025, pos.z+i->pos.z+0.02); + glEnd(); + } + + glPopMatrix(); +} + +void Track3D::render_route(int route) +{ + prepare_render(); + + varray.apply(); + if(route>=0 && static_cast(route) builder=varray.modify(); + + base_seq.clear(); + rail_seq.clear(); + route_seq.clear(); + route_seq.resize(track.get_n_routes()); + + const Track::PartSeq &parts=track.get_parts(); + unsigned index=0; + for(Track::PartSeq::const_iterator i=parts.begin(); i!=parts.end(); ++i) + build_part(*i, *builder, index); +} + +void Track3D::build_part(const Track::Part &part, GL::VertexArrayBuilder &va_builder, unsigned &base_index) +{ + static vector profile; + if(profile.empty()) + { + profile.push_back(Point(0, -0.02, 0)); + profile.push_back(Point(0, -0.014, 0.008)); + profile.push_back(Point(0, -0.014, 0.008)); + profile.push_back(Point(0, 0.014, 0.008)); + profile.push_back(Point(0, 0.014, 0.008)); + profile.push_back(Point(0, 0.02, 0)); + for(unsigned i=0; i<2; ++i) + { + profile.push_back(Point(0, -0.009+i*0.017, 0.008)); + profile.push_back(Point(0, -0.009+i*0.017, 0.0103)); + profile.push_back(Point(0, -0.009+i*0.017, 0.0103)); + profile.push_back(Point(0, -0.008+i*0.017, 0.0103)); + profile.push_back(Point(0, -0.008+i*0.017, 0.0103)); + profile.push_back(Point(0, -0.008+i*0.017, 0.008)); + } + profile.push_back(Point(0, -0.002, 0.012)); + profile.push_back(Point(0, 0.002, 0.012)); + } + static unsigned psize=profile.size(); + + const float &radius=part.radius; + const float &x=part.x; + const float &y=part.y; + const float &length=part.length; + const float &dir=part.dir; + + unsigned nsegs; + if(radius) + { + nsegs=(unsigned)(part.length*(1<=6) ? length/2 : length; + Point p(x+c*len*i-s*profile[j].y, y+s*len*i+c*profile[j].y, profile[j].z+i*track.get_slope()); + va_builder.vertex(p.x, p.y, p.z); + if(profile[j].z==0) + border.push_back(p); + } + } + } + + for(unsigned i=0; i +#include +#include "libmarklin/track.h" +#include "misc.h" + +namespace Marklin { + +class Track3D +{ +public: + Track3D(Track &, unsigned); + void set_color(const Color &c) { color=c; } + void set_quality(unsigned); + void get_bounds(float, Point &, Point &) const; + Track &get_track() const { return track; } + void render(); + void render_endpoints(); + void render_route(int); +private: + Track &track; + Color color; + std::vector border; + Msp::GL::VertexArray varray; + std::vector base_seq; + std::vector rail_seq; + std::vector > route_seq; + unsigned quality; + + void prepare_render(); + void build_object(); + void build_part(const Track::Part &, Msp::GL::VertexArrayBuilder &, unsigned &); +}; +typedef std::list Track3DSeq; + +} // namespace Marklin + +#endif diff --git a/source/designer/designer.cpp b/source/designer/designer.cpp new file mode 100644 index 0000000..eb5a0ef --- /dev/null +++ b/source/designer/designer.cpp @@ -0,0 +1,667 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "designer.h" +#include "input.h" +#include "manipulator.h" +#include "measure.h" +#include "selection.h" + +using namespace std; +using namespace Marklin; +using namespace Msp; + +Designer::Designer(int argc, char **argv): + screen_w(1280), + screen_h(960), + input(0), + mode(SELECT), + cam_yaw(M_PI/2), + cam_pitch(-M_PI/4), + cam_pos(0, -0.5, 0.5), + shift(false), + move_x(0), + move_y(0), + zoom(0), + rotate(0), + pitch(0) +{ + cout<<"blah?\n"; + catalogue.load("tracks.dat"); + cout<second->copy(); + track->set_position(Point((n%11)*0.1-0.5, 0.2-n/11*0.3, 0)); + track->set_rotation(M_PI/2); + cat_layout->add_track(track); + } + + manipulator=new Manipulator(*this); + manipulator->signal_status.connect(sigc::mem_fun(this, &Designer::manipulation_status)); + manipulator->signal_done.connect(sigc::mem_fun(this, &Designer::manipulation_done)); + + layout=new Layout(catalogue); + layout_3d=new Layout3D(*layout); + + if(argc>1) + { + layout->load(argv[1]); + const Track3DSeq <racks=layout_3d->get_tracks(); + for(Track3DSeq::const_iterator i=ltracks.begin(); i!=ltracks.end(); ++i) + { + if((*i)->get_track().get_sensor_id()) + (*i)->set_color(Color(1, 1, 0.5)); + else if((*i)->get_track().get_turnout_id()) + (*i)->set_color(Color(0.5, 1, 1)); + else if((*i)->get_track().get_flex()) + (*i)->set_color(Color(1, 0.5, 1)); + } + } + + selection=new Selection; + manipulator->set_selection(selection); + + measure=new Measure(*this); + measure->signal_changed.connect(sigc::mem_fun(this, &Designer::measure_changed)); + measure->signal_done.connect(sigc::mem_fun(this, &Designer::measure_done)); +} + +int Designer::main() +{ + setenv("__GL_SYNC_TO_VBLANK", "1", 0); + SDL_Init(SDL_INIT_VIDEO); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4); + SDL_SetVideoMode(screen_w, screen_h, 32, SDL_OPENGL); + SDL_EnableUNICODE(1); + + glEnableClientState(GL_VERTEX_ARRAY); + glEnable(GL_DEPTH_TEST); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_CULL_FACE); + + font=new GL::Font(); + Parser::load(*font, "dejavu.font"); + + mode=SELECT; + + Application::main(); + + delete font; + delete input; + + SDL_Quit(); + + return exit_code; +} + +void Designer::map_pointer_coords(int x, int y, float &gx, float &gy) +{ + float cos_pitch=cos(cam_pitch); + float sin_pitch=sin(cam_pitch); + float cos_yaw=cos(cam_yaw); + float sin_yaw=sin(cam_yaw); + + float rx=sin_yaw*0.55228; + float ry=-cos_yaw*0.55228; + + float ux=cos_yaw*-sin_pitch*0.41421; + float uy=sin_yaw*-sin_pitch*0.41421; + float uz=cos_pitch*0.41421; + + float xf=(float)x*2/screen_w-1; + float yf=1-(float)y*2/screen_h; + + float vx=cos_yaw*cos_pitch + xf*rx + yf*ux; + float vy=sin_yaw*cos_pitch + xf*ry + yf*uy; + float vz=sin_pitch + yf*uz; + + gx=cam_pos.x-vx*cam_pos.z/vz; + gy=cam_pos.y-vy*cam_pos.z/vz; +} + +void Designer::tick() +{ + SDL_Event event; + while(SDL_PollEvent(&event)) + { + float gx,gy; + switch(event.type) + { + case SDL_MOUSEBUTTONDOWN: + map_pointer_coords(event.button.x, event.button.y, gx, gy); + button_press(event.button.x, screen_h-1-event.button.y, gx, gy, event.button.button); + break; + case SDL_MOUSEMOTION: + map_pointer_coords(event.motion.x, event.motion.y, gx, gy); + pointer_motion(event.motion.x, screen_h-1-event.motion.y, gx, gy); + break; + case SDL_KEYDOWN: + key_press(event.key.keysym.sym, event.key.keysym.mod, event.key.keysym.unicode); + break; + case SDL_KEYUP: + key_release(event.key.keysym.sym, event.key.keysym.mod); + break; + case SDL_QUIT: + exit(0); + break; + } + } + + const Time::TimeStamp t=Time::now(); + float dt=(t-last_tick)/Time::sec; + last_tick=t; + + if(move_y) + { + cam_pos.x+=cos(cam_yaw)*dt*move_y; + cam_pos.y+=sin(cam_yaw)*dt*move_y; + } + if(move_x) + { + cam_pos.x+=sin(cam_yaw)*dt*move_x; + cam_pos.y+=-cos(cam_yaw)*dt*move_x; + } + if(zoom) + { + cam_pos.x+=cos(cam_yaw)*cos(cam_pitch)*dt*zoom; + cam_pos.y+=sin(cam_yaw)*cos(cam_pitch)*dt*zoom; + cam_pos.z+=sin(cam_pitch)*dt*zoom; + } + if(rotate) + { + float vx=cos(cam_yaw)*cos(cam_pitch); + float vy=sin(cam_yaw)*cos(cam_pitch); + float vz=sin(cam_pitch); + + float gx=cam_pos.x-vx*cam_pos.z/vz; + float gy=cam_pos.y-vy*cam_pos.z/vz; + float d=sqrt(vx*vx+vy*vy)*cam_pos.z/vz; + + cam_yaw+=M_PI*dt*rotate; + if(cam_yaw>M_PI*2) + cam_yaw-=M_PI*2; + else if(cam_yaw<0) + cam_yaw+=M_PI*2; + + cam_pos.x=gx+cos(cam_yaw)*d; + cam_pos.y=gy+sin(cam_yaw)*d; + } + if(pitch) + { + cam_pitch+=M_PI/2*dt*pitch; + if(cam_pitch>M_PI/12) + cam_pitch=M_PI/12; + else if(cam_pitch<-M_PI/2) + cam_pitch=-M_PI/2; + } + + if(tooltip_timeout && t>tooltip_timeout) + { + Track3D *t3d=0; + + if(mode==CATALOGUE) + t3d=pick_track(pointer_x, pointer_y); + else + t3d=pick_track(pointer_x, pointer_y); + + if(t3d) + { + const Track &track=t3d->get_track(); + ostringstream ss; + ss.precision(2); + ss<(ss.str()); + + move_tooltip(pointer_x, pointer_y); + } + else + tooltip=L""; + + tooltip_timeout=Time::TimeStamp(); + } + + render(); + + SDL_GL_SwapBuffers(); +} + +Designer::~Designer() +{ + delete manipulator; + delete selection; + delete layout; + delete layout_3d; + delete cat_layout; + delete cat_layout_3d; + delete measure; +} + +/*** private ***/ + +void Designer::key_press(unsigned key, unsigned mod, wchar_t ch) +{ + if(mode==INPUT) + { + input->key_press(key, mod, ch); + return; + } + + if(key==SDLK_RSHIFT || key==SDLK_LSHIFT) + shift=true; + + if(key==SDLK_n) + mode=CATALOGUE; + else if(key==SDLK_g) + { + manipulator->start_move(); + mode=MANIPULATE; + } + else if(key==SDLK_r) + { + manipulator->start_rotate(); + mode=MANIPULATE; + } + else if(key==SDLK_d) + { + manipulator->duplicate(); + manipulator->start_move(); + mode=MANIPULATE; + } + else if(key==SDLK_w) + { + input=new Input(*this, "Filename"); + input->signal_cancel.connect(sigc::mem_fun(this, &Designer::input_dismiss)); + input->signal_accept.connect(sigc::mem_fun(this, &Designer::save_accept)); + mode=INPUT; + } + else if(key==SDLK_PLUS) + selection->select_more(); + else if(key==SDLK_l && (mod&KMOD_SHIFT)) + { + const TrackSeq &tracks=layout->get_tracks(); + float len=0; + for(TrackSeq::const_iterator i=tracks.begin(); i!=tracks.end(); ++i) + len+=(*i)->get_total_length(); + cout<<"Total length: "<select_linked(); + else if(key==SDLK_m) + { + measure->start(); + mode=MEASURE; + } + else if(key==SDLK_z) + { + manipulator->start_elevate(); + mode=MANIPULATE; + } + else if(key==SDLK_ESCAPE) + { + if(mode==MANIPULATE) + manipulator->cancel(); + else if(mode==CATALOGUE) + mode=SELECT; + else + selection->clear(); + } + else if(key==SDLK_x) + { + Selection::TrackSet tracks=selection->get_tracks(); + selection->clear(); + for(Selection::TrackSet::iterator i=tracks.begin(); i!=tracks.end(); ++i) + { + layout->remove_track(*i); + delete *i; + } + } + else if(key==SDLK_f && (mod&KMOD_SHIFT)) + { + const Selection::TrackSet &tracks=selection->get_tracks(); + const TrackSeq <racks=layout->get_tracks(); + for(Selection::TrackSet::const_iterator i=tracks.begin(); i!=tracks.end(); ++i) + { + (*i)->set_flex(!(*i)->get_flex()); + (*i)->break_links(); + for(TrackSeq::const_iterator j=ltracks.begin(); j!=ltracks.end(); ++j) + if(*j!=*i) + (*i)->snap_to(**j, true); + + Track3D *t3d=layout_3d->get_track(*i); + if((*i)->get_flex()) + t3d->set_color(Color(1, 0.5, 1)); + else + t3d->set_color(Color(1, 1, 1)); + } + } + else if(key==SDLK_f) + manipulator->flatten(); + else if(key==SDLK_e && (mod&KMOD_SHIFT)) + manipulator->even_slope(true); + else if(key==SDLK_e) + manipulator->even_slope(); + else if(key==SDLK_t) + { + Track *track=selection->get_track(); + if(selection->size()==1 && track->get_n_routes()>1) + { + ostringstream ss; + ss<get_turnout_id(); + input=new Input(*this, "Turnout ID", ss.str()); + input->signal_cancel.connect(sigc::mem_fun(this, &Designer::input_dismiss)); + input->signal_accept.connect(sigc::mem_fun(this, &Designer::turnout_id_accept)); + mode=INPUT; + } + } + else if(key==SDLK_s) + { + Track *track=selection->get_track(); + if(selection->size()==1 && track->get_n_routes()==1) + { + ostringstream ss; + ss<get_sensor_id(); + input=new Input(*this, "Sensor ID", ss.str()); + input->signal_cancel.connect(sigc::mem_fun(this, &Designer::input_dismiss)); + input->signal_accept.connect(sigc::mem_fun(this, &Designer::sensor_id_accept)); + mode=INPUT; + } + } + else if(key==SDLK_RIGHT) + rotate=-1; + else if(key==SDLK_LEFT) + rotate=1; + else if(key==SDLK_UP) + move_y=1; + else if(key==SDLK_DOWN) + move_y=-1; + else if(key==SDLK_INSERT) + zoom=-1; + else if(key==SDLK_PAGEUP) + zoom=1; + else if(key==SDLK_HOME) + pitch=1; + else if(key==SDLK_END) + pitch=-1; + else if(key==SDLK_DELETE) + move_x=-1; + else if(key==SDLK_PAGEDOWN) + move_x=1; +} + +void Designer::key_release(unsigned key, unsigned) +{ + if(mode==INPUT) + return; + + if(key==SDLK_RSHIFT || key==SDLK_LSHIFT) + shift=false; + else if(key==SDLK_RIGHT || key==SDLK_LEFT) + rotate=0; + else if(key==SDLK_UP || key==SDLK_DOWN) + move_y=0; + else if(key==SDLK_INSERT || key==SDLK_PAGEUP) + zoom=0; + else if(key==SDLK_HOME || key==SDLK_END) + pitch=0; + else if(key==SDLK_DELETE || key==SDLK_PAGEDOWN) + move_x=0; +} + +void Designer::button_press(int x, int y, float gx, float gy, unsigned btn) +{ + if(mode==CATALOGUE) + { + if(btn==1) + { + Track3D *ctrack=pick_track(x, y); + if(ctrack) + { + Track *track=ctrack->get_track().copy(); + track->set_position(Point(gx, gy, 0)); + layout->add_track(track); + + selection->clear(); + selection->add_track(track); + + mode=SELECT; + } + } + else + mode=SELECT; + } + else if(mode==SELECT) + { + if(btn==1) + { + Track3D *track=pick_track(x, y); + if(track) + { + if(!shift) + selection->clear(); + selection->toggle_track(&track->get_track()); + } + } + } + else if(mode==MANIPULATE) + manipulator->button_press(x, y, gx, gy, btn); + else if(mode==MEASURE) + measure->button_press(x, y, gx, gy, btn); +} + +void Designer::pointer_motion(int x, int y, float gx, float gy) +{ + if(mode==SELECT || mode==CATALOGUE) + { + pointer_x=x; + pointer_y=y; + tooltip_timeout=Time::now()+100*Time::msec; + } + + if(mode!=INPUT) + { + manipulator->pointer_motion(x, y, gx, gy); + measure->pointer_motion(x, y, gx, gy); + } + + if(mode==MEASURE || mode==MANIPULATE) + move_tooltip(x, y); +} + +void Designer::project_3d() +{ + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glFrustum(-0.055228, 0.055228, -0.041421, 0.041421, 0.1, 10); + glMatrixMode(GL_MODELVIEW); +} + +void Designer::apply_camera() +{ + glLoadIdentity(); + if(mode==CATALOGUE) + glTranslatef(0, 0, -1); + else + { + glRotatef(-cam_pitch*180/M_PI-90, 1, 0, 0); + glRotatef(90-cam_yaw*180/M_PI, 0, 0, 1); + glTranslatef(-cam_pos.x, -cam_pos.y, -cam_pos.z); + } +} + +void Designer::render() +{ + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); + glEnable(GL_DEPTH_TEST); + + project_3d(); + apply_camera(); + if(mode==CATALOGUE) + cat_layout_3d->render(); + else + { + layout_3d->render(true); + manipulator->render(); + if(mode==MEASURE) + measure->render(); + } + + /*glBegin(GL_LINES); + glColor4f(1, 0, 0, 1); + glVertex3f(0, 0, 0); + glVertex3f(0.5, 0, 0); + glColor4f(0, 1, 0, 1); + glVertex3f(0, 0, 0); + glVertex3f(0, 0.5, 0); + glColor4f(0, 0, 1, 1); + glVertex3f(0, 0, 0); + glVertex3f(0, 0, 0.5); + glEnd();*/ + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, screen_w, 0, screen_h, 0, 1); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glDisable(GL_DEPTH_TEST); + + if(!tooltip.empty()) + { + glTranslatef(tooltip_x, tooltip_y, 0); + glScalef(20, 20, 20); + float width=font->get_string_width(tooltip); + glColor4f(0, 0, 0, 0.5); + glBegin(GL_QUADS); + glVertex2f(0, 0); + glVertex2f(width, 0); + glVertex2f(width, 1); + glVertex2f(0, 1); + glEnd(); + glColor4f(1, 1, 1, 1); + font->draw_string(tooltip); + } + + if(mode==INPUT) + input->render(); +} + +Track3D *Designer::pick_track(int x, int y) +{ + Layout3D *l=layout_3d; + if(mode==CATALOGUE) + l=cat_layout_3d; + + float xx=((float)(x-(int)screen_w/2)/screen_h)*0.82843; + float yy=((float)y/screen_h-0.5)*0.82843; + float size=(float)4/screen_h*0.82843; + + project_3d(); + apply_camera(); + + return l->pick_track(xx, yy, size); +} + +void Designer::manipulation_status(const string &status) +{ + tooltip=decode(status); +} + +void Designer::manipulation_done(bool) +{ + mode=SELECT; +} + +void Designer::measure_changed() +{ + float pard=measure->get_parallel_distance()*1000; + float perpd=measure->get_perpendicular_distance()*1000; + float d=sqrt(pard*pard+perpd*perpd); + float adiff=measure->get_angle_difference()*180/M_PI; + ostringstream ss; + ss.precision(3); + ss<<"Par "<(ss.str()); +} + +void Designer::measure_done() +{ + mode=SELECT; +} + +void Designer::move_tooltip(int x, int y) +{ + int w=(int)(font->get_string_width(tooltip)*20); + tooltip_x=max(min((int)screen_w-w, x), 0); + tooltip_y=max(min((int)screen_h-20, y), 0); +} + +void Designer::save_accept() +{ + layout->save(input->get_text()); + + input_dismiss(); +} + +void Designer::turnout_id_accept() +{ + Track *track=selection->get_track(); + unsigned id=lexical_cast(input->get_text()); + track->set_turnout_id(id); + + Track3D *t3d=layout_3d->get_track(track); + if(id) + t3d->set_color(Color(0.5, 1, 1)); + else + t3d->set_color(Color(1, 1, 1)); + + input_dismiss(); +} + +void Designer::sensor_id_accept() +{ + Track *track=selection->get_track(); + unsigned id=lexical_cast(input->get_text()); + track->set_sensor_id(id); + + Track3D *t3d=layout_3d->get_track(track); + if(id) + t3d->set_color(Color(1, 1, 0.5)); + else + t3d->set_color(Color(1, 1, 1)); + + input_dismiss(); +} + +void Designer::input_dismiss() +{ + delete input; + input=0; + mode=SELECT; +} + +Application::RegApp Designer::reg; diff --git a/source/designer/designer.h b/source/designer/designer.h new file mode 100644 index 0000000..ece41a5 --- /dev/null +++ b/source/designer/designer.h @@ -0,0 +1,90 @@ +#ifndef DESIGNER_H_ +#define DESIGNER_H_ + +#include +#include +#include +#include +#include "libmarklin/catalogue.h" +#include "libmarklin/layout.h" +#include "3d/layout.h" +#include "3d/track.h" + +class Input; +class Manipulator; +class Measure; +class Selection; + +class Designer: public Msp::Application +{ +public: + Designer(int, char **); + Marklin::Layout *get_layout() { return layout; } + Marklin::Layout3D *get_layout_3d() { return layout_3d; } + Msp::GL::Font &get_font() { return *font; } + int main(); + void map_pointer_coords(int, int, float &, float &); + ~Designer(); +private: + enum Mode + { + SELECT, + CATALOGUE, + MANIPULATE, + MEASURE, + INPUT + }; + + unsigned screen_w; + unsigned screen_h; + Msp::GL::Font *font; + Marklin::Catalogue catalogue; + Marklin::Layout *layout; + Marklin::Layout3D *layout_3d; + Marklin::Layout *cat_layout; + Marklin::Layout3D *cat_layout_3d; + Selection *selection; + Manipulator *manipulator; + Measure *measure; + Input *input; + Mode mode; + float cam_yaw; + float cam_pitch; + Marklin::Point cam_pos; + bool shift; + int move_x; + int move_y; + int zoom; + int rotate; + int pitch; + int pointer_x; + int pointer_y; + int tooltip_x; + int tooltip_y; + std::wstring tooltip; + Msp::Time::TimeStamp tooltip_timeout; + Msp::Time::TimeStamp last_tick; + + void tick(); + void key_press(unsigned, unsigned, wchar_t); + void key_release(unsigned, unsigned); + void button_press(int, int, float, float, unsigned); + void pointer_motion(int, int, float, float); + void project_3d(); + void apply_camera(); + void render(); + Marklin::Track3D *pick_track(int, int); + void manipulation_status(const std::string &); + void manipulation_done(bool); + void measure_changed(); + void measure_done(); + void move_tooltip(int, int); + void save_accept(); + void turnout_id_accept(); + void sensor_id_accept(); + void input_dismiss(); + + static Msp::Application::RegApp reg; +}; + +#endif diff --git a/source/designer/input.cpp b/source/designer/input.cpp new file mode 100644 index 0000000..1b18646 --- /dev/null +++ b/source/designer/input.cpp @@ -0,0 +1,87 @@ +#include +#include +#include +#include "designer.h" +#include "input.h" + +using namespace std; +using namespace Msp; + +Input::Input(Designer &d, const string &t, const string &e): + designer(d), + title(t), + text(e), + pos(text.size()) +{ } + +void Input::key_press(unsigned key, unsigned, wchar_t ch) +{ + if(key==SDLK_RETURN) + signal_accept.emit(); + else if(key==SDLK_ESCAPE) + signal_cancel.emit(); + else if(key==SDLK_BACKSPACE) + { + if(pos>0) + { + text.erase(pos-1, 1); + --pos; + } + } + else if(key==SDLK_DELETE) + { + if(pos0) + --pos; + } + else if(key==SDLK_RIGHT) + { + if(pos=0x20) + { + text.insert(pos, 1, ch); + ++pos; + } +} + +void Input::render() +{ + glLoadIdentity(); + glTranslatef(300, 450, 0); + + GL::Texture::unbind(); + glColor4f(0.7, 0.7, 0.7, 0.9); + glBegin(GL_QUADS); + glVertex2f(0, 0); + glVertex2f(680, 0); + glVertex2f(680, 60); + glVertex2f(0, 60); + glEnd(); + + glColor4f(0, 0, 0, 1); + glTranslatef(5, 35, 0); + + glPushMatrix(); + glScalef(20, 20, 20); + designer.get_font().draw_string(title); + glPopMatrix(); + + glTranslatef(0, -30, 0); + glPushMatrix(); + glScalef(20, 20, 20); + designer.get_font().draw_string(text); + + glTranslatef(designer.get_font().get_string_width(text.substr(0, pos)), 0, 0); + glDisable(GL_TEXTURE_2D); + glBegin(GL_LINES); + glVertex2f(0, 0); + glVertex2f(0, 1); + glEnd(); + glPopMatrix(); +} diff --git a/source/designer/input.h b/source/designer/input.h new file mode 100644 index 0000000..276ce5f --- /dev/null +++ b/source/designer/input.h @@ -0,0 +1,26 @@ +#ifndef INPUT_H_ +#define INPUT_H_ + +#include +#include + +class Designer; + +class Input +{ +public: + sigc::signal signal_accept; + sigc::signal signal_cancel; + + Input(Designer &, const std::string &, const std::string & =""); + const std::string &get_text() { return text; } + void key_press(unsigned, unsigned, wchar_t); + void render(); +private: + Designer &designer; + std::string title; + std::string text; + unsigned pos; +}; + +#endif diff --git a/source/designer/manipulator.cpp b/source/designer/manipulator.cpp new file mode 100644 index 0000000..cbeb982 --- /dev/null +++ b/source/designer/manipulator.cpp @@ -0,0 +1,479 @@ +#include +#include +#include +#include "3d/layout.h" +#include "designer.h" +#include "manipulator.h" +#include "selection.h" + +using namespace std; +using namespace Marklin; +using namespace Msp; + +#include + +Manipulator::Manipulator(Designer &d): + designer(d), + selection(0), + wrap_rot(0), + mode(NONE), + angle(0) +{ } + +void Manipulator::set_selection(Selection *s) +{ + selection_changed_conn.disconnect(); + + selection=s; + if(selection) + selection_changed_conn=selection->signal_changed.connect(sigc::mem_fun(this, &Manipulator::selection_changed)); + + selection_changed(); +} + +void Manipulator::start_move() +{ + if(mode) + cancel(); + + move_origin=gpointer; + + mode=MOVE; +} + +void Manipulator::start_rotate() +{ + if(mode) + cancel(); + + rot_origin=atan2(gpointer.y-center.y, gpointer.x-center.x); + + mode=ROTATE; +} + +void Manipulator::start_elevate() +{ + if(mode) + cancel(); + + elev_origin=pointer_y; + + mode=ELEVATE; +} + +void Manipulator::duplicate() +{ + if(mode) + cancel(); + + TrackSeq new_tracks; + for(MTrackSeq::iterator i=tracks.begin(); i!=tracks.end(); ++i) + { + Track *track=i->track->copy(); + designer.get_layout()->add_track(track); + new_tracks.push_back(track); + } + + selection->clear(); + for(TrackSeq::iterator i=new_tracks.begin(); i!=new_tracks.end(); ++i) + { + selection->add_track(*i); + for(TrackSeq::iterator j=i; j!=new_tracks.end(); ++j) + if(j!=i) + (*i)->snap_to(**j, true); + } +} + +void Manipulator::flatten() +{ + if(mode) + cancel(); + + if(tracks.empty()) return; + + float z=0; + for(MTrackSeq::iterator i=tracks.begin(); i!=tracks.end(); ++i) + z+=i->track->get_position().z+i->track->get_slope()/2; + z/=tracks.size(); + + for(MTrackSeq::iterator i=tracks.begin(); i!=tracks.end(); ++i) + { + Point p=i->track->get_position(); + i->track->set_position(Point(p.x, p.y, z)); + i->track->set_slope(0); + } + + for(TrackSeq::iterator i=neighbors.begin(); i!=neighbors.end(); ++i) + (*i)->check_slope(); + + update_wrap(); +} + +void Manipulator::even_slope(bool smooth) +{ + if(mode) + cancel(); + + if(neighbors.size()!=2) + return; + + for(MTrackSeq::iterator i=tracks.begin(); i!=tracks.end(); ++i) + if(i->track->get_endpoints().size()!=2) + return; + + TrackSeq tracks2; + for(MTrackSeq::iterator i=tracks.begin(); i!=tracks.end(); ++i) + tracks2.push_back(i->track); + + float total_len=0; + + TrackOrderSeq order; + Track *cur=neighbors.front(); + while(tracks2.size()) + { + bool rev=false; + for(TrackSeq::iterator i=tracks2.begin(); i!=tracks2.end(); ++i) + { + const Track::EndpointSeq &epl=(*i)->get_endpoints(); + if(epl.front().link==cur) + { + cur=*i; + tracks2.erase(i); + break; + } + else if(epl.back().link==cur) + { + cur=*i; + rev=true; + tracks2.erase(i); + break; + } + } + order.push_back(TrackOrder(cur, rev)); + total_len+=cur->get_length(); + } + + const Track::Endpoint *ep=neighbors.front()->get_endpoint_by_link(order.front().track); + float start_z=neighbors.front()->get_position().z+ep->pos.z; + ep=neighbors.back()->get_endpoint_by_link(order.back().track); + float end_z=neighbors.back()->get_position().z+ep->pos.z; + + if(smooth) + { + float dir=(end_z>start_z)?1:-1; + float cur_slope=0; + while((end_z-start_z)*dir/total_len>cur_slope+0.025 && order.size()>2) + { + cur_slope+=0.025; + + float dz=order.front().track->get_length()*dir*cur_slope; + set_slope(order.front(), start_z, dz); + start_z+=dz; + total_len-=order.front().track->get_length(); + order.erase(order.begin()); + + dz=order.back().track->get_length()*dir*cur_slope; + set_slope(order.back(), end_z-dz, dz); + end_z-=dz; + total_len-=order.back().track->get_length(); + order.erase(--order.end()); + } + } + + float cur_z=start_z; + for(TrackOrderSeq::iterator i=order.begin(); i!=order.end(); ++i) + { + float dz=i->track->get_length()*(end_z-start_z)/total_len; + set_slope(*i, cur_z, dz); + cur_z+=dz; + } + + for(TrackSeq::iterator i=neighbors.begin(); i!=neighbors.end(); ++i) + (*i)->check_slope(); + + update_wrap(); +} + +void Manipulator::cancel() +{ + if(!mode) + return; + mode=NONE; + + wrap_pos=center; + for(MTrackSeq::iterator i=tracks.begin(); i!=tracks.end(); ++i) + { + i->track->set_position(Point(center.x+i->pos.x, center.y+i->pos.y, center.z+i->pos.z)); + i->track->set_rotation(i->rot); + } + + for(TrackSeq::iterator i=neighbors.begin(); i!=neighbors.end(); ++i) + (*i)->check_slope(); + + angle=0; + //snapped=0; + + signal_done.emit(false); +} + +void Manipulator::button_press(int, int, float, float, unsigned btn) +{ + if(btn==3) + cancel(); + else if(mode) + { + mode=NONE; + update_wrap(); + //snapped=0; + + for(TrackSeq::iterator i=neighbors.begin(); i!=neighbors.end(); ++i) + for(MTrackSeq::iterator j=tracks.begin(); j!=tracks.end(); ++j) + j->track->break_link(**i); + + const TrackSeq <racks=designer.get_layout()->get_tracks(); + for(TrackSeq::const_iterator i=ltracks.begin(); i!=ltracks.end(); ++i) + { + bool ok=true; + for(MTrackSeq::iterator j=tracks.begin(); (j!=tracks.end() && ok); ++j) + ok=(j->track!=*i); + if(!ok) continue; + + for(MTrackSeq::iterator j=tracks.begin(); j!=tracks.end(); ++j) + j->track->snap_to(**i, true); + } + + update_neighbors(); + + signal_done.emit(true); + } +} + +void Manipulator::pointer_motion(int, int y, float gx, float gy) +{ + pointer_y=y; + gpointer=Point(gx, gy, 0); + + if(mode==MOVE) + { + Point delta(gpointer.x-move_origin.x, gpointer.y-move_origin.y, 0); + + wrap_pos=Point(center.x+delta.x, center.y+delta.y, center.z); + for(MTrackSeq::iterator i=tracks.begin(); i!=tracks.end(); ++i) + { + i->track->set_position(Point(wrap_pos.x+i->pos.x, wrap_pos.y+i->pos.y, wrap_pos.z+i->pos.z)); + i->track->set_rotation(i->rot); + } + + const TrackSeq <racks=designer.get_layout()->get_tracks(); + MTrack *snapped=0; + for(TrackSeq::const_iterator i=ltracks.begin(); (i!=ltracks.end() && !snapped); ++i) + { + bool ok=true; + for(MTrackSeq::iterator j=tracks.begin(); (j!=tracks.end() && ok); ++j) + ok=(j->track!=*i); + if(!ok) continue; + + for(MTrackSeq::iterator j=tracks.begin(); (j!=tracks.end() && !snapped); ++j) + if(j->track->snap_to(**i, false)) + snapped=&*j; + } + + if(snapped) + { + float da=snapped->track->get_rotation()-snapped->rot; + float c=cos(da); + float s=sin(da); + const Point &sp=snapped->track->get_position(); + for(MTrackSeq::iterator i=tracks.begin(); i!=tracks.end(); ++i) + { + if(&*i==snapped) + continue; + + Point dp(i->pos.x-snapped->pos.x, i->pos.y-snapped->pos.y, 0); + i->track->set_position(Point(sp.x+c*dp.x-s*dp.y, sp.y+s*dp.x+c*dp.y, sp.z)); + i->track->set_rotation(i->rot+da); + } + } + } + else if(mode==ROTATE) + { + float a=atan2(gpointer.y-center.y, gpointer.x-center.x); + angle+=a-rot_origin; + rot_origin=a; + + wrap_rot=angle; + for(MTrackSeq::iterator i=tracks.begin(); i!=tracks.end(); ++i) + { + float c=cos(angle); + float s=sin(angle); + i->track->set_position(Point(center.x+c*i->pos.x-s*i->pos.y, center.y+s*i->pos.x+c*i->pos.y, center.z*i->pos.z)); + i->track->set_rotation(angle+i->rot); + } + } + else if(mode==ELEVATE) + { + float dz=(y-elev_origin)/1000.; + + ostringstream ss; + ss.precision(3); + ss<<"Elevation: "<track->set_position(Point(center.x+i->pos.x, center.y+i->pos.y, center.z+i->pos.z+dz)); + + for(TrackSeq::iterator i=neighbors.begin(); i!=neighbors.end(); ++i) + (*i)->check_slope(); + } +} + +void Manipulator::render() +{ + glPushMatrix(); + glTranslatef(wrap_pos.x, wrap_pos.y, wrap_pos.z); + glRotatef(wrap_rot*180/M_PI, 0, 0, 1); + + glLineWidth(2); + glColor4f(0, 1, 0, 0.5); + for(list::iterator i=wrap.begin(); i!=wrap.end(); ++i) + { + glPushMatrix(); + glTranslatef(i->pos.x, i->pos.y, i->pos.z); + glRotatef(i->rot*180/M_PI, 0, 0, 1); + + glBegin(GL_LINE_LOOP); + glVertex2f(-i->width/2, -i->height/2); + glVertex2f(i->width/2, -i->height/2); + glVertex2f(i->width/2, i->height/2); + glVertex2f(-i->width/2, i->height/2); + glEnd(); + + glPopMatrix(); + } + + glPopMatrix(); +} + +/*** private ***/ + +void Manipulator::selection_changed() +{ + if(mode) + cancel(); + + tracks.clear(); + if(selection) + { + const Selection::TrackSet &stracks=selection->get_tracks(); + tracks.insert(tracks.end(), stracks.begin(), stracks.end()); + } + + update_neighbors(); + update_wrap(); +} + +void Manipulator::update_wrap() +{ + wrap.clear(); + float min_x=0,max_x=0; + float min_y=0,max_y=0; + for(MTrackSeq::iterator i=tracks.begin(); i!=tracks.end(); ++i) + { + Track3D *t3d=designer.get_layout_3d()->get_track(i->track); + + TrackWrap tw; + float min_area=100; + for(float a=0; aget_bounds(a, minp, maxp); + float area=(maxp.x-minp.x)*(maxp.y-minp.y); + if(areatrack->get_position(); + i->pos=Point(tp.x-center.x, tp.y-center.y, tp.z); + } + for(list::iterator i=wrap.begin(); i!=wrap.end(); ++i) + { + i->pos.x-=center.x; + i->pos.y-=center.y; + } +} + +void Manipulator::update_neighbors() +{ + neighbors.clear(); + for(MTrackSeq::iterator i=tracks.begin(); i!=tracks.end(); ++i) + { + const Track::EndpointSeq &epl=i->track->get_endpoints(); + for(Track::EndpointSeq::const_iterator j=epl.begin(); j!=epl.end(); ++j) + { + if(!j->link) + continue; + if(find(neighbors.begin(), neighbors.end(), j->link)!=neighbors.end()) + continue; + + bool ok=true; + for(MTrackSeq::iterator k=tracks.begin(); (k!=tracks.end() && ok); ++k) + ok=(k->track!=j->link); + + if(ok) + neighbors.push_back(j->link); + } + } +} + +void Manipulator::set_slope(TrackOrder &track, float z, float dz) +{ + const Point &p=track.track->get_position(); + if(track.rev) + { + track.track->set_position(Point(p.x, p.y, z+dz)); + track.track->set_slope(-dz); + } + else + { + track.track->set_position(Point(p.x, p.y, z)); + track.track->set_slope(dz); + } +} + +Manipulator::MTrack::MTrack(Track *t): + track(t), + pos(track->get_position()), + rot(track->get_rotation()) +{ } diff --git a/source/designer/manipulator.h b/source/designer/manipulator.h new file mode 100644 index 0000000..3a8882e --- /dev/null +++ b/source/designer/manipulator.h @@ -0,0 +1,89 @@ +#ifndef MANIPULATOR_H_ +#define MANIPULATOR_H_ + +#include +#include "3d/track.h" + +class Designer; +class Selection; + +class Manipulator +{ +public: + sigc::signal signal_status; + sigc::signal signal_done; + + Manipulator(Designer &); + void set_selection(Selection *); + void start_move(); + void start_rotate(); + void start_elevate(); + void duplicate(); + void flatten(); + void even_slope(bool =false); + void cancel(); + void button_press(int, int, float, float, unsigned); + void pointer_motion(int, int, float, float); + void render(); +private: + enum Mode + { + NONE, + MOVE, + ROTATE, + ELEVATE + }; + + struct MTrack + { + Marklin::Track *track; + Marklin::Point pos; + float rot; + + MTrack(Marklin::Track *); + }; + typedef std::list MTrackSeq; + + struct TrackOrder + { + Marklin::Track *track; + bool rev; + + TrackOrder(Marklin::Track *t, bool r): track(t), rev(r) { } + }; + typedef std::list TrackOrderSeq; + + struct TrackWrap + { + Marklin::Point pos; + float rot; + float width; + float height; + }; + + Designer &designer; + Selection *selection; + MTrackSeq tracks; + Marklin::Point center; + + std::list wrap; + Marklin::Point wrap_pos; + float wrap_rot; + + Marklin::Point gpointer; + int pointer_y; + Mode mode; + Marklin::Point move_origin; + float angle; + float rot_origin; + int elev_origin; + Marklin::TrackSeq neighbors; + sigc::connection selection_changed_conn; + + void selection_changed(); + void update_wrap(); + void update_neighbors(); + void set_slope(TrackOrder &, float, float); +}; + +#endif diff --git a/source/designer/measure.cpp b/source/designer/measure.cpp new file mode 100644 index 0000000..6f1d7bc --- /dev/null +++ b/source/designer/measure.cpp @@ -0,0 +1,120 @@ +#include +#include +#include "designer.h" +#include "3d/layout.h" +#include "measure.h" + +using namespace Marklin; +using namespace Msp; + +Measure::Measure(Designer &d): + designer(d), + state(NONE) +{ } + +void Measure::start() +{ + state=STARTING; +} + +void Measure::snap_to_tracks(Point &pt, float &dir) +{ + const TrackSeq <racks=designer.get_layout()->get_tracks(); + for(TrackSeq::const_iterator i=ltracks.begin(); i!=ltracks.end(); ++i) + if((*i)->snap(pt, dir)) + return; +} + +void Measure::button_press(int, int, float gx, float gy, unsigned btn) +{ + if(!state) + return; + + if(btn==1) + { + spoint=Point(gx, gy, 0); + sdir=0; + snap_to_tracks(spoint, sdir); + + state=ACTIVE; + } + else if(btn==3) + { + if(state==ACTIVE) + state=STARTING; + else + { + state=NONE; + signal_done.emit(); + } + } +} + +void Measure::pointer_motion(int, int, float gx, float gy) +{ + if(!state) + return; + + pointer=Point(gx, gy, 0); + float dir=sdir; + snap_to_tracks(pointer, dir); + + if(state!=STARTING) + { + Point delta(pointer.x-spoint.x, pointer.y-spoint.y, 0); + float c=cos(sdir); + float s=sin(sdir); + + par_dist=delta.x*c+delta.y*s; + perp_dist=delta.x*s-delta.y*c; + + adiff=dir-sdir+M_PI; + while(adiff<-M_PI) + adiff+=M_PI*2; + while(adiff>M_PI) + adiff-=M_PI*2; + + signal_changed.emit(); + } +} + +void Measure::render() +{ + glPushMatrix(); + if(state==ACTIVE) + glTranslatef(spoint.x, spoint.y, spoint.z); + else if(state==STARTING) + glTranslatef(pointer.x, pointer.y, pointer.z); + + glDisable(GL_CULL_FACE); + glColor4f(1, 1, 1, 1); + glBegin(GL_QUAD_STRIP); + for(unsigned i=0; i<=16; ++i) + { + float x=cos(i*M_PI/8)*0.005; + float y=sin(i*M_PI/8)*0.005; + glVertex3f(x, y, 0); + glVertex3f(x, y, 0.01); + } + glEnd(); + + if(state==ACTIVE) + { + float c=cos(sdir); + float s=sin(sdir); + glBegin(GL_QUAD_STRIP); + glVertex3f(0, 0, 0); + glVertex3f(0, 0, 0.01); + glVertex3f(c*par_dist, s*par_dist, 0); + glVertex3f(c*par_dist, s*par_dist, 0.01); + glVertex3f(pointer.x-spoint.x, pointer.y-spoint.y, 0); + glVertex3f(pointer.x-spoint.x, pointer.y-spoint.y, 0.01); + /*glVertex3f(s*perp_dist, -c*perp_dist, 0); + glVertex3f(s*perp_dist, -c*perp_dist, 0.01);*/ + glVertex3f(0, 0, 0); + glVertex3f(0, 0, 0.01); + glEnd(); + } + + glPopMatrix(); +} diff --git a/source/designer/measure.h b/source/designer/measure.h new file mode 100644 index 0000000..d7c7b77 --- /dev/null +++ b/source/designer/measure.h @@ -0,0 +1,42 @@ +#ifndef MEASURE_H_ +#define MEASURE_H_ + +#include + +class Designer; + +class Measure +{ +public: + sigc::signal signal_done; + sigc::signal signal_changed; + + Measure(Designer &); + float get_parallel_distance() const { return par_dist; } + float get_perpendicular_distance() const { return perp_dist; } + float get_angle_difference() const { return adiff; } + void start(); + void button_press(int, int, float, float, unsigned); + void pointer_motion(int, int, float, float); + void render(); +private: + enum State + { + NONE, + STARTING, + ACTIVE + }; + + Designer &designer; + Marklin::Point pointer; + Marklin::Point spoint; + float sdir; + float par_dist; + float perp_dist; + float adiff; + State state; + + void snap_to_tracks(Marklin::Point &, float &); +}; + +#endif diff --git a/source/designer/selection.cpp b/source/designer/selection.cpp new file mode 100644 index 0000000..a693c0a --- /dev/null +++ b/source/designer/selection.cpp @@ -0,0 +1,89 @@ +#include +#include "selection.h" + +using namespace std; +using namespace Marklin; +using namespace Msp; + +Track *Selection::get_track() const +{ + if(tracks.empty()) + return 0; + else + return *tracks.begin(); +} + +void Selection::clear() +{ + tracks.clear(); + signal_changed.emit(); +} + +void Selection::add_track(Track *t) +{ + if(tracks.insert(t).second) + signal_changed.emit(); +} + +void Selection::remove_track(Track *t) +{ + if(tracks.erase(t)) + signal_changed.emit(); +} + +void Selection::toggle_track(Track *t) +{ + if(!tracks.erase(t)) + tracks.insert(t); + + signal_changed.emit(); +} + +void Selection::select_more() +{ + TrackSet new_tracks; + for(TrackSet::iterator i=tracks.begin(); i!=tracks.end(); ++i) + { + const Track::EndpointSeq &epl=(*i)->get_endpoints(); + for(Track::EndpointSeq::const_iterator j=epl.begin(); j!=epl.end(); ++j) + if(j->link) + new_tracks.insert(j->link); + } + + bool changed=false; + for(TrackSet::iterator i=new_tracks.begin(); i!=new_tracks.end(); ++i) + if(tracks.insert(*i).second) + changed=true; + + if(changed) + signal_changed.emit(); +} + +void Selection::select_linked() +{ + bool changed=false; + TrackSeq queue(tracks.begin(), tracks.end()); + while(!queue.empty()) + { + Track *track=queue.front(); + queue.erase(queue.begin()); + + const Track::EndpointSeq &epl=track->get_endpoints(); + for(Track::EndpointSeq::const_iterator j=epl.begin(); j!=epl.end(); ++j) + if(j->link && tracks.insert(j->link).second) + { + queue.push_back(j->link); + changed=true; + } + } + for(TrackSet::iterator i=tracks.begin(); i!=tracks.end(); ++i) + { + const Track::EndpointSeq &epl=(*i)->get_endpoints(); + for(Track::EndpointSeq::const_iterator j=epl.begin(); j!=epl.end(); ++j) + if(j->link && tracks.insert(j->link).second) + changed=true; + } + + if(changed) + signal_changed.emit(); +} diff --git a/source/designer/selection.h b/source/designer/selection.h new file mode 100644 index 0000000..36e4435 --- /dev/null +++ b/source/designer/selection.h @@ -0,0 +1,28 @@ +#ifndef SELECTION_H_ +#define SELECTION_H_ + +#include +#include +#include "3d/track.h" + +class Selection +{ +public: + typedef std::set TrackSet; + + sigc::signal signal_changed; + + const TrackSet &get_tracks() const { return tracks; } + Marklin::Track *get_track() const; + unsigned size() const { return tracks.size(); } + void clear(); + void add_track(Marklin::Track *); + void remove_track(Marklin::Track *); + void toggle_track(Marklin::Track *); + void select_more(); + void select_linked(); +private: + TrackSet tracks; +}; + +#endif diff --git a/source/engineer/engineer.cpp b/source/engineer/engineer.cpp new file mode 100644 index 0000000..573ef6f --- /dev/null +++ b/source/engineer/engineer.cpp @@ -0,0 +1,485 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "engineer.h" +#include "mainpanel.h" +#include "trainpanel.h" + +using namespace std; +using namespace Marklin; +using namespace Msp; + +#include + +Engineer::Engineer(int argc, char **argv): + screen_w(1280), + screen_h(960), + fullscreen(false), + layout(catalogue), + layout_3d(layout), + no_lighting(false), + placing_train(0) +{ + string res; + bool debug=false; + string device="/dev/ttyS0"; + unsigned quality=4; + + GetOpt getopt; + getopt.add_option('r', "resolution", res, GetOpt::REQUIRED_ARG); + getopt.add_option('f', "fullscreen", fullscreen, GetOpt::NO_ARG); + getopt.add_option('g', "debug", debug, GetOpt::NO_ARG); + getopt.add_option('d', "device", device, GetOpt::REQUIRED_ARG); + getopt.add_option('q', "quality", quality, GetOpt::REQUIRED_ARG); + getopt.add_option( "no-lighting", no_lighting, GetOpt::NO_ARG); + getopt(argc, argv); + + if(!res.empty()) + { + if(RegMatch m=Regex("([1-9][0-9]*)x([1-9][0-9]*)").match(res)) + { + screen_w=lexical_cast(m[1].str); + screen_h=lexical_cast(m[2].str); + } + else + throw UsageError("Invalid resolution"); + } + + if(device!="none") + control.open(device); + + control.set_debug(debug); + + layout_3d.set_quality(quality); + + catalogue.load("tracks.dat"); + + const list &args=getopt.get_args(); + if(args.empty()) + throw UsageError("No layout given"); + layout.load(args.front()); + + trfc_mgr=new TrafficManager(control, layout); + + view_all(); + + /*const TrackSeq &tracks=layout.get_tracks(); + + for(TrackSeq::const_iterator i=tracks.begin(); i!=tracks.end(); ++i) + { + if(unsigned trnt_id=(*i)->get_turnout_id()) + { + Turnout *trnt=new Turnout(control, trnt_id); + trnt->signal_route_changed.connect(sigc::mem_fun(this, &Engineer::turnout_route_changed)); + } + if(unsigned sens_id=(*i)->get_sensor_id()) + { + Sensor *sens=new Sensor(control, sens_id); + sens->signal_state_changed.connect(sigc::bind(sigc::mem_fun(this, &Engineer::sensor_state_changed), sens_id)); + } + }*/ +} + +Engineer::~Engineer() +{ +} + +void Engineer::add_train(unsigned addr) +{ + if(control.get_locomotive(addr)) + return; + + Locomotive *loco=new Locomotive(control, addr); + Train *train=new Train(*trfc_mgr, *loco); + + TrainPanel *tpanel=new TrainPanel(*this, ui_res, *train); + int y=main_panel->get_geometry().y; + for(TrainPanelSeq::iterator i=train_panels.begin(); i!=train_panels.end(); ++i) + y-=(*i)->get_geometry().h; + tpanel->set_position(0, y-tpanel->get_geometry().h); + train_panels.push_back(tpanel); + + placing_train=train; +} + +int Engineer::main() +{ + SDL_Init(SDL_INIT_VIDEO); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4); + SDL_Surface *screen=SDL_SetVideoMode(screen_w, screen_h, 32, SDL_OPENGL|(fullscreen?SDL_FULLSCREEN:0)); + if(!screen) + { + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0); + screen=SDL_SetVideoMode(screen_w, screen_h, 32, SDL_OPENGL|(fullscreen?SDL_FULLSCREEN:0)); + } + if(!screen) + { + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 0); + screen=SDL_SetVideoMode(screen_w, screen_h, 32, SDL_OPENGL|(fullscreen?SDL_FULLSCREEN:0)); + } + if(!screen) + throw Exception("Couldn't create window"); + + glEnableClientState(GL_VERTEX_ARRAY); + glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); + glEnable(GL_COLOR_MATERIAL); + glDepthFunc(GL_LEQUAL); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + font=new GL::Font(); + if(screen_w>=1024) + { + font_size=20; + Parser::load(*font, "dejavu-20.font"); + } + else + { + font_size=12; + Parser::load(*font, "dejavu-12.font"); + } + + Parser::load(ui_res, "engineer.res"); + main_panel=new MainPanel(*this, ui_res); + main_panel->set_position(0, screen_h-main_panel->get_geometry().h); + + Application::main(); + + delete font; + delete main_panel; + + SDL_Quit(); + + return exit_code; +} + +void Engineer::tick() +{ + //cout<<"tick\n"; + + SDL_Event event; + while(SDL_PollEvent(&event)) + { + switch(event.type) + { + case SDL_MOUSEBUTTONDOWN: + button_press(event.button.x, screen_h-1-event.button.y, event.button.button); + break; + case SDL_MOUSEBUTTONUP: + button_release(event.button.x, screen_h-1-event.button.y, event.button.button); + break; + case SDL_MOUSEMOTION: + pointer_motion(event.motion.x, screen_h-1-event.motion.y); + break; + case SDL_KEYDOWN: + key_press(event.key.keysym.sym, event.key.keysym.mod); + break; + case SDL_QUIT: + exit(0); + break; + } + } + + control.tick(); + + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); + + project_3d(); + glLoadIdentity(); + glRotatef(-cam_rot*180/M_PI, 0, 0, 1); + glTranslatef(-cam_pos.x, -cam_pos.y, -cam_pos.z); + + if(!no_lighting) + { + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + float params[4]; + params[0]=0; + params[1]=-0.2; + params[2]=1; + params[3]=0; + glLightfv(GL_LIGHT0, GL_POSITION, params); + } + + //glEnable(GL_DEPTH_TEST); + glEnable(GL_MULTISAMPLE); + + layout_3d.render(); + + glDisable(GL_LIGHTING); + glColor4f(1, 1, 1, 1); + const Track3DSeq <racks=layout_3d.get_tracks(); + for(Track3DSeq::const_iterator i=ltracks.begin(); i!=ltracks.end(); ++i) + { + Track &track=(*i)->get_track(); + if(track.get_turnout_id()) + { + Turnout *trnt=control.get_turnout(track.get_turnout_id()); + if(trnt) + (*i)->render_route(trnt->get_route()); + else + (*i)->render_route(-1); + } + else + (*i)->render_route(-1); + } + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, screen_w, 0, screen_h, 0, 1); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glDisable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + glDisable(GL_MULTISAMPLE); + + main_panel->render(); + for(TrainPanelSeq::iterator i=train_panels.begin(); i!=train_panels.end(); ++i) + (*i)->render(); + + glLoadIdentity(); + glTranslatef(340, 10, 0); + glScalef(20, 20, 20); + glColor4f(1, 1, 1, 1); + font->draw_string(status_text); + + SDL_GL_SwapBuffers(); +} + +void Engineer::key_press(unsigned, unsigned) +{ +} + +void Engineer::button_press(int x, int y, unsigned btn) +{ + if(main_panel->get_geometry().is_inside(x, y)) + { + main_panel->button_press(x, y, btn); + return; + } + for(TrainPanelSeq::iterator i=train_panels.begin(); i!=train_panels.end(); ++i) + if((*i)->get_geometry().is_inside(x, y)) + { + (*i)->button_press(x, y, btn); + return; + } + + Track3D *track=pick_track(x, y); + if(track) + { + if(placing_train) + { + Section *sect=trfc_man->get_section_by_track(track->get_track()); + } + else + { + Turnout *turnout=control.get_turnout(track->get_track().get_turnout_id()); + if(turnout) + turnout->set_route(1-turnout->get_route()); + } + } +} + +void Engineer::button_release(int x, int y, unsigned btn) +{ + if(main_panel->get_geometry().is_inside(x, y)) + { + main_panel->button_release(x, y, btn); + return; + } + for(TrainPanelSeq::iterator i=train_panels.begin(); i!=train_panels.end(); ++i) + if((*i)->get_geometry().is_inside(x, y)) + { + (*i)->button_release(x, y, btn); + return; + } +} + +void Engineer::pointer_motion(int x, int y) +{ + if(main_panel->get_geometry().is_inside(x, y)) + { + main_panel->pointer_motion(x, y); + return; + } + for(TrainPanelSeq::iterator i=train_panels.begin(); i!=train_panels.end(); ++i) + if((*i)->get_geometry().is_inside(x, y)) + { + (*i)->pointer_motion(x, y); + return; + } + + Track3D *track=pick_track(x, y); + if(track && track->get_track().get_turnout_id()) + { + ostringstream ss; + ss<<"Turnout "<get_track().get_turnout_id(); + status_text=ss.str(); + } + else + status_text=""; +} + +void Engineer::view_all() +{ + const Track3DSeq &tracks=layout_3d.get_tracks(); + + cam_rot=0; + float best_height=-1; + float mid_x=0; + float mid_y=0; + for(float angle=0; angleget_bounds(angle, minp, maxp); + min_x=min(min_x, minp.x); + max_x=max(max_x, maxp.x); + min_y=min(min_y, minp.y); + max_y=max(max_y, maxp.y); + } + + float width=max_x-min_x; + float height=max_y-min_y; + height=max(height, width); + + if(heightget_track().get_sensor_id()==addr) + { + if(state) + (*i)->set_color(Color(1, 0, 0)); + else + (*i)->set_color(Color(1, 1, 1)); + } +} + +void Engineer::project_3d() +{ + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + //glFrustum(-0.055228, 0.055228, -0.041421, 0.041421, 0.1, 10); + glFrustum(-0.069036, 0.041421, -0.041421, 0.041421, 0.1, 10); + glMatrixMode(GL_MODELVIEW); +} + +Track3D *Engineer::pick_track(int x, int y) +{ + float xx=((float)(x-(int)screen_w*5/8)/screen_h)*0.82843; + //float xx=((float)(x-(int)screen_w/2)/screen_h)*0.82843; + float yy=((float)y/screen_h-0.5)*0.82843; + float size=(float)4/screen_h*0.82843; + + project_3d(); + glLoadIdentity(); + glRotatef(-cam_rot*180/M_PI, 0, 0, 1); + glTranslatef(-cam_pos.x, -cam_pos.y, -cam_pos.z); + + return layout_3d.pick_track(xx, yy, size); + + /*unsigned select_buf[1024]; + glSelectBuffer(1024, select_buf); + glRenderMode(GL_SELECT); + + //XXX Hardcoded values + float xn=((float)(x-800)/960)*0.082843; + float yn=((float)(y-480)/960)*0.082843; + float size=(float)4/960*0.082843; + + project_3d(); + glLoadIdentity(); + + double clip[4]; + clip[0]=0.1; + clip[1]=0; + clip[2]=xn-size; + clip[3]=0; + glClipPlane(GL_CLIP_PLANE0, clip); + glEnable(GL_CLIP_PLANE0); + + clip[0]=-0.1; + clip[2]=-(xn+size); + glClipPlane(GL_CLIP_PLANE1, clip); + glEnable(GL_CLIP_PLANE1); + + clip[0]=0; + clip[1]=0.1; + clip[2]=yn-size; + glClipPlane(GL_CLIP_PLANE2, clip); + glEnable(GL_CLIP_PLANE2); + + clip[1]=-0.1; + clip[2]=-(yn+size); + glClipPlane(GL_CLIP_PLANE3, clip); + glEnable(GL_CLIP_PLANE3); + + glRotatef(-cam_rot*180/M_PI, 0, 0, 1); + glTranslatef(-cam_pos.x, -cam_pos.y, -cam_pos.z); + + layout_3d.render(); + + glDisable(GL_CLIP_PLANE0); + glDisable(GL_CLIP_PLANE1); + glDisable(GL_CLIP_PLANE2); + glDisable(GL_CLIP_PLANE3); + + unsigned n_records=glRenderMode(GL_RENDER); + if(n_records) + { + Track *track=0; + unsigned i=0; + unsigned track_depth=numeric_limits::max(); + for(unsigned j=0; j Engineer::reg; diff --git a/source/engineer/engineer.h b/source/engineer/engineer.h new file mode 100644 index 0000000..112e52f --- /dev/null +++ b/source/engineer/engineer.h @@ -0,0 +1,65 @@ +#ifndef ENGINEER_H_ +#define ENGINEER_H_ + +#include +#include +#include +#include "libmarklin/catalogue.h" +#include "libmarklin/control.h" +#include "libmarklin/trafficmanager.h" +#include "3d/layout.h" + +class MainPanel; +class TrainPanel; + +class Engineer: public Msp::Application +{ +public: + Engineer(int argc, char **argv); + ~Engineer(); + + Marklin::Control &get_control() { return control; } + unsigned get_screen_width() const { return screen_w; } + unsigned get_screen_height() const { return screen_h; } + unsigned get_font_size() const { return font_size; } + Msp::GL::Font &get_font() { return *font; } + void add_train(unsigned); + int main(); + void quit() { exit(0); } +private: + typedef std::list TrainPanelSeq; + + unsigned screen_w; + unsigned screen_h; + unsigned font_size; + bool fullscreen; + Msp::GL::Font *font; + Marklin::Catalogue catalogue; + Marklin::Layout layout; + Marklin::Layout3D layout_3d; + Marklin::Control control; + Marklin::Point cam_pos; + float cam_rot; + Msp::GLtk::Resources ui_res; + MainPanel *main_panel; + TrainPanelSeq train_panels; + std::string status_text; + bool no_lighting; + Marklin::TrafficManager *trfc_mgr; + Train *placing_train; + + void tick(); + void key_press(unsigned, unsigned); + void button_press(int, int, unsigned); + void button_release(int, int, unsigned); + void pointer_motion(int, int); + void view_all(); + void turnout_route_changed(unsigned); + void sensor_state_changed(bool, unsigned); + void project_3d(); + Marklin::Track3D *pick_track(int, int); + + static Msp::Application::RegApp reg; +}; + +#endif diff --git a/source/engineer/guicomponent.h b/source/engineer/guicomponent.h new file mode 100644 index 0000000..f752936 --- /dev/null +++ b/source/engineer/guicomponent.h @@ -0,0 +1,44 @@ +#ifndef GUICOMPONENT_H_ +#define GUICOMPONENT_H_ + +class GuiComponent +{ +public: + enum HAlign + { + LEFT, + CENTER, + RIGHT + }; + + enum VAlign + { + TOP, + MIDDLE, + BOTTOM + }; + + struct Part + { + enum Type + { + IMAGE, + LABEL, + TEXT + }; + + std::string variation; + std::string state; + Type type; + std::string data; + HAlign align_x; + VAlign align_y; + int offset_x; + int offset_y; + }; + typedef std::list PartSeq; + +private: +}; + +#endif diff --git a/source/engineer/guiresources.h b/source/engineer/guiresources.h new file mode 100644 index 0000000..86d29ce --- /dev/null +++ b/source/engineer/guiresources.h @@ -0,0 +1,4 @@ +#ifndef GUIRESOURCES_H_ +#define GUIRESOURCES_H_ + +#endif diff --git a/source/engineer/mainpanel.cpp b/source/engineer/mainpanel.cpp new file mode 100644 index 0000000..7e68c03 --- /dev/null +++ b/source/engineer/mainpanel.cpp @@ -0,0 +1,70 @@ +#include +#include "engineer.h" +#include "mainpanel.h" + +using namespace Msp; + +MainPanel::MainPanel(Engineer &e, GLtk::Resources &r): + Panel(r), + engineer(e) +{ + set_size(200, 100); + + GLtk::Button *btn; + + add(*(btn=new GLtk::Button(res, "Off"))); + btn->set_geometry(GLtk::Geometry(10, 50, 40, 25)); + btn->set_style("red"); + btn->signal_clicked.connect(sigc::mem_fun(this, &MainPanel::power_off)); + + add(*(ind_off=new GLtk::Indicator(res))); + ind_off->set_geometry(GLtk::Geometry(10, 78, 40, 12)); + ind_off->set_style("red"); + + add(*(btn=new GLtk::Button(res, "On"))); + btn->set_geometry(GLtk::Geometry(50, 50, 40, 25)); + btn->set_style("green"); + btn->signal_clicked.connect(sigc::mem_fun(this, &MainPanel::power_on)); + + add(*(ind_on=new GLtk::Indicator(res))); + ind_on->set_geometry(GLtk::Geometry(50, 78, 40, 12)); + ind_on->set_style("green"); + + add(*(btn=new GLtk::Button(res, "Quit"))); + btn->set_geometry(GLtk::Geometry(150, 50, 40, 25)); + btn->set_style("red"); + btn->signal_clicked.connect(sigc::mem_fun(this, &MainPanel::quit)); + + add(*(btn=new GLtk::Button(res, "+Loc"))); + btn->set_geometry(GLtk::Geometry(10, 10, 40, 25)); + btn->signal_clicked.connect(sigc::mem_fun(this, &MainPanel::new_loc)); + + if(engineer.get_control().get_power()) + ind_on->set_active(true); + else + ind_off->set_active(true); +} + +void MainPanel::power_on() +{ + engineer.get_control().set_power(true); + ind_on->set_active(true); + ind_off->set_active(false); +} + +void MainPanel::power_off() +{ + engineer.get_control().set_power(false); + ind_on->set_active(false); + ind_off->set_active(true); +} + +void MainPanel::new_loc() +{ + engineer.add_train(0); +} + +void MainPanel::quit() +{ + engineer.quit(); +} diff --git a/source/engineer/mainpanel.h b/source/engineer/mainpanel.h new file mode 100644 index 0000000..3d9a2b7 --- /dev/null +++ b/source/engineer/mainpanel.h @@ -0,0 +1,24 @@ +#ifndef MAINPANEL_H_ +#define MAINPANEL_H_ + +#include +#include + +class Engineer; + +class MainPanel: public Msp::GLtk::Panel +{ +public: + MainPanel(Engineer &, Msp::GLtk::Resources &); +private: + Engineer &engineer; + Msp::GLtk::Indicator *ind_on; + Msp::GLtk::Indicator *ind_off; + + void power_on(); + void power_off(); + void new_loc(); + void quit(); +}; + +#endif diff --git a/source/engineer/trainpanel.cpp b/source/engineer/trainpanel.cpp new file mode 100644 index 0000000..d9a8c77 --- /dev/null +++ b/source/engineer/trainpanel.cpp @@ -0,0 +1,36 @@ +#include +#include "trainpanel.h" + +using namespace Msp; +using namespace Marklin; + +TrainPanel::TrainPanel(Engineer &e, const GLtk::Resources &r, Train &t): + Panel(r), + engineer(e), + train(t) +{ + set_size(200, 100); + + add(*(lbl_name=new GLtk::Label(res, "Train 1"))); + lbl_name->set_style("digital"); + lbl_name->set_geometry(GLtk::Geometry(10, geom.h-34, 140, 24)); + + GLtk::Button *btn; + + add(*(btn=new GLtk::Button(res, "Name"))); + btn->set_geometry(GLtk::Geometry(150, geom.h-34, 40, 24)); + + add(*(sld_speed=new GLtk::HSlider(res))); + sld_speed->set_geometry(GLtk::Geometry(10, geom.h-49, 180, 10)); + sld_speed->set_range(0, 14); + sld_speed->set_step(1); + sld_speed->signal_value_changed.connect(sigc::mem_fun(this, &TrainPanel::speed_slider_changed)); + + add(*(btn=new GLtk::Button(res, "Place"))); + btn->set_geometry(GLtk::Geometry(150, geom.h-75, 40, 24)); +} + +void TrainPanel::speed_slider_changed(double v) +{ + train.set_speed(static_cast(v)); +} diff --git a/source/engineer/trainpanel.h b/source/engineer/trainpanel.h new file mode 100644 index 0000000..79b9d49 --- /dev/null +++ b/source/engineer/trainpanel.h @@ -0,0 +1,25 @@ +#ifndef TRAINPANEL_H_ +#define TRAINPANEL_H_ + +#include +#include +#include +#include "libmarklin/train.h" + +class Engineer; + +class TrainPanel: public Msp::GLtk::Panel +{ +public: + TrainPanel(Engineer &, const Msp::GLtk::Resources &, Marklin::Train &); +private: + Engineer &engineer; + Marklin::Train &train; + Msp::GLtk::Label *lbl_name; + Msp::GLtk::HSlider *sld_speed; + Marklin::Locomotive *loco; + + void speed_slider_changed(double); +}; + +#endif diff --git a/source/libmarklin/catalogue.cpp b/source/libmarklin/catalogue.cpp new file mode 100644 index 0000000..3c8e279 --- /dev/null +++ b/source/libmarklin/catalogue.cpp @@ -0,0 +1,63 @@ +#include +#include +#include "catalogue.h" +#include "track.h" + +using namespace std; +using namespace Msp; + +namespace Marklin { + +Track *Catalogue::get_track(unsigned art_nr) +{ + TrackMap::const_iterator i=tracks.find(art_nr); + + if(i!=tracks.end()) + return i->second; + + return 0; +} + +void Catalogue::load(const string &fn) +{ + ifstream in(fn.c_str()); + if(!in) + throw Exception("File not found"); + + Parser::Parser parser(in, fn); + Loader loader(*this); + loader.load(parser); +} + +Catalogue::~Catalogue() +{ + for(TrackMap::iterator i=tracks.begin(); i!=tracks.end(); ++i) + delete i->second; +} + +Catalogue::Loader::Loader(Catalogue &c): + cat(c) +{ + add("track", &Loader::track); +} + +void Catalogue::Loader::track(unsigned art_no) +{ + TrackMap::iterator i=cat.tracks.find(art_no); + if(i!=cat.tracks.end()) + throw Exception("Duplicate track number"); + + Track *trk=new Track(art_no); + try + { + load_sub(*trk); + } + catch(const Msp::Exception &) + { + delete trk; + throw; + } + cat.tracks.insert(TrackMap::value_type(trk->get_article_number(), trk)); +} + +} // namespace Marklin diff --git a/source/libmarklin/catalogue.h b/source/libmarklin/catalogue.h new file mode 100644 index 0000000..1a328bb --- /dev/null +++ b/source/libmarklin/catalogue.h @@ -0,0 +1,35 @@ +#ifndef LIBMARKLIN_CATALOGUE_H_ +#define LIBMARKLIN_CATALOGUE_H_ + +#include +#include + +namespace Marklin { + +class Track; + +class Catalogue +{ +public: + class Loader: public Msp::Parser::Loader + { + public: + Loader(Catalogue &); + private: + Catalogue &cat; + + void track(unsigned); + }; + typedef std::map TrackMap; + + Track *get_track(unsigned); + const TrackMap &get_tracks() const { return tracks; } + void load(const std::string &); + ~Catalogue(); +private: + TrackMap tracks; +}; + +} // namespace Marklin + +#endif diff --git a/source/libmarklin/command.h b/source/libmarklin/command.h new file mode 100644 index 0000000..2106d23 --- /dev/null +++ b/source/libmarklin/command.h @@ -0,0 +1,26 @@ +#ifndef COMMAND_H_ +#define COMMAND_H_ + +#include +#include +#include "constants.h" + +namespace Marklin { + +class Command +{ +public: + sigc::signal signal_done; + + Command(const std::string &c): cmd(c), sent(false) { } + void set_sent(bool s) { sent=s; } + const std::string &get_command() const { return cmd; } + bool get_sent() const { return sent; } +private: + std::string cmd; + bool sent; +}; + +} // namespace Marklin + +#endif diff --git a/source/libmarklin/constants.h b/source/libmarklin/constants.h new file mode 100644 index 0000000..2db75c2 --- /dev/null +++ b/source/libmarklin/constants.h @@ -0,0 +1,52 @@ +#ifndef ERROR_H_ +#define ERROR_H_ + +namespace Marklin { + +enum Error +{ + ERR_NO_ERROR=0, + ERR_SYS_ERROR, + ERR_BAD_PARAM, + ERR_POWER_OFF=0x6, + ERR_NO_LOK_SPACE=0x8, // No space in lok command buffer + ERR_NO_TURNOUT_SPACE, // No space in turnout command buffer + ERR_NO_DATA, // "no Lok status available (Lok is not in a slot)" + ERR_NO_SLOT, // "there is no slot available" + ERR_BAD_LOK_ADDR, + ERR_LOK_BUSY, + ERR_BAD_TURNOUT_ADDR, + ERR_BAD_SO_VALUE, + ERR_NO_I2C_SPACE, + ERR_LOW_TURNOUT_SPACE=0x40, + ERR_LOK_HALTED, + ERR_LOK_POWER_OFF, + ERR_UNKNOWN_ERROR=0xFF +}; + +enum Cmd +{ + CMD_LOK=0x80, + CMD_LOK_STATUS=0x84, + CMD_LOK_CONFIG=0x85, + CMD_FUNC=0x88, + CMD_FUNC_STATUS=0x8C, + CMD_TURNOUT=0x90, + CMD_TURNOUT_FREE=0x93, + CMD_TURNOUT_STATUS=0x94, + CMD_TURNOUT_GROUP_STATUS=0x95, + CMD_SENSOR_STATUS=0x98, + CMD_SENSOR_REPORT=0x99, + CMD_SENSOR_PARAM_SET=0x9D, + CMD_POWER_OFF=0xA6, + CMD_POWER_ON=0xA7, + CMD_NOP=0xC4, + CMD_EVENT=0xC8, + CMD_EVENT_LOK=0xC9, + CMD_EVENT_TURNOUT=0xCA, + CMD_EVENT_SENSOR=0xCB +}; + +} // namespace Marklin + +#endif diff --git a/source/libmarklin/control.cpp b/source/libmarklin/control.cpp new file mode 100644 index 0000000..d371777 --- /dev/null +++ b/source/libmarklin/control.cpp @@ -0,0 +1,329 @@ +#include +#include +#include +#include +#include +#include +#include +#include "command.h" +#include "control.h" + +using namespace std; +using namespace Msp; + +namespace Marklin { + +Control::Control(): + serial_fd(-1), + p50_enabled(false), + power(true), + poll_sensors(false), + debug(false) +{ } + +void Control::set_power(bool p) +{ + power=p; + if(power) + command(string(1, CMD_POWER_ON)); + else + command(string(1, CMD_POWER_OFF)); +} + +Turnout *Control::get_turnout(unsigned id) const +{ + TurnoutMap::const_iterator i=turnouts.find(id); + if(i!=turnouts.end()) + return i->second; + + return 0; +} + +Locomotive *Control::get_locomotive(unsigned id) const +{ + for(LocomotiveSeq::const_iterator i=locomotives.begin(); i!=locomotives.end(); ++i) + if((*i)->get_address()==id) + return *i; + return 0; +} + +Sensor *Control::get_sensor(unsigned id) const +{ + SensorMap::const_iterator i=sensors.find(id); + if(i!=sensors.end()) + return i->second; + + return 0; +} + +void Control::open(const string &dev) +{ + serial_fd=::open(dev.c_str(), O_RDWR); + if(serial_fd<0) + throw Exception("Couldn't open serial port\n"); + + static unsigned baud[]= + { + 2400, B2400, + 4800, B4800, + 9600, B9600, + 19200, B19200, + 0 + }; + + termios attr; + tcgetattr(serial_fd, &attr); + cfmakeraw(&attr); + attr.c_cflag|=CSTOPB; + + bool ok=false; + for(unsigned i=0; baud[i]; i+=2) + { + cfsetospeed(&attr, baud[i+1]); + tcsetattr(serial_fd, TCSADRAIN, &attr); + + write(serial_fd, "\xC4", 1); + + pollfd pfd={serial_fd, POLLIN, 0}; + if(poll(&pfd, 1, 500)>0) + { + cout<<"IB detected at "<get_address())==0) + turnouts.insert(TurnoutMap::value_type(t->get_address(), t)); +} + +void Control::add_locomotive(Locomotive *l) +{ + if(find(locomotives.begin(), locomotives.end(), l)==locomotives.end()) + locomotives.push_back(l); +} + +void Control::add_sensor(Sensor *s) +{ + if(sensors.count(s->get_address())==0) + { + sensors.insert(SensorMap::value_type(s->get_address(), s)); + poll_sensors=true; + } +} + +void Control::tick() +{ + const Time::TimeStamp t=Time::now(); + + if(t>next_event_query) + { + next_event_query=t+300*Time::msec; + command(string(1, CMD_EVENT)).signal_done.connect(sigc::mem_fun(this, &Control::event_query_done)); + } + + if(poll_sensors) + { + unsigned max_addr=(--sensors.end())->second->get_address(); + string cmd(3, 0); + cmd[0]=CMD_SENSOR_PARAM_SET; + cmd[1]=0; + cmd[2]=(max_addr+7)/8; + command(cmd); + command(string(1, CMD_SENSOR_REPORT)); + poll_sensors=false; + } + + if(queue.size() && queue.front().get_sent()) + { + pollfd pfd={serial_fd, POLLIN, 0}; + if(poll(&pfd, 1, 0)>0) + { + string resp=read_reply((Cmd)(unsigned char)queue.front().get_command()[0]); + if(debug) + { + printf("read: "); + for(unsigned i=0; i>(7-j%8))&1; + cout< +#include +#include +#include "constants.h" +#include "sensor.h" +#include "locomotive.h" +#include "turnout.h" + +namespace Marklin { + +class Command; + +class Control +{ +public: + sigc::signal signal_turnout_event; + sigc::signal signal_sensor_event; + + Control(); + void set_power(bool); + void set_debug(bool d) { debug=d; } + bool get_power() const { return power; } + const TurnoutMap &get_turnouts() const { return turnouts; } + Turnout *get_turnout(unsigned) const; + Locomotive *get_locomotive(unsigned) const; + Sensor *get_sensor(unsigned) const; + unsigned get_queue_length() const { return queue.size(); } + void open(const std::string &); + Command &command(const std::string &); + void add_turnout(Turnout *); + void add_locomotive(Locomotive *); + void add_sensor(Sensor *); + void tick(); + ~Control(); +private: + int serial_fd; + bool p50_enabled; + bool power; + std::list queue; + TurnoutMap turnouts; + LocomotiveSeq locomotives; + SensorMap sensors; + Msp::Time::TimeStamp next_event_query; + bool poll_sensors; + bool debug; + + void read_all(int, char *, int); + std::string read_reply(Cmd); + void event_query_done(Error, const std::string &); + void turnout_event_done(Error, const std::string &); + void sensor_event_done(Error, const std::string &); +}; + +} // namespace Marklin + +#endif diff --git a/source/libmarklin/geometry.h b/source/libmarklin/geometry.h new file mode 100644 index 0000000..9dee86e --- /dev/null +++ b/source/libmarklin/geometry.h @@ -0,0 +1,18 @@ +#ifndef LIBMARKLIN_GEOMETRY_H_ +#define LIBMARKLIN_GEOMETRY_H_ + +#include + +namespace Marklin { + +struct Point +{ + float x,y,z; + + Point(): x(0), y(0), z(0) { } + Point(float x_, float y_, float z_): x(x_), y(y_), z(z_) { } +}; + +} // namespace Marklin + +#endif diff --git a/source/libmarklin/layout.cpp b/source/libmarklin/layout.cpp new file mode 100644 index 0000000..1fe06c7 --- /dev/null +++ b/source/libmarklin/layout.cpp @@ -0,0 +1,134 @@ +#include +#include +#include +#include "catalogue.h" +#include "layout.h" + +using namespace std; +using namespace Msp; + +namespace Marklin { + +Layout::Layout(Catalogue &c): + catalogue(c) +{ } + +void Layout::add_track(Track *t) +{ + if(find(tracks.begin(), tracks.end(), t)==tracks.end()) + { + tracks.push_back(t); + signal_track_added.emit(t); + } +} + +void Layout::remove_track(Track *t) +{ + TrackSeq::iterator i=remove_if(tracks.begin(), tracks.end(), bind1st(equal_to(), t)); + if(i!=tracks.end()) + { + tracks.erase(i, tracks.end()); + signal_track_removed.emit(t); + } +} + +void Layout::check_links() +{ + for(TrackSeq::iterator i=tracks.begin(); i!=tracks.end(); ++i) + (*i)->break_links(); + + for(TrackSeq::iterator i=tracks.begin(); i!=tracks.end(); ++i) + for(TrackSeq::iterator j=i; j!=tracks.end(); ++j) + if(j!=i) + (*i)->snap_to(**j, true); +} + +void Layout::load(const string &fn) +{ + ifstream in(fn.c_str()); + if(!in) + throw Exception("Couldn't open file"); + + filename=fn; + Parser::Parser parser(in, fn); + Loader loader(*this); + loader.load(parser); + + check_links(); + + for(TrackSeq::iterator i=tracks.begin(); i!=tracks.end(); ++i) + (*i)->check_slope(); +} + +int Layout::save(const string &fn) +{ + ofstream out(fn.c_str()); + if(!out) return -1; + + filename=fn; + + if(base.size()) + out<<"base \""<get_article_number()<<"\n{\n"; + const Point &p=(*i)->get_position(); + out<<"\tposition "<get_rotation()<<";\n"; + out<<"\tslope "<<(*i)->get_slope()<<";\n"; + + unsigned id=(*i)->get_turnout_id(); + if(id) + out<<"\tturnout_id "<get_sensor_id(); + if(id) + out<<"\tsensor_id "<get_flex()) + out<<"\tflex true;\n"; + + out<<"};\n"; + } + + return 0; +} + +Layout::~Layout() +{ + for(TrackSeq::iterator i=tracks.begin(); i!=tracks.end(); ++i) + delete *i; +} + +/******************* +** Layout::Loader +*/ + +Layout::Loader::Loader(Layout &l): + layout(l) +{ + add("base", &Layout::base); + add("track", &Loader::track); +} + +void Layout::Loader::track(unsigned art_nr) +{ + Track *tmpl=layout.catalogue.get_track(art_nr); + if(!tmpl) + throw Exception("Unknown track"); + + Track *trk=tmpl->copy(); + try + { + load_sub(*trk); + } + catch(Msp::Exception &e) + { + delete trk; + throw; + } + layout.tracks.push_back(trk); + layout.signal_track_added.emit(trk); +} + +} // namespace Marklin diff --git a/source/libmarklin/layout.h b/source/libmarklin/layout.h new file mode 100644 index 0000000..c292af6 --- /dev/null +++ b/source/libmarklin/layout.h @@ -0,0 +1,49 @@ +#ifndef LIBMARKLIN_LAYOUT_H_ +#define LIBMARKLIN_LAYOUT_H_ + +#include +#include +#include "route.h" +#include "track.h" + +namespace Marklin { + +class Catalogue; + +class Layout +{ +public: + class Loader: public Msp::Parser::Loader + { + public: + Loader(Layout &); + Layout &get_object() { return layout; } + private: + Layout &layout; + + void track(unsigned); + }; + + sigc::signal signal_track_added; + sigc::signal signal_track_removed; + + Layout(Catalogue &); + const TrackSeq &get_tracks() const { return tracks; } + void add_track(Track *); + void remove_track(Track *); + void check_links(); + void load(const std::string &); + int save(const std::string &); + ~Layout(); +private: + Catalogue &catalogue; + std::string filename; + std::string base; + TrackSeq tracks; + RouteSeq routes; +}; + +} // namespace Marklin + +#endif + diff --git a/source/libmarklin/locomotive.cpp b/source/libmarklin/locomotive.cpp new file mode 100644 index 0000000..ecacea1 --- /dev/null +++ b/source/libmarklin/locomotive.cpp @@ -0,0 +1,116 @@ +#include +#include +#include "command.h" +#include "constants.h" +#include "control.h" +#include "locomotive.h" + +using namespace std; +using namespace Msp; + +namespace Marklin { + +Locomotive::Locomotive(Control &c, unsigned a): + control(c), + addr(a), + speed(0), + reverse(false), + funcs(0) +{ + control.add_locomotive(this); + + refresh_status(); +} + +void Locomotive::set_speed(unsigned spd) +{ + speed=min(spd, 14U); + + send_command(false); +} + +void Locomotive::set_reverse(bool rev) +{ + if(rev==reverse) + return; + + if(speed) + { + (new Time::Timer((500+speed*150)*Time::msec))->signal_timeout.connect(sigc::mem_fun(this, &Locomotive::reverse_timeout)); + set_speed(0); + } + else + { + reverse=rev; + send_command(false); + } +} + +void Locomotive::set_function(unsigned func, bool state) +{ + if(state) + funcs|=1<>8)&0xFF; + control.command(string(cmd,3)).signal_done.connect(sigc::mem_fun(this,&Locomotive::status_reply)); +} + +void Locomotive::send_command(bool setf) +{ + char cmd[16]; + cmd[0]=CMD_LOK; + cmd[1]=addr&0xFF; + cmd[2]=(addr>>8)&0xFF; + + if(speed==0) + cmd[3]=0; + else if(speed==1) + cmd[3]=2; + else + cmd[3]=(speed*19-18)/2; + + cmd[4]=(reverse?0:0x20) | ((funcs&1)?0x10:0); + + if(setf) + { + cmd[4]|=0x80; + for(unsigned i=0; i<4; ++i) + if((funcs>>i)&2) + cmd[4]|=(1< +#include +#include "constants.h" + +namespace Marklin { + +class Control; + +class Locomotive +{ +public: + Locomotive(Control &, unsigned); + void set_speed(unsigned); + void set_reverse(bool); + void set_function(unsigned, bool); + unsigned get_address() const { return addr; } + unsigned get_speed() const { return speed; } + bool get_reverse() const { return reverse; } + bool get_function(unsigned f) const { return (funcs>>f)&1; } + void refresh_status(); +private: + Control &control; + unsigned addr; + unsigned speed; + bool reverse; + unsigned funcs; + + void send_command(bool); + void status_reply(Error, const std::string &); + bool reverse_timeout(); +}; +typedef std::list LocomotiveSeq; + +} // namespace Marklin + +#endif diff --git a/source/libmarklin/route.cpp b/source/libmarklin/route.cpp new file mode 100644 index 0000000..9894398 --- /dev/null +++ b/source/libmarklin/route.cpp @@ -0,0 +1,7 @@ +#include "route.h" + +namespace Marklin { + + + +} // namespace Marklin diff --git a/source/libmarklin/route.h b/source/libmarklin/route.h new file mode 100644 index 0000000..f5b7602 --- /dev/null +++ b/source/libmarklin/route.h @@ -0,0 +1,28 @@ +#ifndef LIBMARKLIN_ROUTE_H_ +#define LIBMARKLIN_ROUTE_H_ + +#include +#include +#include "track.h" + +namespace Marklin { + +class Route +{ +public: + typedef std::map TurnoutMap; + + Route(); + const TurnoutMap &get_turnouts() const { return turnouts; } + void add_track(Track *); + ~Route(); +private: + std::string name; + TrackSeq tracks; + TurnoutMap turnouts; +}; +typedef std::list RouteSeq; + +} // namespace Marklin + +#endif diff --git a/source/libmarklin/section.cpp b/source/libmarklin/section.cpp new file mode 100644 index 0000000..17a0ce4 --- /dev/null +++ b/source/libmarklin/section.cpp @@ -0,0 +1,183 @@ +#include "control.h" +#include "section.h" +#include "trafficmanager.h" +#include "turnout.h" + +using namespace Msp; + +#include +using namespace std; + +namespace Marklin { + +Section::Section(TrafficManager &tm, Track *start): + trfc_mgr(tm), + id(next_id++), + sensor_id(0), + train(0) +{ + tracks.insert(start); + + if(start->get_sensor_id()) + { + sensor_id=start->get_sensor_id(); + const Track::EndpointSeq &eps=start->get_endpoints(); + for(Track::EndpointSeq::const_iterator i=eps.begin(); i!=eps.end(); ++i) + endpoints.push_back(Endpoint(start, &*i)); + } + else + { + TrackSeq queue; + queue.push_back(start); + + while(!queue.empty()) + { + Track *track=queue.front(); + queue.erase(queue.begin()); + + const Track::EndpointSeq &eps=track->get_endpoints(); + for(Track::EndpointSeq::const_iterator i=eps.begin(); i!=eps.end(); ++i) + if(i->link && tracks.count(i->link)==0) + { + if(!i->link->get_sensor_id()) + { + queue.push_back(i->link); + tracks.insert(i->link); + } + else + endpoints.push_back(Endpoint(track, &*i)); + } + } + } + + unsigned n=0; + for(EndpointSeq::iterator i=endpoints.begin(); i!=endpoints.end(); ++i, ++n) + { + unsigned route=1<routes|=route; + set visited; + find_routes(i->track, i->track_ep, route, visited); + } +} + +const Section::Endpoint *Section::get_endpoint_by_link(const Section *other) const +{ + for(EndpointSeq::const_iterator i=endpoints.begin(); i!=endpoints.end(); ++i) + if(i->link==other) + return &*i; + + return 0; +} + +const Section::Endpoint *Section::traverse(const Endpoint *ep) const +{ + Track *track=ep->track; + const Track::Endpoint *track_ep=ep->track_ep; + + while(1) + { + unsigned cur_route=0; + unsigned tid=track->get_turnout_id(); + if(tid) + { + Turnout *turnout=trfc_mgr.get_control().get_turnout(tid); + if(turnout) + cur_route=turnout->get_route(); + } + + const Track::Endpoint *other_ep=track->traverse(track_ep, cur_route); + if(!other_ep) + return 0; + + for(EndpointSeq::const_iterator i=endpoints.begin(); i!=endpoints.end(); ++i) + if(i->track==track && i->track_ep==other_ep) + return &*i; + + track_ep=track->get_endpoint_by_link(other_ep->link); + track=other_ep->link; + + if(tracks.count(track)==0) + return 0; + } +} + +void Section::check_link(Section &other) +{ + for(EndpointSeq::iterator i=endpoints.begin(); i!=endpoints.end(); ++i) + { + if(i->link) + continue; + + for(EndpointSeq::iterator j=other.endpoints.begin(); j!=other.endpoints.end(); ++j) + if(j->track==i->track_ep->link && j->track_ep->link==i->track && !j->link) + { + i->link=&other; + j->link=this; + } + } +} + +bool Section::reserve(const Train *t) +{ + if(!t || !train) + { + train=t; + return true; + } + else + return false; +} + +void Section::print_debug() +{ + cout<<"Section "<get_sensor_id()) + cout<<", sensor="<<(*tracks.begin())->get_sensor_id(); + cout<<'\n'; + for(EndpointSeq::iterator i=endpoints.begin(); i!=endpoints.end(); ++i) + { + cout<<" Endpoint, link="; + if(i->link) + cout<link->id; + else + cout<<"none"; + cout<<", routes="<routes<<'\n'; + } +} + +void Section::find_routes(Track *track, const Track::Endpoint *track_ep, unsigned route, std::set &visited) +{ + visited.insert(track); + + const Track::EndpointSeq &eps=track->get_endpoints(); + for(Track::EndpointSeq::const_iterator i=eps.begin(); i!=eps.end(); ++i) + { + if(&*i==track_ep) continue; + if(!i->link) continue; + if(!(i->routes&track_ep->routes)) continue; + if(visited.count(i->link)) continue; + + if(tracks.count(i->link)) + find_routes(i->link, i->link->get_endpoint_by_link(track), route, visited); + else + { + for(EndpointSeq::iterator j=endpoints.begin(); j!=endpoints.end(); ++j) + if(j->track==track && j->track_ep==&*i) + j->routes|=route; + } + } + + visited.erase(--visited.end()); +} + +unsigned Section::next_id=1; + + +Section::Endpoint::Endpoint(Track *t, const Track::Endpoint *e): + track(t), + track_ep(e), + link(0), + routes(0) +{ } + +} // namespace Marklin diff --git a/source/libmarklin/section.h b/source/libmarklin/section.h new file mode 100644 index 0000000..679466f --- /dev/null +++ b/source/libmarklin/section.h @@ -0,0 +1,51 @@ +#ifndef MARKLIN_3D_SECTION_H_ +#define MARKLIN_3D_SECTION_H_ + +#include +#include +#include "track.h" + +namespace Marklin { + +class Train; +class TrafficManager; + +class Section +{ +public: + struct Endpoint + { + Track *track; + const Track::Endpoint *track_ep; + Section *link; + unsigned routes; + + Endpoint(Track *, const Track::Endpoint *); + }; + typedef std::list EndpointSeq; + + Section(TrafficManager &, Track *); + unsigned get_sensor_id() const { return sensor_id; } + const TrackSet &get_tracks() const { return tracks; } + const Endpoint *get_endpoint_by_link(const Section *) const; + const Endpoint *traverse(const Endpoint *) const; + void check_link(Section &); + bool reserve(const Train *); + void print_debug(); +private: + TrafficManager &trfc_mgr; + unsigned id; + unsigned sensor_id; + TrackSet tracks; + EndpointSeq endpoints; + const Train *train; + + void find_routes(Track *, const Track::Endpoint *, unsigned, TrackSet &); + + static unsigned next_id; +}; +typedef std::list

SectionSeq; + +} // namespace Marklin + +#endif diff --git a/source/libmarklin/sensor.cpp b/source/libmarklin/sensor.cpp new file mode 100644 index 0000000..f6913ba --- /dev/null +++ b/source/libmarklin/sensor.cpp @@ -0,0 +1,24 @@ +#include "control.h" +#include "sensor.h" + +namespace Marklin { + +Sensor::Sensor(Control &c, unsigned a): + control(c), + addr(a), + state(false) +{ + control.add_sensor(this); + control.signal_sensor_event.connect(sigc::mem_fun(this, &Sensor::sensor_event)); +} + +void Sensor::sensor_event(unsigned a, bool s) +{ + if(a==addr && s!=state) + { + state=s; + signal_state_changed.emit(state); + } +} + +} // namespace Marklin diff --git a/source/libmarklin/sensor.h b/source/libmarklin/sensor.h new file mode 100644 index 0000000..e5e1b43 --- /dev/null +++ b/source/libmarklin/sensor.h @@ -0,0 +1,32 @@ +#ifndef LIBMARKLIN_SENSOR_H_ +#define LIBMARKLIN_SENSOR_H_ + +#include +#include +#include + +namespace Marklin { + +class Control; + +class Sensor +{ +public: + sigc::signal signal_state_changed; + + Sensor(Control &, unsigned); + unsigned get_address() const { return addr; } + bool get_state() const { return state; } +private: + Control &control; + unsigned addr; + bool state; + + void sensor_event(unsigned, bool); +}; +typedef std::list SensorSeq; +typedef std::map SensorMap; + +} // namespace Marklin + +#endif diff --git a/source/libmarklin/track.cpp b/source/libmarklin/track.cpp new file mode 100644 index 0000000..aaf1b75 --- /dev/null +++ b/source/libmarklin/track.cpp @@ -0,0 +1,374 @@ +#include +#include "track.h" + +using namespace std; +using namespace Msp; + +#include + +namespace Marklin { + +Track::Track(unsigned a): + art_nr(a), + rot(0), + slope(0), + flex(false), + turnout_id(0), + sensor_id(0) +{ } + +Track::~Track() +{ + for(EndpointSeq::iterator i=endpoints.begin(); i!=endpoints.end(); ++i) + if(i->link) + { + Track *trk=i->link; + i->link=0; + trk->break_link(*this); + } +} + +void Track::set_position(const Point &p) +{ + pos=p; +} + +void Track::set_rotation(float r) +{ + rot=r; + while(rot<0) + rot+=M_PI*2; + while(rot>M_PI*2) + rot-=M_PI*2; +} + +void Track::set_slope(float s) +{ + if(endpoints.size()!=2) return; + + slope=s; + endpoints.back().pos.z=slope; +} + +const Track::Endpoint *Track::get_endpoint_by_link(Track *other) const +{ + for(EndpointSeq::const_iterator i=endpoints.begin(); i!=endpoints.end(); ++i) + if(i->link==other) + return &*i; + + return 0; +} + +float Track::get_length() const +{ + float len=parts.front().length; + if(parts.front().radius) + len*=parts.front().radius; + return len; +} + +float Track::get_total_length() const +{ + float len=0; + for(PartSeq::const_iterator i=parts.begin(); i!=parts.end(); ++i) + { + float l=i->length; + if(i->radius) + l*=i->radius; + len+=l; + } + return len; +} + +unsigned Track::get_n_routes() const +{ + unsigned n=1; + for(PartSeq::const_iterator i=parts.begin(); i!=parts.end(); ++i) + if(i->route>=n) + n=i->route+1; + return n; +} + +bool Track::snap_to(Track &other, bool link) +{ + float limit=(link && !flex) ? 1e-6 : 1e-4; + for(EndpointSeq::iterator i=endpoints.begin(); i!=endpoints.end(); ++i) + { + float x=pos.x+i->pos.x*cos(rot)-i->pos.y*sin(rot); + float y=pos.y+i->pos.y*cos(rot)+i->pos.x*sin(rot); + for(EndpointSeq::iterator j=other.endpoints.begin(); j!=other.endpoints.end(); ++j) + { + if(j->link) + continue; + + float x2=other.pos.x+j->pos.x*cos(other.rot)-j->pos.y*sin(other.rot); + float y2=other.pos.y+j->pos.y*cos(other.rot)+j->pos.x*sin(other.rot); + float dx=x2-x; + float dy=y2-y; + if(dx*dx+dy*dyrot-i->rot+M_PI); + set_position(Point(x2-(i->pos.x*cos(rot)-i->pos.y*sin(rot)), y2-(i->pos.y*cos(rot)+i->pos.x*sin(rot)), other.pos.z+j->pos.z-i->pos.z)); + if(link) + { + if(i->link) + break_link(*i->link); + i->link=&other; + j->link=this; + } + return true; + } + } + } + + return false; +} + +bool Track::snap(Point &pt, float &d) const +{ + for(EndpointSeq::const_iterator i=endpoints.begin(); i!=endpoints.end(); ++i) + { + float x=pos.x+i->pos.x*cos(rot)-i->pos.y*sin(rot); + float y=pos.y+i->pos.y*cos(rot)+i->pos.x*sin(rot); + float dx=pt.x-x; + float dy=pt.y-y; + if(dx*dx+dy*dy<1e-4) + { + pt.x=x; + pt.y=y; + d=rot+i->rot; + return true; + } + } + + return false; +} + +void Track::break_link(Track &trk) +{ + for(EndpointSeq::iterator i=endpoints.begin(); i!=endpoints.end(); ++i) + if(i->link==&trk) + { + i->link=0; + trk.break_link(*this); + return; + } +} + +void Track::break_links() +{ + for(EndpointSeq::iterator i=endpoints.begin(); i!=endpoints.end(); ++i) + if(i->link) + { + Track *trk=i->link; + i->link=0; + trk->break_link(*this); + } +} + +void Track::check_slope() +{ + if(endpoints.size()!=2) + return; + + Track *link1=endpoints.front().link; + Track *link2=endpoints.back().link; + if(link1 && link2) + { + const Endpoint *ep1=link1->get_endpoint_by_link(this); + const Endpoint *ep2=link2->get_endpoint_by_link(this); + pos.z=link1->pos.z+ep1->pos.z; + slope=(link2->pos.z+ep2->pos.z)-pos.z; + } + else + { + slope=0; + if(link1) + { + const Endpoint *ep=link1->get_endpoint_by_link(this); + pos.z=link1->pos.z+ep->pos.z; + } + else if(link2) + { + const Endpoint *ep=link2->get_endpoint_by_link(this); + pos.z=link2->pos.z+ep->pos.z; + } + } + + endpoints.back().pos.z=slope; +} + +const Track::Endpoint *Track::traverse(const Endpoint *ep, unsigned route) const +{ + if(ep->routes&(1<routes&(1<routes&(1<routes&ep->routes)) + return &*i; + } + + return 0; +} + +Track *Track::copy() const +{ + Track *trk=new Track(*this); + for(EndpointSeq::iterator i=trk->endpoints.begin(); i!=trk->endpoints.end(); ++i) + i->link=0; + trk->turnout_id=0; + trk->sensor_id=0; + + return trk; +} + +/*** private ***/ + +void Track::collect_endpoints() +{ + endpoints.clear(); + + for(PartSeq::iterator i=parts.begin(); i!=parts.end(); ++i) + i->collect_endpoints(endpoints); + endpoints.back().pos.z=slope; + + for(EndpointSeq::iterator i=endpoints.begin(); i!=endpoints.end();) + { + bool rm=false; + for(EndpointSeq::iterator j=i; j!=endpoints.end();) + { + if(j==i) + { + ++j; + continue; + } + float dx=i->pos.x-j->pos.x; + float dy=i->pos.y-j->pos.y; + if(dx*dx+dy*dy<0.0001) + { + float da=i->rot-j->rot; + if(da<-M_PI) + da+=M_PI*2; + if(da>M_PI) + da-=M_PI*2; + if(da<-3 || da>3) + rm=true; + i->routes|=j->routes; + j=endpoints.erase(j); + } + else + ++j; + } + if(rm) + i=endpoints.erase(i); + else + ++i; + } +} + +/******************* +** Track::Loader +*/ + +Track::Loader::Loader(Track &t): + track(t) +{ + add("description", &Track::description); + add("part", &Loader::part); + add("position", &Loader::position); + add("rotation", &Track::rot); + add("slope", &Track::slope); + add("turnout_id", &Track::turnout_id); + add("sensor_id", &Track::sensor_id); + add("flex", &Track::flex); +} + +Track::Loader::~Loader() +{ + track.collect_endpoints(); +} + +void Track::Loader::part() +{ + Part p; + load_sub(p); + track.parts.push_back(p); +} + +void Track::Loader::position(float x, float y, float z) +{ + track.pos=Point(x, y, z); +} + +/******************* +** Track::Part +*/ + +Track::Part::Part(): + x(0), + y(0), + dir(0), + length(0), + radius(0), + route(0), + dead_end(false) +{ } + +void Track::Part::collect_endpoints(EndpointSeq &epl) +{ + epl.push_back(Endpoint(Point(x, y, 0), dir+M_PI, 1< +#include +#include +#include "geometry.h" + +namespace Marklin { + +class Track +{ +public: + class Loader: public Msp::Parser::Loader + { + public: + Loader(Track &); + Track &get_object() { return track; } + ~Loader(); + private: + Track &track; + + void part(); + void position(float, float, float); + }; + + struct Endpoint + { + Point pos; + float rot; + Track *link; + unsigned routes; + + Endpoint(const Point &p, float r, unsigned o): pos(p), rot(r), link(0), routes(o) { } + }; + typedef std::list EndpointSeq; + + struct Part + { + class Loader: public Msp::Parser::Loader + { + public: + Loader(Part &); + Part &get_object() { return part; } + ~Loader(); + private: + Part ∂ + + void start(float, float, float); + }; + + float x,y; + float dir; + float length; + float radius; + unsigned route; + bool dead_end; + + Part(); + void collect_endpoints(EndpointSeq &); + }; + typedef std::list PartSeq; + + Track(unsigned); + ~Track(); + void set_position(const Point &); + void set_rotation(float); + void set_slope(float); + void set_flex(bool f) { flex=f; } + void set_turnout_id(unsigned i) { turnout_id=i; } + void set_sensor_id(unsigned i) { sensor_id=i; } + const Point &get_position() const { return pos; } + float get_rotation() const { return rot; } + unsigned get_article_number() const { return art_nr; } + const PartSeq &get_parts() const { return parts; } + const EndpointSeq &get_endpoints() const { return endpoints; } + const Endpoint *get_endpoint_by_link(Track *) const; + const std::string &get_description() const { return description; } + float get_slope() const { return slope; } + bool get_flex() const { return flex; } + float get_length() const; + float get_total_length() const; + unsigned get_turnout_id() const { return turnout_id; } + unsigned get_sensor_id() const { return sensor_id; } + unsigned get_n_routes() const; + bool snap_to(Track &, bool); + bool snap(Point &, float &) const; + void break_link(Track &); + void break_links(); + void check_slope(); + const Endpoint *traverse(const Endpoint *, unsigned) const; + + /** + Creates a copy of the track. The new track will be almost identical, but + won't have any links to other tracks, nor a turnout or sensor id. + */ + Track *copy() const; +private: + unsigned art_nr; + std::string description; + PartSeq parts; + EndpointSeq endpoints; + Point pos; + float rot; + float slope; + bool flex; + unsigned turnout_id; + unsigned sensor_id; + + // Direct copying not allowed due to links. See the copy() function. + //Track(const Track &); + Track &operator=(const Track &); + + void collect_endpoints(); +}; +typedef std::list TrackSeq; +typedef std::set TrackSet; + +} // namespace Marklin + +#endif diff --git a/source/libmarklin/trafficmanager.cpp b/source/libmarklin/trafficmanager.cpp new file mode 100644 index 0000000..7a47f14 --- /dev/null +++ b/source/libmarklin/trafficmanager.cpp @@ -0,0 +1,71 @@ +#include "control.h" +#include "layout.h" +#include "trafficmanager.h" +#include "turnout.h" + +using namespace std; + +namespace Marklin { + +TrafficManager::TrafficManager(Control &c, Layout &l): + control(c), + layout(l) +{ + const TrackSeq &tracks=layout.get_tracks(); + + TrackSet used_tracks; + for(TrackSeq::const_iterator i=tracks.begin(); i!=tracks.end(); ++i) + { + if(unsigned tid=(*i)->get_turnout_id()) + { + Turnout *t=control.get_turnout(tid); + if(!t) + t=new Turnout(control, tid); + //t->signal_route_changed.connect(sigc::mem_fun(*i, &Track::set_current_route)); + } + if(unsigned sid=(*i)->get_sensor_id()) + { + Sensor *s=control.get_sensor(sid); + if(!s) + s=new Sensor(control, sid); + } + if(used_tracks.count(*i)==0) + { + Section *sect=new Section(*this, *i); + sections.push_back(sect); + used_tracks.insert(sect->get_tracks().begin(), sect->get_tracks().end()); + } + } + + for(SectionSeq::iterator i=sections.begin(); i!=sections.end(); ++i) + { + for(SectionSeq::iterator j=i; j!=sections.end(); ++j) + if(j!=i) + (*i)->check_link(**j); + (*i)->print_debug(); + } +} + +Section *TrafficManager::get_section_by_track(const Track *t) const +{ + for(SectionSeq::const_iterator i=sections.begin(); i!=sections.end(); ++i) + { + const TrackSet &tracks=(*i)->get_tracks(); + if(tracks.count(const_cast(t))) + return *i; + } + + return 0; +} + +void TrafficManager::add_train(Train *t) +{ + if(find(trains.begin(), trains.end(), t)==trains.end()) + trains.push_back(t); +} + +void TrafficManager::turnout_route_changed(unsigned, Turnout *) +{ +} + +} // namespace Marklin diff --git a/source/libmarklin/trafficmanager.h b/source/libmarklin/trafficmanager.h new file mode 100644 index 0000000..11ecc95 --- /dev/null +++ b/source/libmarklin/trafficmanager.h @@ -0,0 +1,31 @@ +#ifndef LIBMARKLIN_TRAFFICMANAGER_H_ +#define LIBMARKLIN_TRAFFICMANAGER_H_ + +#include "section.h" +#include "train.h" + +namespace Marklin { + +class Control; +class Layout; +class Turnout; + +class TrafficManager +{ +public: + TrafficManager(Control &, Layout &); + Control &get_control() const { return control; } + Section *get_section_by_track(const Track *) const; + void add_train(Train *); +private: + Control &control; + Layout &layout; + SectionSeq sections; + TrainSeq trains; + + void turnout_route_changed(unsigned, Turnout *); +}; + +} // namespace Marklin + +#endif diff --git a/source/libmarklin/train.cpp b/source/libmarklin/train.cpp new file mode 100644 index 0000000..73b2823 --- /dev/null +++ b/source/libmarklin/train.cpp @@ -0,0 +1,121 @@ +#include "control.h" +#include "trafficmanager.h" +#include "train.h" + +namespace Marklin { + +Train::Train(TrafficManager &tm, Locomotive &l): + trfc_mgr(tm), + loco(l), + target_speed(0) +{ + trfc_mgr.add_train(this); + trfc_mgr.get_control().signal_sensor_event.connect(sigc::mem_fun(this, &Train::sensor_event)); +} + +void Train::set_speed(unsigned speed) +{ + target_speed=speed; + if(rsv_sections.empty() && !reserve_more()) + return; + loco.set_speed(speed); +} + +void Train::place(Section *sect, const Section::Endpoint *entry) +{ + for(SectRefSeq::iterator i=rsv_sections.begin(); i!=rsv_sections.end();) + { + i->section->reserve(0); + i=rsv_sections.erase(i); + } + + for(SectRefSeq::iterator i=cur_sections.begin(); i!=cur_sections.end();) + { + i->section->reserve(0); + i=cur_sections.erase(i); + } + + if(!sect->reserve(this)) + return; + + cur_sections.push_back(SectionRef(sect, entry)); +} + +bool Train::free_section(Section *sect) +{ + for(SectRefSeq::iterator i=rsv_sections.begin(); i!=rsv_sections.end(); ++i) + if(i->section==sect) + { + while(i!=rsv_sections.end()) + { + i->section->reserve(0); + i=rsv_sections.erase(i); + } + return true; + } + + return false; +} + +void Train::sensor_event(unsigned addr, bool state) +{ + if(state) + { + SectRefSeq::iterator i; + for(i=rsv_sections.begin(); i!=rsv_sections.end(); ++i) + if(i->section->get_sensor_id() && i->section->get_sensor_id()!=addr) + break; + cur_sections.splice(cur_sections.begin(), rsv_sections, rsv_sections.begin(), i); + + reserve_more(); + if(rsv_sections.empty()) + loco.set_speed(0); + } + else + { + SectRefSeq::iterator i; + for(i=cur_sections.begin(); i!=cur_sections.end(); ++i) + if(i->section->get_sensor_id()==addr) + break; + if(i!=cur_sections.end()) + { + ++i; + for(SectRefSeq::iterator j=cur_sections.begin(); j!=i; ++j) + j->section->reserve(0); + cur_sections.erase(cur_sections.begin(), i); + } + + reserve_more(); + } +} + +bool Train::reserve_more() +{ + SectionRef *last=0; + if(!rsv_sections.empty()) + last=&rsv_sections.back(); + else if(!cur_sections.empty()) + last=&cur_sections.back(); + if(!last) + return false; + + bool result=false; + unsigned size=rsv_sections.size(); + while(size<5) + { + const Section::Endpoint *exit=last->section->traverse(last->entry); + if(exit->link->reserve(this)) + { + rsv_sections.push_back(SectionRef(exit->link, exit->link->get_endpoint_by_link(last->section))); + last=&rsv_sections.back(); + ++size; + result=true; + } + else + break; + } + + return result; +} + +} // namespace Marklin diff --git a/source/libmarklin/train.h b/source/libmarklin/train.h new file mode 100644 index 0000000..363fb55 --- /dev/null +++ b/source/libmarklin/train.h @@ -0,0 +1,45 @@ +#ifndef LIBMARKLIN_TRAIN_H_ +#define LIBMARKLIN_TRAIN_H_ + +#include +#include "section.h" + +namespace Marklin { + +class Locomotive; +class TrafficManager; + +class Train: public sigc::trackable +{ +public: + Train(TrafficManager &, Locomotive &); + const std::string &get_name() const { return name; } + void set_speed(unsigned); + void place(Section *, const Section::Endpoint *); + bool free_section(Section *); + void tick(); +private: + struct SectionRef + { + Section *section; + const Section::Endpoint *entry; + + SectionRef(Section *s, const Section::Endpoint *e): section(s), entry(e) { } + }; + typedef std::list SectRefSeq; + + TrafficManager &trfc_mgr; + std::string name; + Locomotive &loco; + SectRefSeq cur_sections; + SectRefSeq rsv_sections; + unsigned target_speed; + + void sensor_event(unsigned, bool); + bool reserve_more(); +}; +typedef std::list TrainSeq; + +} // namespace Marklin + +#endif diff --git a/source/libmarklin/turnout.cpp b/source/libmarklin/turnout.cpp new file mode 100644 index 0000000..eca9ae2 --- /dev/null +++ b/source/libmarklin/turnout.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include "command.h" +#include "control.h" +#include "turnout.h" + +using namespace std; +using namespace Msp; + +namespace Marklin { + +Turnout::Turnout(Control &c, unsigned a): + control(c), + addr(a), + route(0) +{ + control.add_turnout(this); + + control.signal_turnout_event.connect(sigc::mem_fun(this, &Turnout::turnout_event)); + + char cmd[3]; + cmd[0]=CMD_TURNOUT_STATUS; + cmd[1]=addr&0xFF; + cmd[2]=(addr>>8)&0xFF; + control.command(string(cmd,3)).signal_done.connect(sigc::mem_fun(this, &Turnout::status_reply)); +} + +void Turnout::set_route(unsigned r) +{ + route=r; + + command(true); + (new Time::Timer(200*Time::msec))->signal_timeout.connect(sigc::mem_fun(this, &Turnout::switch_timeout)); + + signal_route_changed.emit(route); +} + +void Turnout::command(bool on) +{ + char cmd[3]; + cmd[0]=CMD_TURNOUT; + cmd[1]=addr&0xFF; + cmd[2]=((addr>>8)&0x7); + if(on) + cmd[2]|=0x40; + if(route==0) + cmd[2]|=0x80; + control.command(string(cmd,3)); +} + +void Turnout::status_reply(Error err, const string &reply) +{ + if(err==ERR_NO_ERROR) + { + route=(reply[0]&4)?0:1; + signal_route_changed.emit(route); + } +} + +bool Turnout::switch_timeout() +{ + command(false); + + return false; +} + +void Turnout::turnout_event(unsigned a, bool r) +{ + if(a==addr && r!=route) + { + route=r; + signal_route_changed.emit(route); + } +} + +} // namespace Marklin diff --git a/source/libmarklin/turnout.h b/source/libmarklin/turnout.h new file mode 100644 index 0000000..edf1c03 --- /dev/null +++ b/source/libmarklin/turnout.h @@ -0,0 +1,38 @@ +#ifndef LIBMARKLIN_TURNOUT_H_ +#define LIBMARKLIN_TURNOUT_H_ + +#include +#include +#include +#include +#include "constants.h" + +namespace Marklin { + +class Control; + +class Turnout +{ +public: + sigc::signal signal_route_changed; + + Turnout(Control &, unsigned); + void set_route(unsigned); + unsigned get_address() const { return addr; } + unsigned get_route() const { return route; } +private: + Control &control; + unsigned addr; + unsigned route; + + void command(bool); + void status_reply(Error, const std::string &); + bool switch_timeout(); + void turnout_event(unsigned, bool); +}; +typedef std::list TurnoutSeq; +typedef std::map TurnoutMap; + +} // namespace Marklin + +#endif diff --git a/source/shoppinglist/main.cpp b/source/shoppinglist/main.cpp new file mode 100644 index 0000000..a87e2f8 --- /dev/null +++ b/source/shoppinglist/main.cpp @@ -0,0 +1,138 @@ +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace Msp; + +class ShoppingList +{ +public: + ShoppingList(int, char **); + void print(ostream &); +private: + class InventoryLoader: public Parser::Loader + { + public: + InventoryLoader(ShoppingList &); + private: + ShoppingList &sl; + + void track(unsigned, unsigned); + }; + + class LayoutLoader: public Parser::Loader + { + public: + LayoutLoader(ShoppingList &); + private: + ShoppingList &sl; + + void track(unsigned); + }; + + typedef map TrackMap; + + TrackMap inventory; + TrackMap layout; + + void load_inventory(const string &); + void load_layout(const string &); +}; + +int main(int argc, char **argv) +{ + ShoppingList sl(argc, argv); + sl.print(cout); + return 0; +} + +ShoppingList::ShoppingList(int argc, char **argv) +{ + string inv_fn="inventory"; + GetOpt getopt; + getopt.add_option('i', "inventory", inv_fn, GetOpt::REQUIRED_ARG); + getopt(argc,argv); + + load_inventory(inv_fn); + load_layout(getopt.get_args().front()); +} + +void ShoppingList::load_inventory(const string &fn) +{ + ifstream in(fn.c_str()); + Parser::Parser parser(in, fn); + InventoryLoader il(*this); + il.load(parser); +} + +void ShoppingList::load_layout(const string &fn) +{ + ifstream in(fn.c_str()); + Parser::Parser parser(in, fn); + LayoutLoader ll(*this); + ll.load(parser); +} + +void ShoppingList::print(ostream &out) +{ + out<<"// Need to get:\n"; + for(TrackMap::iterator i=layout.begin(); i!=layout.end(); ++i) + { + TrackMap::iterator j=inventory.find(i->first); + if(j!=inventory.end()) + { + if(j->secondsecond) + out<<"track "<first<<' '<second-j->second<<";\n"; + } + else + out<<"track "<first<<' '<second<<";\n"; + } + + out<<"// Pre-existing:\n"; + for(TrackMap::iterator i=layout.begin(); i!=layout.end(); ++i) + { + TrackMap::iterator j=inventory.find(i->first); + if(j!=inventory.end()) + out<<"track "<first<<' '<second,j->second)<<";\n"; + } + + out<<"// Unused:\n"; + for(TrackMap::iterator i=inventory.begin(); i!=inventory.end(); ++i) + { + TrackMap::iterator j=layout.find(i->first); + if(j!=layout.end()) + { + if(j->secondsecond) + out<<"track "<first<<' '<second-j->second<<";\n"; + } + else + out<<"track "<first<<' '<second<<";\n"; + } +} + +ShoppingList::InventoryLoader::InventoryLoader(ShoppingList &s): + sl(s) +{ + add("track", &InventoryLoader::track); +} + +void ShoppingList::InventoryLoader::track(unsigned part, unsigned count) +{ + sl.inventory[part]+=count; +} + +ShoppingList::LayoutLoader::LayoutLoader(ShoppingList &s): + sl(s) +{ + add("track", &LayoutLoader::track); + add("base"); +} + +void ShoppingList::LayoutLoader::track(unsigned part) +{ + ++sl.layout[part]; +} diff --git a/tracks.dat b/tracks.dat new file mode 100644 index 0000000..75d36d9 --- /dev/null +++ b/tracks.dat @@ -0,0 +1,425 @@ +// Straight + +track 24064 +{ + description "Straight, 64.3mm (R3-R4-R5)"; + part + { + length 64.3; + }; +}; + +track 24071 +{ + description "Straight, 70.3mm (for slim turnouts)"; + part + { + length 70.8; + }; +}; + +track 24077 +{ + description "Straight, 77.5mm (R1-R2-R3)"; + part + { + length 77.5; + }; +}; + +track 24094 +{ + description "Straight, 94.2mm"; + part + { + length 94.2; + }; +}; + +track 24172 +{ + description "Straight, 171.7mm"; + part + { + length 171.7; + }; +}; + +track 24188 +{ + description "Straight, 188.3mm (turnout length)"; + part + { + length 188.3; + }; +}; + +track 24229 +{ + description "Straight, 229.3mm"; + part + { + length 229.3; + }; +}; + +track 24236 +{ + description "Straight, 236.1mm (slim turnout length)"; + part + { + length 236.1; + }; +}; + +// Curves, R1 + +track 24107 +{ + description "Curve, R1 = 360mm, 7.5°"; + part + { + length 7.5; + radius 360; + }; +}; + +track 24115 +{ + description "Curve, R1 = 360mm, 15°"; + part + { + length 15; + radius 360; + }; +}; + +track 24130 +{ + description "Curve, R1 = 360mm, 30°"; + part + { + length 30; + radius 360; + }; +}; + +// Curves, R2 + +track 24206 +{ + description "Curve, R2 = 437.5mm, 5.7° (turnout to 30°)"; + part + { + length 5.7; + radius 437.5; + }; +}; + +track 24207 +{ + description "Curve, R2 = 437.5mm, 7.5°"; + part + { + length 7.5; + radius 437.5; + }; +}; + +track 24215 +{ + description "Curve, R2 = 437.5mm, 15°"; + part + { + length 15; + radius 437.5; + }; +}; + +track 24224 +{ + description "Curve, R2 = 437.5mm, 24.3° (turnout)"; + part + { + length 24.3; + radius 437.5; + }; +}; + +track 24230 +{ + description "Curve, R2 = 437.5mm, 30°"; + part + { + length 30; + radius 437.5; + }; +}; + +// Curves, R3 + +track 24330 +{ + description "Curve, R3 = 515mm, 30°"; + part + { + length 30; + radius 515; + }; +}; + +// Curves, R4 + +track 24430 +{ + description "Curve, R4 = 579.3mm, 30°"; + part + { + length 30; + radius 579.3; + }; +}; + +// Curves, R5 + +track 24530 +{ + description "Curve, R5 = 643.6mm, 30°"; + part + { + length 30; + radius 643.6; + }; +}; + +// Curves, slim turnout + +track 24912 +{ + description "Curve, R = 1114.6mm, 12.1° (slim turnout)"; + part + { + length 12.1; + radius 1114.6; + }; +}; + +// Curved turnouts, R1 + +track 24671 +{ + description "Curved turnout, left"; + part + { + length 77.5; + route 0; + }; + part + { + start 77.5 0 0; + length 30; + radius 360; + route 0; + }; + part + { + length 30; + radius 360; + route 1; + }; +}; + +track 24672 +{ + description "Curved turnout, right"; + part + { + length 77.5; + route 0; + }; + part + { + start 77.5 0 0; + length 30; + radius -360; + route 0; + }; + part + { + length 30; + radius -360; + route 1; + }; +}; + +// Turnouts, R2 + +track 24611 +{ + description "Turnout, left"; + part + { + length 188.3; + route 0; + }; + part + { + length 24.3; + radius 437.5; + route 1; + }; +}; + +track 24612 +{ + description "Turnout, right"; + part + { + length 188.3; + route 0; + }; + part + { + length 24.3; + radius -437.5; + route 1; + }; +}; + +track 24630 +{ + description "Turnout, 3-way"; + part + { + length 188.3; + route 0; + }; + part + { + length 24.3; + radius 437.5; + route 1; + }; + part + { + length 24.3; + radius -437.5; + route 2; + }; +}; + +track 24624 +{ + description "Turnout, double crossing"; + part + { + length 188.3; + }; + part + { + length 24.3; + radius -437.5; + }; + part + { + start 8.34 38.74 -24.3; + length 188.3; + }; + part + { + start 8.34 38.74 -24.3; + length 24.3; + radius 437.5; + }; +}; + +// Slim turnouts + +track 24711 +{ + description "Slim turnout, left"; + part + { + length 236.1; + route 0; + }; + part + { + length 12.1; + radius 1114.6; + route 1; + }; +}; + +track 24712 +{ + description "Slim turnout, right"; + part + { + length 236.1; + route 0; + }; + part + { + length 12.1; + radius -1114.6; + route 1; + }; +}; + +// Crossings + +track 24640 +{ + description "Crossing, 24.3°"; + part + { + length 188.3; + }; + part + { + start 8.34 38.74 -24.3; + length 188.3; + }; +}; + +track 24649 +{ + description "Crossing, 48.6°"; + part + { + length 103.3; + }; + part + { + start 17.49 38.74 -48.6; + length 103.3; + }; +}; + +track 24740 +{ + description "Slim crossing, 12.1°"; + part + { + length 236.1; + }; + part + { + start 2.62 24.75 -12.1; + length 236.1; + }; +}; + +// Specials + +track 24977 +{ + description "Bumper"; + part + { + length 94.2; + dead_end true; + }; +}; + -- 2.43.0