--- /dev/null
+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 ".";
+ };
+ };
+};
--- /dev/null
+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;
+};
--- /dev/null
+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;
+};
--- /dev/null
+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;
+};
--- /dev/null
+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";
+ };
+};
--- /dev/null
+#include <algorithm>
+#include <fstream>
+#include <msp/gl/rendermode.h>
+#include <msp/gl/select.h>
+#include <msp/gl/texture.h>
+#include <msp/parser/parser.h>
+#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<GL::SelectRecord> 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<unsigned>::max();
+ for(vector<GL::SelectRecord>::iterator i=select_buf.begin(); i!=select_buf.end(); ++i)
+ if(i->min_depth<track_depth)
+ {
+ track=reinterpret_cast<Track3D *>(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
--- /dev/null
+#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
+
--- /dev/null
+#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
--- /dev/null
+#include <cmath>
+#include <GL/gl.h>
+#include <msp/gl/misc.h>
+#include "track.h"
+
+using namespace std;
+using namespace Msp;
+
+#include <iostream>
+
+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<Point>::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<unsigned>(route)<route_seq.size())
+ glDrawElements(GL_QUADS, route_seq[route].size(), GL_UNSIGNED_INT, &route_seq[route][0]);
+ else
+ {
+ for(unsigned i=0; i<route_seq.size(); ++i)
+ glDrawElements(GL_QUADS, route_seq[i].size(), GL_UNSIGNED_INT, &route_seq[i][0]);
+ }
+
+ glPopMatrix();
+}
+
+void Track3D::prepare_render()
+{
+ const Point &pos=track.get_position();
+ float rot=track.get_rotation();
+
+ glPushMatrix();
+ glTranslatef(pos.x, pos.y, pos.z);
+ glRotatef(rot*180/M_PI, 0, 0, 1);
+}
+
+void Track3D::build_object()
+{
+ varray.clear();
+ RefPtr<GL::VertexArrayBuilder> 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<Point> 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<<quality))+1;
+ Point center(x-sin(dir)*radius, y+cos(dir)*radius, 0);
+ float r=fabs(radius);
+ float start=((radius<0)?M_PI:0)+dir;
+ float angle=(radius<0)?-length:length;
+ int inv=(radius<0)?-1:1;
+ for(unsigned i=0; i<=nsegs; ++i)
+ {
+ float a=start+i*angle/nsegs;
+ float c=cos(a);
+ float s=sin(a);
+
+ for(unsigned j=0; j<profile.size(); ++j)
+ {
+ unsigned k=j&~1;
+ float dy=profile[k+1].y-profile[k].y;
+ float dz=profile[k+1].z-profile[k].z;
+ float d=sqrt(dy*dy+dz*dz);
+ va_builder.normal(s*dz/d, -c*dz/d, dy/d);
+
+ Point p(center.x+s*(r-profile[j].y*inv), center.y-c*(r-profile[j].y*inv), profile[j].z+i*track.get_slope()/nsegs);
+ va_builder.vertex(p.x, p.y, p.z);
+ if(profile[j].z==0)
+ border.push_back(p);
+ }
+ }
+ }
+ else
+ {
+ nsegs=1;
+ float c=cos(dir);
+ float s=sin(dir);
+ for(unsigned i=0; i<2; ++i)
+ {
+ for(unsigned j=0; j<profile.size(); ++j)
+ {
+ unsigned k=j&~1;
+ float dy=profile[k+1].y-profile[k].y;
+ float dz=profile[k+1].z-profile[k].z;
+ float d=sqrt(dy*dy+dz*dz);
+ va_builder.normal(s*dz/d, -c*dz/d, dy/d);
+
+ float len=(part.dead_end && i==1 && j>=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<nsegs; ++i)
+ {
+ for(unsigned j=0; j<3; ++j)
+ {
+ base_seq.push_back(base_index+i*psize+j*2);
+ base_seq.push_back(base_index+(i+1)*psize+j*2);
+ base_seq.push_back(base_index+(i+1)*psize+1+j*2);
+ base_seq.push_back(base_index+i*psize+1+j*2);
+ }
+ for(unsigned j=3; j<9; ++j)
+ {
+ rail_seq.push_back(base_index+i*psize+j*2);
+ rail_seq.push_back(base_index+(i+1)*psize+j*2);
+ rail_seq.push_back(base_index+(i+1)*psize+1+j*2);
+ rail_seq.push_back(base_index+i*psize+1+j*2);
+ }
+ route_seq[part.route].push_back(base_index+i*psize+18);
+ route_seq[part.route].push_back(base_index+(i+1)*psize+18);
+ route_seq[part.route].push_back(base_index+(i+1)*psize+19);
+ route_seq[part.route].push_back(base_index+i*psize+19);
+ }
+
+ base_index+=(nsegs+1)*psize;
+}
+
+} // namespace Marklin
--- /dev/null
+#ifndef MARKLIN3D_TRACK_H_
+#define MARKLIN3D_TRACK_H_
+
+#include <list>
+#include <msp/gl/vertexarray.h>
+#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<Point> border;
+ Msp::GL::VertexArray varray;
+ std::vector<unsigned> base_seq;
+ std::vector<unsigned> rail_seq;
+ std::vector<std::vector<unsigned> > route_seq;
+ unsigned quality;
+
+ void prepare_render();
+ void build_object();
+ void build_part(const Track::Part &, Msp::GL::VertexArrayBuilder &, unsigned &);
+};
+typedef std::list<Track3D *> Track3DSeq;
+
+} // namespace Marklin
+
+#endif
--- /dev/null
+#include <signal.h>
+#include <cmath>
+#include <iostream>
+#include <GL/gl.h>
+#include <SDL.h>
+#include <msp/gl/rendermode.h>
+#include <msp/gl/select.h>
+#include <msp/strings/codec.h>
+#include <msp/strings/lexicalcast.h>
+#include <msp/strings/utf8.h>
+#include <msp/strings/utils.h>
+#include <msp/time/units.h>
+#include <msp/time/utils.h>
+#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<<catalogue.get_tracks().size()<<'\n';
+
+ cat_layout=new Layout(catalogue);
+ cat_layout_3d=new Layout3D(*cat_layout);
+
+ const Catalogue::TrackMap &ctracks=catalogue.get_tracks();
+ unsigned n=0;
+ for(Catalogue::TrackMap::const_iterator i=ctracks.begin(); i!=ctracks.end(); ++i, ++n)
+ {
+ Track *track=i->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<<track.get_article_number()<<' '<<track.get_description();
+ if(mode!=CATALOGUE)
+ ss<<" (slope "<<track.get_slope()/track.get_length()*100<<"%)";
+ if(track.get_turnout_id())
+ ss<<" (turnout "<<track.get_turnout_id()<<')';
+ else if(track.get_sensor_id())
+ ss<<" (sensor "<<track.get_sensor_id()<<')';
+ tooltip=decode<Utf8>(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: "<<len<<"m\n";
+ }
+ else if(key==SDLK_l)
+ selection->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<<track->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<<track->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<Utf8>(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 "<<pard<<"mm - Perp "<<perpd<<"mm - Total "<<d<<"mm - Angle "<<adiff<<"°";
+ tooltip=decode<Utf8>(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<unsigned>(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<unsigned>(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> Designer::reg;
--- /dev/null
+#ifndef DESIGNER_H_
+#define DESIGNER_H_
+
+#include <string>
+#include <msp/core/application.h>
+#include <msp/gl/font.h>
+#include <msp/time/timestamp.h>
+#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<Designer> reg;
+};
+
+#endif
--- /dev/null
+#include <SDL_keysym.h>
+#include <GL/gl.h>
+#include <msp/gl/texture.h>
+#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(pos<text.size())
+ text.erase(pos, 1);
+ }
+ else if(key==SDLK_LEFT)
+ {
+ if(pos>0)
+ --pos;
+ }
+ else if(key==SDLK_RIGHT)
+ {
+ if(pos<text.size())
+ ++pos;
+ }
+ else if(ch>=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();
+}
--- /dev/null
+#ifndef INPUT_H_
+#define INPUT_H_
+
+#include <string>
+#include <sigc++/sigc++.h>
+
+class Designer;
+
+class Input
+{
+public:
+ sigc::signal<void> signal_accept;
+ sigc::signal<void> 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
--- /dev/null
+#include <algorithm>
+#include <cmath>
+#include <GL/gl.h>
+#include "3d/layout.h"
+#include "designer.h"
+#include "manipulator.h"
+#include "selection.h"
+
+using namespace std;
+using namespace Marklin;
+using namespace Msp;
+
+#include <iostream>
+
+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: "<<dz*1000<<"mm ("<<(center.z+dz)*1000<<"mm)";
+ signal_status.emit(ss.str());
+
+ wrap_pos.z=center.z+dz;
+ 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+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<TrackWrap>::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; a<M_PI; a+=0.01)
+ {
+ Point minp,maxp;
+ t3d->get_bounds(a, minp, maxp);
+ float area=(maxp.x-minp.x)*(maxp.y-minp.y);
+ if(area<min_area)
+ {
+ float c=cos(a);
+ float s=sin(a);
+ float x=(minp.x+maxp.x)/2;
+ float y=(minp.y+maxp.y)/2;
+ tw.pos=Point(c*x-s*y, s*x+c*y, (minp.z+maxp.z)/2);
+ tw.rot=a;
+ tw.width=maxp.x-minp.x+0.01;
+ tw.height=maxp.y-minp.y+0.01;
+
+ min_area=area;
+ }
+ }
+
+ if(i==tracks.begin())
+ {
+ min_x=max_x=tw.pos.x;
+ min_y=max_y=tw.pos.y;
+ }
+ else
+ {
+ min_x=min(min_x, tw.pos.x);
+ max_x=max(max_x, tw.pos.x);
+ min_y=min(min_y, tw.pos.y);
+ max_y=max(max_y, tw.pos.y);
+ }
+ wrap.push_back(tw);
+ }
+
+ center=Point((min_x+max_x)/2, (min_y+max_y)/2, 0);
+ wrap_pos=center;
+ wrap_rot=0;
+ for(MTrackSeq::iterator i=tracks.begin(); i!=tracks.end(); ++i)
+ {
+ const Point &tp=i->track->get_position();
+ i->pos=Point(tp.x-center.x, tp.y-center.y, tp.z);
+ }
+ for(list<TrackWrap>::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())
+{ }
--- /dev/null
+#ifndef MANIPULATOR_H_
+#define MANIPULATOR_H_
+
+#include <sigc++/sigc++.h>
+#include "3d/track.h"
+
+class Designer;
+class Selection;
+
+class Manipulator
+{
+public:
+ sigc::signal<void, const std::string &> signal_status;
+ sigc::signal<void, bool> 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<MTrack> MTrackSeq;
+
+ struct TrackOrder
+ {
+ Marklin::Track *track;
+ bool rev;
+
+ TrackOrder(Marklin::Track *t, bool r): track(t), rev(r) { }
+ };
+ typedef std::list<TrackOrder> TrackOrderSeq;
+
+ struct TrackWrap
+ {
+ Marklin::Point pos;
+ float rot;
+ float width;
+ float height;
+ };
+
+ Designer &designer;
+ Selection *selection;
+ MTrackSeq tracks;
+ Marklin::Point center;
+
+ std::list<TrackWrap> 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
--- /dev/null
+#include <cmath>
+#include <GL/gl.h>
+#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();
+}
--- /dev/null
+#ifndef MEASURE_H_
+#define MEASURE_H_
+
+#include <sigc++/sigc++.h>
+
+class Designer;
+
+class Measure
+{
+public:
+ sigc::signal<void> signal_done;
+ sigc::signal<void> 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
--- /dev/null
+#include <algorithm>
+#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();
+}
--- /dev/null
+#ifndef SELECTION_H_
+#define SELECTION_H_
+
+#include <set>
+#include <sigc++/sigc++.h>
+#include "3d/track.h"
+
+class Selection
+{
+public:
+ typedef std::set<Marklin::Track *> TrackSet;
+
+ sigc::signal<void> 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
--- /dev/null
+#include <cmath>
+#include <limits>
+#include <SDL.h>
+#include <GL/gl.h>
+#include <msp/core/error.h>
+#include <msp/core/getopt.h>
+#include <msp/strings/lexicalcast.h>
+#include <msp/strings/regex.h>
+#include "engineer.h"
+#include "mainpanel.h"
+#include "trainpanel.h"
+
+using namespace std;
+using namespace Marklin;
+using namespace Msp;
+
+#include <iostream>
+
+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<unsigned>(m[1].str);
+ screen_h=lexical_cast<unsigned>(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<string> &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 "<<track->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; angle<M_PI; angle+=0.01)
+ {
+ float min_x=0;
+ float max_x=0;
+ float min_y=0;
+ float max_y=0;
+ for(Track3DSeq::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
+ {
+ Point minp,maxp;
+ (*i)->get_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(height<best_height || best_height<0)
+ {
+ cam_rot=angle;
+ best_height=height;
+ mid_x=(min_x+max_x)/2;
+ mid_y=(min_y+max_y)/2;
+ }
+ }
+
+ float c=cos(cam_rot);
+ float s=sin(cam_rot);
+ cam_pos.x=c*mid_x-s*mid_y;
+ cam_pos.y=s*mid_x+c*mid_y;
+ cam_pos.z=max(best_height*1.05/0.82843, 0.15);
+}
+
+void Engineer::turnout_route_changed(unsigned)
+{
+}
+
+void Engineer::sensor_state_changed(bool state, unsigned addr)
+{
+ 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()==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<unsigned>::max();
+ for(unsigned j=0; j<n_records; ++j)
+ {
+ unsigned ns_size=select_buf[i++];
+ unsigned min_depth=select_buf[i++];
+ ++i; // Skip max_depth
+ if(min_depth<track_depth)
+ {
+ track=(Track *)select_buf[i+ns_size-1];
+ track_depth=min_depth;
+ }
+ i+=ns_size;
+ }
+
+ return track;
+ }
+
+ return 0;*/
+}
+
+Application::RegApp<Engineer> Engineer::reg;
--- /dev/null
+#ifndef ENGINEER_H_
+#define ENGINEER_H_
+
+#include <msp/core/application.h>
+#include <msp/gl/font.h>
+#include <msp/gltk/resources.h>
+#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<TrainPanel *> 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<Engineer> reg;
+};
+
+#endif
--- /dev/null
+#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<Part> PartSeq;
+
+private:
+};
+
+#endif
--- /dev/null
+#ifndef GUIRESOURCES_H_
+#define GUIRESOURCES_H_
+
+#endif
--- /dev/null
+#include <msp/gltk/button.h>
+#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();
+}
--- /dev/null
+#ifndef MAINPANEL_H_
+#define MAINPANEL_H_
+
+#include <msp/gltk/indicator.h>
+#include <msp/gltk/panel.h>
+
+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
--- /dev/null
+#include <msp/gltk/button.h>
+#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<unsigned>(v));
+}
--- /dev/null
+#ifndef TRAINPANEL_H_
+#define TRAINPANEL_H_
+
+#include <msp/gltk/hslider.h>
+#include <msp/gltk/label.h>
+#include <msp/gltk/panel.h>
+#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
--- /dev/null
+#include <fstream>
+#include <msp/parser/parser.h>
+#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
--- /dev/null
+#ifndef LIBMARKLIN_CATALOGUE_H_
+#define LIBMARKLIN_CATALOGUE_H_
+
+#include <map>
+#include <msp/parser/loader.h>
+
+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<unsigned, Track *> TrackMap;
+
+ Track *get_track(unsigned);
+ const TrackMap &get_tracks() const { return tracks; }
+ void load(const std::string &);
+ ~Catalogue();
+private:
+ TrackMap tracks;
+};
+
+} // namespace Marklin
+
+#endif
--- /dev/null
+#ifndef COMMAND_H_
+#define COMMAND_H_
+
+#include <string>
+#include <sigc++/sigc++.h>
+#include "constants.h"
+
+namespace Marklin {
+
+class Command
+{
+public:
+ sigc::signal<void, Error, const std::string &> 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
--- /dev/null
+#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
--- /dev/null
+#include <fcntl.h>
+#include <termios.h>
+#include <sys/poll.h>
+#include <iostream>
+#include <msp/core/error.h>
+#include <msp/time/units.h>
+#include <msp/time/utils.h>
+#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 "<<baud[i]<<" bits/s, P50 is ";
+ char buf[2];
+ if(read(serial_fd, buf, 2)==2)
+ {
+ p50_enabled=true;
+ cout<<"enabled";
+ }
+ else
+ {
+ p50_enabled=false;
+ cout<<"disabled";
+ }
+ cout<<'\n';
+ ok=true;
+ break;
+ }
+ }
+ if(!ok)
+ throw Exception("IB not detected");
+}
+
+Command &Control::command(const string &cmd)
+{
+ queue.push_back(Command(cmd));
+ return queue.back();
+}
+
+void Control::add_turnout(Turnout *t)
+{
+ if(turnouts.count(t->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<resp.size(); ++i)
+ printf("%02X ", (unsigned char)resp[i]);
+ printf("(%d bytes)\n", resp.size());
+ }
+
+ queue.front().signal_done.emit((Error)resp[0], resp.substr(1));
+ queue.erase(queue.begin());
+ }
+ else
+ return;
+ }
+
+ if(queue.size())
+ {
+ string cmd=queue.front().get_command();
+
+ if(p50_enabled)
+ {
+ if(cmd[0]&0x80)
+ cmd="X"+cmd;
+ else
+ cmd="x"+cmd;
+ }
+
+ if(debug)
+ {
+ printf("write: ");
+ for(unsigned i=0; i<cmd.size(); ++i)
+ printf("%02X ",(unsigned char)cmd[i]);
+ printf("(%d bytes)\n",cmd.size());
+ }
+
+ write(serial_fd, cmd.data(), cmd.size());
+ queue.front().set_sent(true);
+ }
+}
+
+Control::~Control()
+{
+ close(serial_fd);
+}
+
+/*** private ***/
+
+void Control::read_all(int fd, char *buf, int size)
+{
+ int pos=0;
+ while(pos<size)
+ {
+ int len=read(fd, buf+pos, size-pos);
+ pos+=len;
+ }
+}
+
+string Control::read_reply(Cmd cmd)
+{
+ string result;
+ if(cmd==CMD_EVENT)
+ {
+ result+=ERR_NO_ERROR;
+ for(unsigned i=0; i<3; ++i)
+ {
+ char c;
+ read(serial_fd, &c, 1);
+ result+=c;
+ if(!(c&0x80)) break;
+ }
+ }
+ else if(cmd==CMD_EVENT_LOK)
+ {
+ result+=ERR_NO_ERROR;
+ char c[5];
+ read(serial_fd, c+4, 1);
+ result+=c[4];
+ while(c[4]&0x80)
+ {
+ read_all(serial_fd, c, 5);
+ result.append(c, 5);
+ }
+ }
+ else if(cmd==CMD_EVENT_TURNOUT)
+ {
+ result+=ERR_NO_ERROR;
+ char c[511];
+ read(serial_fd, c, 1);
+ read_all(serial_fd, c+1, c[0]*2);
+ result.append(c, c[0]*2+1);
+ }
+ else if(cmd==CMD_EVENT_SENSOR)
+ {
+ result+=ERR_NO_ERROR;
+ char c[3];
+ read(serial_fd, c+2, 1);
+ result+=c[2];
+ while(c[2])
+ {
+ read_all(serial_fd, c, 3);
+ result.append(c, 3);
+ }
+ }
+ else
+ {
+ unsigned expected_bytes=1;
+ if(cmd==CMD_FUNC_STATUS || cmd==CMD_TURNOUT_STATUS)
+ expected_bytes=2;
+ if(cmd==CMD_SENSOR_STATUS || cmd==CMD_TURNOUT_GROUP_STATUS)
+ expected_bytes=3;
+ if(cmd==CMD_LOK_STATUS)
+ expected_bytes=4;
+ if(cmd==CMD_LOK_CONFIG)
+ expected_bytes=5;
+ char c[5];
+ read_all(serial_fd, c, 1);
+ result+=c[0];
+ if(!c[0])
+ {
+ read_all(serial_fd, c+1, expected_bytes-1);
+ result.append(c+1, expected_bytes-1);
+ }
+ }
+
+ return result;
+}
+
+void Control::event_query_done(Error, const string &resp)
+{
+ if(resp[0]&0x20)
+ command(string(1, CMD_EVENT_TURNOUT)).signal_done.connect(sigc::mem_fun(this, &Control::turnout_event_done));
+ if(resp[0]&0x04)
+ command(string(1, CMD_EVENT_SENSOR)).signal_done.connect(sigc::mem_fun(this, &Control::sensor_event_done));
+}
+
+void Control::turnout_event_done(Error, const string &resp)
+{
+ unsigned count=resp[0];
+ for(unsigned i=0; i<count; ++i)
+ {
+ unsigned addr=(unsigned char)resp[i*2+1]+((resp[i*2+2]&7)<<8);
+ bool status=!(resp[i*2+2]&0x80);
+ cout<<"Turnout "<<addr<<", status "<<status<<'\n';
+ signal_turnout_event.emit(addr, status);
+ }
+}
+
+void Control::sensor_event_done(Error, const string &resp)
+{
+ for(unsigned i=0; resp[i]; i+=3)
+ {
+ unsigned module=(unsigned char)resp[i];
+ cout<<"S88 module "<<module<<", status ";
+ for(unsigned j=0; j<16; ++j)
+ {
+ bool status=(resp[i+1+j/8]>>(7-j%8))&1;
+ cout<<status;
+ signal_sensor_event.emit(module*16+j-15, status);
+ }
+ cout<<'\n';
+ }
+}
+
+} // namespace Marklin
--- /dev/null
+#ifndef LIBMARKLIN_CONTROL_H_
+#define LIBMARKLIN_CONTROL_H_
+
+#include <list>
+#include <string>
+#include <msp/time/timestamp.h>
+#include "constants.h"
+#include "sensor.h"
+#include "locomotive.h"
+#include "turnout.h"
+
+namespace Marklin {
+
+class Command;
+
+class Control
+{
+public:
+ sigc::signal<void, unsigned, bool> signal_turnout_event;
+ sigc::signal<void, unsigned, bool> 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<Command> 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
--- /dev/null
+#ifndef LIBMARKLIN_GEOMETRY_H_
+#define LIBMARKLIN_GEOMETRY_H_
+
+#include <vector>
+
+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
--- /dev/null
+#include <algorithm>
+#include <fstream>
+#include <msp/parser/parser.h>
+#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<Track *>(), 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 \""<<base<<"\";\n";
+ for(TrackSeq::iterator i=tracks.begin(); i!=tracks.end(); ++i)
+ {
+ out<<"track "<<(*i)->get_article_number()<<"\n{\n";
+ const Point &p=(*i)->get_position();
+ out<<"\tposition "<<p.x<<' '<<p.y<<' '<<p.z<<";\n";
+ out<<"\trotation "<<(*i)->get_rotation()<<";\n";
+ out<<"\tslope "<<(*i)->get_slope()<<";\n";
+
+ unsigned id=(*i)->get_turnout_id();
+ if(id)
+ out<<"\tturnout_id "<<id<<";\n";
+
+ id=(*i)->get_sensor_id();
+ if(id)
+ out<<"\tsensor_id "<<id<<";\n";
+
+ if((*i)->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
--- /dev/null
+#ifndef LIBMARKLIN_LAYOUT_H_
+#define LIBMARKLIN_LAYOUT_H_
+
+#include <sigc++/sigc++.h>
+#include <msp/parser/loader.h>
+#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<void, Track *> signal_track_added;
+ sigc::signal<void, Track *> 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
+
--- /dev/null
+#include <msp/time/timer.h>
+#include <msp/time/units.h>
+#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<<func;
+ else
+ funcs&=~(1<<func);
+
+ send_command(true);
+}
+
+void Locomotive::refresh_status()
+{
+ char cmd[3];
+ cmd[0]=CMD_LOK_STATUS;
+ cmd[1]=addr&0xFF;
+ cmd[2]=(addr>>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<<i);
+ }
+ control.command(string(cmd,5));
+}
+
+void Locomotive::status_reply(Error err, const string &reply)
+{
+ if(err==ERR_NO_ERROR)
+ {
+ if((unsigned char)reply[0]<=1)
+ speed=0;
+ else
+ speed=(unsigned char)reply[0]*2/19+1;
+ reverse=(reply[1]&0x20)?false:true;
+ funcs=(reply[1]&0xF)<<1;
+ if(reply[1]&0x10)
+ funcs|=1;
+ }
+}
+
+bool Locomotive::reverse_timeout()
+{
+ reverse=!reverse;
+ send_command(true);
+ return false;
+}
+
+} // namespace Marklin
--- /dev/null
+#ifndef LIBMARKLIN_LOCOMOTIVE_H_
+#define LIBMARKLIN_LOCOMOTIVE_H_
+
+#include <list>
+#include <string>
+#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<Locomotive *> LocomotiveSeq;
+
+} // namespace Marklin
+
+#endif
--- /dev/null
+#include "route.h"
+
+namespace Marklin {
+
+
+
+} // namespace Marklin
--- /dev/null
+#ifndef LIBMARKLIN_ROUTE_H_
+#define LIBMARKLIN_ROUTE_H_
+
+#include <list>
+#include <string>
+#include "track.h"
+
+namespace Marklin {
+
+class Route
+{
+public:
+ typedef std::map<unsigned, unsigned> 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<Route *> RouteSeq;
+
+} // namespace Marklin
+
+#endif
--- /dev/null
+#include "control.h"
+#include "section.h"
+#include "trafficmanager.h"
+#include "turnout.h"
+
+using namespace Msp;
+
+#include <iostream>
+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<<n;
+ i->routes|=route;
+ set<Track *> 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 "<<id;
+ if((*tracks.begin())->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<<i->link->id;
+ else
+ cout<<"none";
+ cout<<", routes="<<i->routes<<'\n';
+ }
+}
+
+void Section::find_routes(Track *track, const Track::Endpoint *track_ep, unsigned route, std::set<Marklin::Track *> &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
--- /dev/null
+#ifndef MARKLIN_3D_SECTION_H_
+#define MARKLIN_3D_SECTION_H_
+
+#include <list>
+#include <set>
+#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<Endpoint> 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<Section *> SectionSeq;
+
+} // namespace Marklin
+
+#endif
--- /dev/null
+#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
--- /dev/null
+#ifndef LIBMARKLIN_SENSOR_H_
+#define LIBMARKLIN_SENSOR_H_
+
+#include <list>
+#include <map>
+#include <sigc++/sigc++.h>
+
+namespace Marklin {
+
+class Control;
+
+class Sensor
+{
+public:
+ sigc::signal<void, bool> 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<Sensor *> SensorSeq;
+typedef std::map<unsigned, Sensor *> SensorMap;
+
+} // namespace Marklin
+
+#endif
--- /dev/null
+#include <cmath>
+#include "track.h"
+
+using namespace std;
+using namespace Msp;
+
+#include <iostream>
+
+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*dy<limit)
+ {
+ set_rotation(other.rot+j->rot-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<<route))
+ {
+ // Find the other endpoint for this route
+ for(EndpointSeq::const_iterator i=endpoints.begin(); i!=endpoints.end(); ++i)
+ if((i->routes&(1<<route)) && &*i!=ep)
+ return &*i;
+ }
+ else
+ {
+ // Find an endpoint that's connected to this one and has the requested route
+ for(EndpointSeq::const_iterator i=endpoints.begin(); i!=endpoints.end(); ++i)
+ if((i->routes&(1<<route)) && (i->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<<route));
+ if(dead_end)
+ return;
+ else if(radius)
+ {
+ float a=(radius<0)?-length:length;
+ float c=cos(a);
+ float s=sin(a);
+ float rx=radius*sin(dir);
+ float ry=-radius*cos(dir);
+ epl.push_back(Endpoint(Point(x+c*rx-s*ry-rx, y+c*ry+s*rx-ry, 0), dir+a, 1<<route));
+ }
+ else
+ epl.push_back(Endpoint(Point(x+cos(dir)*length, y+sin(dir)*length, 0), dir, 1<<route));
+}
+
+Track::Part::Loader::Loader(Part &p):
+ part(p)
+{
+ add("start", &Loader::start);
+ add("length", &Part::length);
+ add("radius", &Part::radius);
+ add("route", &Part::route);
+ add("dead_end", &Part::dead_end);
+}
+
+Track::Part::Loader::~Loader()
+{
+ if(part.radius)
+ {
+ part.length*=M_PI/180;
+ part.radius/=1000;
+ }
+ else
+ part.length/=1000;
+
+ part.x/=1000;
+ part.y/=1000;
+ part.dir*=M_PI/180;
+}
+
+void Track::Part::Loader::start(float x, float y, float d)
+{
+ part.x=x;
+ part.y=y;
+ part.dir=d;
+}
+
+} // namespace Marklin
--- /dev/null
+#ifndef LIBMARKLIN_TRACK_H_
+#define LIBMARKLIN_TRACK_H_
+
+#include <list>
+#include <set>
+#include <msp/parser/loader.h>
+#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<Endpoint> 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<Part> 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<Track *> TrackSeq;
+typedef std::set<Track *> TrackSet;
+
+} // namespace Marklin
+
+#endif
--- /dev/null
+#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<Track *>(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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#ifndef LIBMARKLIN_TRAIN_H_
+#define LIBMARKLIN_TRAIN_H_
+
+#include <sigc++/trackable.h>
+#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<SectionRef> 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<Train *> TrainSeq;
+
+} // namespace Marklin
+
+#endif
--- /dev/null
+#include <iostream>
+#include <msp/time/timer.h>
+#include <msp/time/units.h>
+#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
--- /dev/null
+#ifndef LIBMARKLIN_TURNOUT_H_
+#define LIBMARKLIN_TURNOUT_H_
+
+#include <list>
+#include <map>
+#include <string>
+#include <sigc++/sigc++.h>
+#include "constants.h"
+
+namespace Marklin {
+
+class Control;
+
+class Turnout
+{
+public:
+ sigc::signal<void, unsigned> 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<Turnout *> TurnoutSeq;
+typedef std::map<unsigned, Turnout *> TurnoutMap;
+
+} // namespace Marklin
+
+#endif
--- /dev/null
+#include <fstream>
+#include <iostream>
+#include <map>
+#include <msp/core/getopt.h>
+#include <msp/parser/loader.h>
+#include <msp/parser/parser.h>
+
+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<unsigned, unsigned> 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->second<i->second)
+ out<<"track "<<i->first<<' '<<i->second-j->second<<";\n";
+ }
+ else
+ out<<"track "<<i->first<<' '<<i->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 "<<i->first<<' '<<min(i->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->second<i->second)
+ out<<"track "<<i->first<<' '<<i->second-j->second<<";\n";
+ }
+ else
+ out<<"track "<<i->first<<' '<<i->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];
+}
--- /dev/null
+// 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;
+ };
+};
+