Building a TrakSim Map


This document describes how to use the BuildMap function to create your own track maps for TrakSim. You need to create a track descriptor text file describing the properties of the "park" that the simulated car will drive around, and if you have any superstructure, any objects that rise above the ground level (other than interior walls), you need to create an image file containing RGB images of each type of artifact you want TrakSim to display, in each perspective you want it to be seen from, and make the pixel array from that file available to the BuildMap function. One of the TrakSim APIs is a ReadTiff32Image method to read in an uncompressed RGB Tiff32 file, which you can use for that purpose if your image file is compatible. Part of the track descriptor text file is used to define the image locations and sizes and placement on the track map, which is described in the section on Artifact Specification. The BuildMap function returns an array, which if you write it to a file named "TrackImg.indx" then TrakSim will read it as its default track.

The track descriptor text file begins with a single line specifying five Global Park Properties. This is followed by any number of lines describing individual Track Details, including the location and orientation of artifacts. The details section ends with a period in the first position of its line, optionally followed by image specifications for artifacts as described in the section on Artifact Specification.

Included in this distribution is a track descriptor file "TrackImg.txt" containing several example track descriptors. Most of the examples in this document come from the first descriptor in that file, named "Figure-8". The second descriptor "PSU TestOval" is a modest first cut at what the participants in the third annual NWAPW might need for testing their work. Delete the first descriptor (or move it elsewhere in the file) in order to use the second.

Global Park Properties

The first line of the track descriptor file must begin with five numbers, which may be either decimal or (Java) hexadecimal. Here is the first line from the "Figure-8" track descriptor:
0x700070 0x96931963 0x38002C 90 13 "Figure-8" -- 2018 April 25

The first number 0x700070 defines the logical (full-size) "park" dimensions in meters (yards), in this case 112x112. If you are running 1:8 scale model cars, then the actual floor size would be 14x14 (meters) or 45 feet square. The upper (first) 16 bits is the north-south dimension, and the lower 16 bits is the east-west dimension.

The second number 0x96931963 defines the track and floor or ground coloration. Like the park dimensions, it is divided into two 16-bit parts, actually 15-bit parts, with the two upper bits in each half having other significance. The low half of the number specifies an RGB color for the track, and the upper half specifies the color of non-track (but in-park) ground. In our example, the track is brown (R=9, G=6, B=3) and the non-track is green (R=6, G=9, B=3). The next three bits determine the degree of darkening for the checkerboard odd squares (1: -3). A pure black&while checkerboard would be represented by 0x5FFF5FFF. Of course the darkening factor has no effect if checkerboard is disabled.

The sign bit on the second number signifies that this is an indoor track, and the background is a slightly darker wall (see Walls below). If positive, the backdrop is a flat green below the horizon and sky blue above. The middle bit is reserved for future use and should be set to 0.

The third number 0x38002C defines the position of the car in park meters at startup, in this example 56 meters south of the top edge of the map and 44 meters east of the left edge.

The fourth number is the direction the car is initially pointing at startup, in integer degrees clockwise from north, in this example facing due east.

The fifth and final number defines the width of the white line on the track edges in integer park centimeters, in this case 13cm = 5" -- which in a 1:8 scale track would be about 3/4" wide tape on the floor. If you enter a zero here (or leave it out) then the default specified in the WhiteLnWi constant is used.

The remainder of the first line is ignored and can be used for comments or a title or whatever. In this example it's a descriptor name and revision date.

Track Details

The main body of the track descriptor file consists of a sequence of track operators, tagged lines where each significant line begins with a capital letter identifying what operation this line specifies. Lines that begin in any other way are ignored and can be used for comments. The end of the track details section is signified by a period in the first position of its line. These tags are defined in this release:
@ -- Time & Position for an animated artifact
A -- Advance track edge some number of grid (2m) units
B -- Bucket Fill
E -- Advance track edge Eastward (usually generated by macro)
F -- Rectangular area Fill
H -- Horizontal (eastward) wall
I -- Initialize as no prior
J -- Macro to build moving artifact sequence
K -- (internal use only)
L -- Macro to turn track edge advance Left
N -- Advance track edge Northward (usually generated by macro)
O -- Object specification (usually generated by macro)
P -- Pilaster (vertical Post, otherwise like a wall)
Q -- Convert track to non-track
R -- Macro to turn track edge advance Right
S -- Advance track edge Southward (usually generated by macro)
T -- Start Timing sequence
U -- Painted Image
V -- Vertical (southward) wall
W -- Advance track edge Westward (usually generated by macro)
X -- Erase overstrike prevention history
Y -- Macro to build static artifact
Z -- (internal use only)
. -- End of track operators

All of the coordinate numbers in this section specify a position on the park map, which is partitioned off in 2-meter units, a maximum of 256 meters (128 grid units) wide (west to east) and 200 meters (100 grid units) high (north to south). When a position is given, the first number after the tag is the vertical (southward from the north edge) coordinate and the next number is the horizontal coordinate (eastward from the west edge). Sometimes (as noted) fractional positions can be used, and resolve to 25cm (eighths of a grid unit).

The track is defined by its right edge, that is, you should outline the track by tracing its edge counter-clockwise all the way around. In the most common case of (circular or oval) tracks that loop back on themselves with islands of non-track surrounded by one or more track loops, you need to define the outside edge counter-clockwise around the outside, and then re-initialize the chaining and traverse each interior island clockwise, so that your advance always leaves track on the left, non-track on the right.

If you use one of the compass-named advance operators (N,S,E,W), they always depend on a previous advance to have ended somewhere, then specify where the next segment ends. The first time you use one of these, there is no previous end, so the given coordinate does not advance anywhere, but it does define a new starting point and direction. Using the 'A' Advance operator continues in whichever of the four compass directions you left off for that many grid units. Using a Left or Right turn operator turns the track 90 degrees in the specified direction and sets a new current compass direction.

The Advance and turn macro operators always assume compass directions, but you are not restricted to those four directions. If you use one of the compass directions, you specify an ending point, which needs to be more or less in the specified direction, but may angle off as much as 45 degrees to the left or right. This is in fact how the turn macro operators make curves: they lay out a sequence of short straight segments angling more and more in the desired direction. You can make your own diagonals and partial curves to connect with the rest of the track constructed with macros, or you can ignore the macros entirely and use the compass operators directly, although it is significantly more work to get the track to look reasonable.

Besides the track operators, there are separate operators for specifying areas and walls, and for specifying artifacts that rise above the floor of the track, which are grouped for discussion separately. These operators are now discussed in this somewhat logical order.

Area & Wall Operators

F -- Rectangular area Fill -- F nn ww ss ee cc
Four rectangular coordinates are specified, respectively the North, West, South, and East edges of the rectangle to be filled with either non-track (0) or track (1). Normally you designate the park area with a rectangular fill. You can also use the Fill operator to fill in small holes within the track.

B -- Bucket Fill -- B vv hh cc

You can fill an arbitrary region using the tool that works like the paint bucket in paint programs. Specify the map grid coordinates where the drop starts, and it will fill all contiguous (left/right/up/down, but not diagonal) map cells touching it. You can fill the contiguous cells with track (1) or non-track (0). Only previously unfilled cells and cells containing the opposite color will be filled; cells already filled with the same color, as well as walls and track edges stop the flow of the fill. If you want to build a non-rectangular park area, you can use the track edge operators to surround the irregular boundaries, Bucket fill the enclosed region with track, then use the Convert to non-track (Q) operator to make that area into park.
H -- Horizontal (eastward) wall -- H vv hh ee cc
V -- Vertical (southward) wall -- V vv hh ee cc
P -- Pilaster (vertical Post) -- P vv hh
After laying out your (non-track) floor area, you can define single-grid (that would be two meters thick at full scale, or for example, 25cm=10" thick at 1:8 scale) walls. If the Indoor Map (sign bit) flag is set in the floor coloration word of the first line, and if you did not explicitly set out exterior walls using wall operators, then one of those colors. Four colors are defined in the class DriverCons (see "Scenery Colors") for wall-like objects. Horizontal and vertical walls can be either CreamWall (cc=1) or DarkWall (cc=2); CreamWall walls are additionally tinted gradually darker as they recede from the viewer's perspective, to give them some depth. You might consider leaving the DarkWall color darker to use it for displaying doors in the CreamWall walls. Posts (pilasters) are defined in their own color, PilasterCo which is initially set to a medium gray.

Due to roundoff error, sometimes distant walls are missed by the drawing engine. The extra searching to make sure they are not missed would take more processing time than was deemed reasonable. You can always add wall artifacts which would not suffer that problem. Diagonal and curved walls can be accommodated the same way.

U -- Painted Image -- U vv hh oo hh^vv tt ww -- or -- U vv hh oo ff tt ww

Artifacts have a vertical dimension and a user-specified appearance, and (except for traffic lights), if your car tries to occupy the same location, it will crash. There is also a need for lines and other stuff painted on the pavement, which are harmless to drive over yet which the autonomous car often needs to see and understand (or perhaps intelligently ignore). You can paint a modest number of images using the Painted Image operator. The location of the northwest corner of an image fragment is the same as the usual vv and hh parameters in (possibly fractional, resolving to 25cm) 2-meter grid units. You also specify some option bits oo, currently two bits with a value 0-3 to indicate how many 90-degree clockwise rotations to apply, plus a third bit +4 for high-res (32 pix/meter, otherwise 4ppm), and then either a line number ff in the image index which contains an offset in the image file to the image to be painted and its height and width in the standard format., or else the image offset hh^vv directly (vertical offset ^vv required) and image height hh and width ww in pixels. You can also optionally override the height and width (cutting pixels off the bottom and right edges) in the index version. As with artifact images in the same file, the surrounding white space is transparent, and you could conceivably overlay several images on your track; the order of the painted image operators in the file determines which images are "on top" (bottom to top). The images replace everything in view except walls and artifacts (and the car avatar, when shown).

The default image resolution is one pixel for each 25cm square of ground; the high-resolution option offers more detail, at one pixel for each 3cm (about one square inch) of ground. You can rotate your images automatically by 90 or 180 degrees; if you need other orientations, you must supply alternate images. Image placement is still limited to 25cm resolution; if you want more precise placement than that, adjust the position of the pixels in the image by shifting the file offset hh^vv (as in the "Divided Highway" track descriptor included in the distributed "TrackImg.txt" file). Note that the height and width of an image is always (file) pixels.

You need to understand that if you rotate the image, the tall & wide parameters in your operator line represent the dimensions  of the rotated pixels. For example if you are using a horizontal image of a painted line to do a north-south dotted line (as in the "Divided Highway" track descriptor), then you would rotate the image (option +1, or in this example +5 for hi-res) and designate tt=160 and ww=10 (because 160 3cm pixels tall s a five meter = 15-foot stripe, and ten 3cm pixels = 10" wide, although only six pixels of width) while the image in the file is the other way around.

If you want white paint on your floor or simulated ground, you need to surround it with non-white pixels so the white pixels are not changed to transparent, but you do not need to include those boundary pixels in the images you paint. If you still want regions of transparency inside the boundaries, you can use a magical color Transprnt (currently specified to be near-white, 0xFEFEFE, but you could choose any one of the 16 million available colors for that purpose) which always becomes transparent. The distributed "TrackImg.tiff" example image file contains a white-painted STOP line with the word "STOP" in front of it, as is commonly used in states without harsh winters. It is oriented for northbound streets; add +1 for eastbound streets or +3 for westbound. Note that the each region of white paint is surrounded by a light gray border, and the two interior unpainted holes (in the "O" and "P") use the Transprnt color.

Track Operators

The track that your simulated car drives around is defined by tracing out (straight) line segments of its right edge, that is the edge that if you are facing in the direction of advance, the right edge begins where the previous advance left off and ends in the cell specified by that operator. The track is defined to be to the left of that edge, and non-track is to the right (the white line is exactly on that straight line from start to end).

There are four compass directions which a track edge can be generally aiming, and at the end of a segment in any of those four directions, the end of the track edge currently being defined ends on the specified final grid cell at the edge corresponding to the direction of advance. For example, the Northward advance operator ends at the middle of the north edge of that cell, unless a fractional cell position is specified, in which case that is the endpoint. The endpoint is significant, because the next track advance operator resumes from there to its own endpoint, effectively drawing a straight line from point to point in the order specified.

You need to be careful that each advance starts off in a direction compatible with where the previous segment left off, because the edge generation tool does not put more than one track edge specifier in any single cell. This makes for a very efficient way to draw the scene: map cells that are nowhere near a track can be quickly determined to be non-track, and similarly map cells within the track but not near an edge are quickly determined to be so;  only those grid cells across which the track edge crosses require extra processing to determine which side of the edge this particular pixel is (and therefore which color to paint it). This means that if the logical track edge crosses the cell more than once -- by advancing to a far side, then returning to a near side -- only one of those lines can be contained in the map data, and your displayed track will have discontinuities. Keeping track of this turns out to be more complicated than it might seem. For this reason we have the advance macros for doing compass straights and reasonable turns automatically.

But first the basic compass operators.

N -- Advance track edge Northward -- N vv hh
S -- Advance track edge Southward -- S vv hh
E -- Advance track edge Eastward -- E vv hh
W -- Advance track edge Westward -- W vv hh

The main purpose of the compass direction name is to tell the track generation tool which edge of the final cell to end at. The final cell is specified by a grid (2-meter) coordinate; the start of each edge segment is defined by the end of the previous track edge segment. The first edge operator creates no edge segments, but only defines an end for the next operator to build on. Most track definitions, and most of every track definition will begin with a single compass direction to specify where to begin and which direction you intend to advance from there, and specify most or all of the track by Advancing in the current compass direction for some number of 2-meter grid cells, with Left and Right turns interspersed as needed.

I -- Initialize as no prior -- I
X -- Erase overstrike prevention history -- X nn
Every operator to advance the current track edge builds on the previous endpoint. That's well and good for completing the outside perimeter, but you need to restart the edge of each inner island of non-track as unconnected. The Initialize operator does that.

When the advance of a track edge crosses exactly through the corners of two diagonally adjacent cells, the white line as drawn might pinch out to nothing there unless the other two cells at that corner are also encoded as track edges. If the track turns at that corner, the logic to add those extra cells can overstrike actual track edge, so the simplistic (and reasonably effective) solution is to keep a modest record of recent grid cells into which track edge has been encoded. Normally, there's no problem, but if you are doing some tricky track edge positioning -- such as for a fork in the track -- the overstrike prevention leaves ugly gaps in the appearance of the track. If you erase the history, you can subsequently specify additional edge lines to override previous generated edges. The number you give it holds off adjacent cell filling for that many more advance lines, which prevents new advance lines from overstriking nearby cells. You don't need to use the erase overstrike prevention history operator if you are building simple loops, only for tricky track shapes; normally you insert it after you see nearby track edges leaving holes in the track edge.

A -- Advance track edge -- A nn
L -- Turn track edge advance Left -- L ss
R -- Turn track edge advance Right -- R ss
These are macros that analyze the current track edge position and generate one or more compass operators as appropriate for advancing the current edge in the specified relative direction. Advance goes a minimum of one cell in the previous direction. You can turn left or right in the next cell (ss=0) which approximates a sharp right-angle turn with a short (1.4m) 45 degree segment across one corner of the grid cell, or you can turn more gradually (ss=8) in an 8x8-cell (15 meter) square, which still ends up turning a full 90 degrees using three short straight segments.

If you only use these three operators after an initial compass starting point, then you only need to keep in mind the total advance in each of the horizontal and vertical directions, and if the net sum is zero, your track will close in on itself. Sharp left and right turns advance one cell more in the previous direction, and none in the new direction. Gradual right (inside curve) turns add six cells in the previous direction and five in the new direction; gradual left (outside) turns add nine cells in the previous direction and eight in the new direction, all as indicated in the following table, which assumes a current direction Southward and a current position 12,18; other starting positions or directions can be worked out by appropriate coordinate transformation, as exemplified on the last two lines of the table:

C.Aim C.Pos Op'tor NewAim NewPos (Generated)
S 12,18 A 4 S 16,18 S 16 18
S 12,18 L 0 E 13,18 E 13 18
S 12,18 R 0 W 13,18 W 13 18
S 12,18 L 8 E 21,26 S 16 19 / E 20 22 / E 21 26
S 12,18 R 5 W 18,13 S 15 17 / W 17 16 / W 18 13
E 13,18 A 4 E 13,22 E 13 22
W 18,13 R 5 N 13,7 W 17 10 / N 16 8 / N 13 7

One of the examples in the distributed "TrackImg.txt" track descriptor file is a simple "Minimal Circle" track, constructed from four gradual turns, one at each corner of an otherwise square track. The straight segments are only two grid units (four park meters) and are dominated by the eight-unit curves, so the result appears roughly circular. If you were to increase the eight straight segments significantly, the track would appear more like a rounded rectangle.

A slightly more comprehensive example is made up from two basic circles joined into a "Figure-8" with added artifacts, which are discussed in the next section on Artifact Specification.

As indicated above, these three advance operators are macros, that is, their operation is to generate one or more compass edge segments. You can observe the generated operators in the console log (look for lines tagged "[*]") if the NoisyMap switch in the Java class "DriverCons" is set to true. Looking at the generated operators can be very helpful for thinking about how to add diagonal edges; you can see an example of that process in the analysis of the "PSU TestOval" track below.

Fractional Positions and Sharp Corners

The track edges built by these tools only approximate what a real track might be like, which we suppose is "good enough" for a simulation of this caliber. Partly because the park map grid is 2-meter squares, and partly because the native resolution of the track building tools was initially integer grid coordinates -- we now do 25cm resolution in the track edge building tools, but are still limited to a single straight edge in each grid cell -- the track edges are somewhat blocky. I suppose with more time and resources to expend on this simulator, we could do 3mm resolution in the track building tools (which is the limit for diagonal line precision in the current encoding) and accordingly better curve approximation, but for the kind of steering precision possible in an actual autonomous car, what we have is generally adequate.

Fractional positions in track edge operators are accepted if one or both the vertical and horizontal coordinates contain decimal points, but the resolution is currently limited to 1/8th grid cell dimensions (25cm). When integer coordinates are specified, the ending points are the middle of their respective faces; with fractional coordinates the endpoints are still on the respective faces, but you specify where along that face.

One of the casualties of this lack of precision is the difficulty in making sharp corners in the track. If your image processing needs to discern true corners in the track edge -- perhaps such as the notch in a fork in the road -- and if you can tolerate careful management of the placement of those corners, corners are possible. The key issue to keep in mind is that each cell can encode only a single straight edge, so your corners must coincide with 2-meter grid cell boundaries. Furthermore, the compass operators always leave the current point at the edge of the cell named by the operator -- for example a Westward operator leaves the current position on the west edge of the final cell of that run. A diagonal edge designated as westward, but advancing more southward, then resuming northward and only slightly westward, will leave a reasonably sharp corner there.

Consider the track descriptor "Divided Highway" in the file "TrackImg.txt" which is a short segment of a two-lane bidirectional north-south track that switches to a divided highway in the middle. The vertex of the divider end is on the boundary between cells (14,5) and (14,6). Track is always built tracing the right edge, so this vertex is approached from the north along the divider, then tilted slightly west, then turned sharply northward again.

The "MinimalSquare" track descriptor in the distributed file "TrackImg.txt" shows an example of using fractional track edges to butt two orthogonal edges up against each other in lieu of a proper corner.

Q -- Convert track to non-track -- Q

To create a park area with non-rectangular borders, start by laying out what looks like an outside track edge that closes in on itself, then Bucket-drop track (or non-track) into the enclosed area, then convert the track to non-track, which converts both pure track cells and edge cells into non-track cells, which is now your odd-shaped park. Then you can add real (unconverted) track on top, as well as walls and artifacts. The track boundaries of the default "PatsAcres" track in the distribution were created this way.

Artifact Specification

The original idea was to specify objects with elevation above the ground (other than the simulated car itself) as part of the cell structure of the track, but that idea fell to the triage axe. Then it became evident that we needed to support not only static objects like trees and hay bales and spectators around the sides (as seen in downloaded images of the PatsAcres go-kart track), but also pedestrians walking across the simulated car's path and maybe even traffic lights that change from green to yellow to red and back on a regular schedule. So artifacts are back in, but on a more limited scale. A 4-bit number identifies the artifact type, which might be a traffic light or an immobile stop sign, or else one of eleven animation sequences.

The information for building an artifact in your track data is divided between track operator text lines which specify the time and space parameters for a particular artifact, and the image specification, which is further divided between an image fragment in a larger collage of artifact images (the file "TrackImg.tiff"), and some index lines telling TrakSim the position, size and scaling for each image fragment. There is a single Object specification line for each phase of an animation, or each directional view aspect of a static artifact. This is long and detailed, so there are macros which require less detailed specification, and which get the rest of the information from the "StopInfo" block of image fragment descriptors -- essentially an index into the image file -- at the end of your track descriptor.

First the operator lines:

Y -- Macro to build static artifact -- Y vv hh ss aa
@ -- Macro to set Time & Position for an animated artifact -- @ vv hh ss ff+aa ee

Artifacts (anything to be drawn on the screen, other than the floor, track and walls) are specified generally by the grid cell position where they are drawn, plus the graphic information for drawing them. Most of the graphic information for each artifact is contained in a line of the image index (the "StopInfo" block of image fragment descriptors), which refers to one of the artifact images from the collage in a separate file; the particular line of the index may be specified in the track operator line, or else (in the case of stop signs and traffic lights) it is hard-wired for the type of artifact.

Artifacts are further distinguished by whether they are static (stay in one grid location, 'Y') or else ('@') dynamically appear to move, encoded as a sequence of static images distributed over space each at a particular time in the simulation. The kind of artifact is further determined by its reference number ss.

Reference number 0 is a stop sign (or any other single object if you so choose), and its images are specified in the first six lines of the index block, which separately designate what it looks like full-on, full reverse, diagonal front and back, or else edge-on left and right. The file "TrackImg.tiff" and the built-in index block (if you do not include a replacement in your track descriptor file) comes with a set of credible stop sign images.

Reference numbers 1, 2 and 3 are used for traffic light specification; you specify only #2, the others are filled in automatically using lines 7 and 8 of the index block. The light cycles automatically on a power of 2 seconds (4, 8, 16, etc) as specified in the xTrLiteTime parameter in the Java class "DriverCons".

Reference number 4 is reserved for starting a timing sequence that forms the basis for reference numbers 5-15, and is defined using the Start Timing sequence operator (below).

Reference numbers 5 through 15 give you up to 11 distinct animated motion artifacts, each with a specific line number ff in the index block, which may be the first of several views, and ending at a particular expiration time ee relative to the sequence started by the currently active sequence timer (or else tt=0 for this particular line to remain active until the end of the simulation). The 'J' macro is normally used to specify a whole sequence of times and positions for your motion animations in a single track operator, giving your artifact relatively smooth and linear motion across the screen. TrakSim does not currently support multiple views on animated artifacts, it is assumed that the animation plays for the simulated car in a particular position or range of motion, and that your animation already has selected the views appropriate for those view angles.

Reference numbers 16 and higher can be used for arbitrary static artifacts, where the reference number designates the line number in the index block you provide as part of your track descriptor file. Multiple view images can be used for the same artifact reference number, as distinguished by the view angle and view range given in the index lines. The orientation angle aa is used by TrakSim together with the relative position of the simulated car and the view ranges you supply to select an appropriate view for presentation. This works the same as for the stop sign, which is essentially a hard-wired special case.

J -- Macro to build moving/animated artifact sequence -- J vv hh ss nn ff zz dd rr ee ii pp*xx
Although this looks big and scary, it's much simpler than putting together a zillion individual '@' operator lines. Each of the five parameters of the '@' operator line are the same here, and are directly used in the first generated '@' operator line. Then nn-1 more '@' operator lines are generated, with the position incremented by the radius rr in direction dd, using line ff+1 of the index block (and its successors, until line zz has been used, then cycling back to ff, as many times as necessary) with an initial expiration time ee increased by ii, which cannot be less than the FrameTime defined in Java class "DriverCons". If the radius is less than the 25cm resolution of the display engine, the fractional parts are accumulated and rounded for best motion accuracy. Direction angle dd is in degrees clockwise from North, and the artifact (pedestrian or car or whatever) moves in an approximately straight line in that direction, by one radius per time increment ii.

Making multiple views work properly turned out too difficult for the available time and resources, but you can optionally specify a fractional and/or negative step value pp to increment the index line number from ff to zz, (and an optional initial index *xx between ff and zz), and it will repeat (or skip) index lines as necessary. If your index is in a rotational order, you can effectively have different views of the same artifact presented appropriately along the way as it morphs from approach to departure.

You can search the console log for lines tagged "{..}" to review the generated '@' operator lines.

At the end of the sequence the artifact is removed from the scene, but you can add a final '@' operator line (or even more 'J' lines) with the same reference number after the 'J' operator line, and if it has a zero expiration time then it will specify what is continually shown after the timed sequence ends. A sequence of 'J' operator lines with varying directions (and/or varying radii) can display the artifact wandering around in a non-linear way, if desired.

O -- Object specification -- O vv hh aa rr tt ww hh^vv pp ee
Although you probably will not be creating any 'O' operators in your track description, it probably helps to see what the macros need to generate. As in the macros, vv and hh are the grid coordinates, aa and rr are the view angle and range used to select one of many 'O' operators from the list for the same coordinates, tt and ww are the pixel dimensions of the image, hh^vv is the position of that image in the file, pp is the resolution in pixels per meter, and ee is the expiration time.

T -- Start Timing sequence -- T vv hh bb ss rr

The animated artifacts are specified by a sequence of '@' operators, each specifying a position and an expiration time. The time is defined by the sequence timer, which counts eighths of a second from the time it is started. A timing sequence is normally initiated when the simulated car crosses some horizontal or vertical position (or both), either Eastward or Southward, or else Westward or Northward -- or rather when tested, the car is on that side of the threshold, without consideration of whether it recently arrived or was already there. The number bbis made up of the sum of four bits:
+8: The car must be South of the vv coordinate to trigger
+4: The car must be North of the vv coordinate to trigger
+2: The car must be East of the hh coordinate to trigger
+1: The car must be West of the hh coordinate to trigger
When all of the specified conditions are met, this sequence is started with an initial time ss seconds. You can trigger any number new timing sequences, which are numbered initially 0 and then sequentially as high as needed, each as specified in rr. It is recommended (but not required) that your initial times ss be in ascending order for successive sequences; if they are not, it may be difficult to properly sequence the animations depending on these times. The timing sequence start operators are processed in the order they occur in your specification file, one pass for each new frame build, but they trigger in numerical order, which allows you to set overlapping trigger conditions without interference. For example if they are physically in reverse numerical order in the file, then each new trigger will affect a complete object placement (screen redraw) process before the next timing sequence is tested. If necessary you can include intermediate trigger conditions such as the simulated car crossing some easily specified lap point before it reaches a trigger position that might otherwise be hard to specify by a single quadrant of the park map.

"StopInfo" Image Index

The artifact operator lines specify location and orientation (and time) for the artifact images; the rest of the numerical specification is (or can be) encoded in the lines of the index block of image fragment descriptors. If you are satisfied with the default stop sign, traffic light, and pedestrian images in the distributed "TrackImg.tiff" file, you can rely on the default index block embedded in the "TrakSim" Java code. Otherwise you can extend or replace the example index information to correspond to your own images in a replacement, possibly enlarged, "TrackImg.tiff" file. This section tells you how to do that.

The image index is identified in the track descriptor file by ".StopInfo" in a line by itself (no spaces) after the final track operator line. The period terminates the processing of track operator lines (unless previously terminated by a period line), and the index is itself terminated by a line that begins with two periods separated by a single space. You can use a single index block for the entire track descriptor file provided that there are no index terminator lines before it.

The index itself consists of ten or more lines of  a half-dozen or so numbers each. Except as noted, there must be spaces between numbers and at the front and end of each line, but extra spaces are ignored. You may include a comment after any line, but it should follow the final space; comments begin with a pair of hyphens and end at the end of the line. The lines mostly consist of fragments of extended track operator lines, that is, the macro artifact operator lines use these lines to finish out generation of the internal artifact operator lines. Each of the active lines must have the required portion of these parameters:

rr+aa tt ww hh^vv pp~nn
rr is the range of view in degrees from 8 to 360, or else 0 (meaning 360), and
 +aa as a suffix to the range parameter is an additive to the orientation supplied by the artifact operator;
tt and ww is how tall and wide the image in the file is, in pixels,
hh is the offset of the image in pixels from the top-left corner, or else its horizontal position, and
 ^vv is the (optional) vertical position, default 0 if the image begins on the top line,
pp is the resolution of the image in pixels per meter when displayed at full resolution;
~nn is the line number, used to validate the index,
=nn is the operator reference number of a group of related images beginning on line nn.

You should preserve the first ten lines as distributed except as necessary when substituting alternative images; your added images will follow this pattern. Note that the four special characters each join two (or more) numbers into a single word. This is important because the parameters are extracted by word count, and optional parameters need to be identified without upsetting the normal word count.

Multiple view images can be used for the same artifact reference number, provided they are in successive lines of the index. They are distinguished by the orientation (adder) angle +aa and view angle range rr, each being considered in order until one of them specifies a direction and view range wide enough to encompass the simulated car, that is, that view should be what the car sees at this relative position. The adder angle +aa is in degrees clockwise from the direction the artifact is facing, that is, to the right from its perspective. The range rr is in degrees total view angle, which is divided equally to the left and right of angle aa. Specify range 0 on the last view as a default in case no previous views are acceptable. If no views are in range, then the artifact is not shown. Be sure to include the "=nn" designator (where "nn" is the line number of the first index line in the collection) which is also the reference number, because that's how the software knows these index lines belong to a single object.

As an example, compare the image index (in the distributed "TrackImg.txt" file) to the stop sign images in the distributed "TrackImg.tiff" file. All six of these images begin on the first pixel row of the file, and represent front and back, left and right, and diagonal views. The front and back each range +/-45 (total 90) degrees from straight-on front or back, then a second (horizontally shrunken) image is used for the diagonal views between 45 and 80 degrees on each side (total 160, but excluding the 90 degrees already caught by the earlier lines). Finally an edge-on side view is offered for the remaining 20 degrees of each side. Notice that the last line is formally a catch-all full 360 range, even though only 20 degrees ever get used. Notice also that there are two different image sizes, with corresponding different specified resolutions. Images likely to be displayed close to the viewer (like the front of a stop sign the car is approaching) should be in a higher resolution than is needed for images only seen more distant, or for which the additional resolution is unimportant.

Most of the images you add will be indexed in lines 16 and higher. Each 'Y' artifact operator line should refer to its corresponding index line for access to the particular image corresponding to that view angle. See the "Figure-8" track descriptor for an example of a car parked by the side of the road facing west. If you start up in that track with the close-up map enabled, you can click in the lower close-up map to direct the aim of the car, and in the smaller park map at the top of the window to place the car. Judicious clicks in the two maps let you migrate the car around the parked car (while facing it), so to see how the display engine scales the images according to the distance, and the views according to where you are looking at it from. Try changing the coordinates and aim of the 'Y' artifact operator line on the car to see how that affects the display.

The same car image is used in a 'J' animation sequence where the car drives by on the crossroad as you approach, while a pedestrian walks across the road in front of the parked car. These are described in more detail in the "Figure-8" track descriptor example below.

Your images in the "TrackImg.tiff" file need to be non-overlapping in rectangles of white space which extends eventually all the way to the four edges. The WhiteAlfa method converts contiguous white space into transparent alpha channel, so if you want white at the edge of your image, you should surround it with something non-white (0xFEFEFE is very nearly white, but will fail to convert to transparent). You should specify in your index the top-left corner of the closest containing rectangle, and the rectangle dimensions for height and width. Images are displayed by skipping or replicating pixels -- which is much faster than blurring inexactly aligned pixels -- so if you have a single vertical centerline that should always be displayed, make sure your rectangle has an odd width centered on that vertical line. For example the stop sign may be shrunken substantially in the distance, but it will always show the post line and at least one column of red above it (at maximum park distance = 320m, the 2m height will display at least 4 pixels high on a 640x480 window).

The images in the distributed "TrackImg.tiff" file are rather crude (having been cobbled together by a non-artist ;-) so you are free to replace them with better art, preferably better than the car images I scraped from the internet under the "Fair Use" doctrine of the copyright law.

Beginning in 2018 June, the distributed "TrackImg.tiff" file includes a revision date in pixels (currently near the lower left corner). You should make sure the file matches the code dates, as some image positions are hard-coded.

Debugging Your Track Descriptor File

TrakSim is not a friendly program and it's easy to make mistakes. Fortunately, once you have a workable track map, you don't need to do this again, except maybe an occasional tweak. In this section We review a few heuristics for making sure the map is what you want.

If you've laid out a track and it doesn't look like you planned, you can use a technique I call "Lion Hunting" from an old mathematician's joke: "How does a mathematician hunt lions in a forest? He divides the forest in half; either the lion is in this half or that half. Then he divides the half where the lion is into half again (now two quarters) and repeats the question, until finally the section of the forest is so small, all you can see is the lion. In finding problems with your track descriptor, place a period at the front of a line about the middle of your descriptor (halfway to the normal terminator). If the resulting track looks OK (for the operators still there), then the problem is in the back half, and you can divide that half in half, otherwise it's in the front half. Repeat until you have found the offending line.

If some view facet of an object disappears, check its index line: if it points to a blank part of the image file, nothing will show. If you left out a required parameter, the whole line will be ignored or effectively deleted.

The map building tool does some minimal checking for inconsistent or invalid parameters, and tries to ignore the errors. If you notice that some track specification is missing, make sure the NoisyMap switch in the Java class "DriverCons" is set to true., then search the console log for lines containing the tag "<??>" which often flag found errors. Immediately to the left of this tag is an error number in the form "*-00)" except that "-00" represents an actual negative number between *-2 and *-99; you then search the source code of the BuildMap method in Java class "TrakSim" for a line containing "rpt = -00" (but with the actual number) and read the nearby comments or other clues for the cause of rejection. The console log line after the "<??>" usually contains the offending source line enclosed in apostrophes (single quotes), usually with your original comment preserved on generated lines.

The first number on lines tagged "(BildArt)" or "(BildMap)" is the file line number, but it will be increased by generated lines if you use macros. Nevertheless, you can think in terms of relative line numbers back (or forward) to a unique comment to help find the offending line.

If your whole image index disappears, check the log for a line "** Invalid StopInfo" which is also tagged "<??>" just after the offending line.

If you are into searching through somebody else's code to find a problem, it might help to understand that many of the methods are organized as a sequence of steps with numbered error checks (the variable "why" is incremented, and thus represents the error number in that method), then at the end, the error number (or zero, if no error) is logged in the console log (among other parameters useful in debugging but probably no longer helpful) after an "="; searching through the method code for "why =" will generally locate the problem condition. There are vaious final boolean switches in the Java class DriverCons to turn those error summaries (and other diagnostics) on or off. The console log becomes impossibly long (and slow) if they are all turned on, which is why most of them are turned off after the code seems to work correctly.

If you are having problems with the display of images, you can use the Painted Image operator 'U' with a horizontal position greater than the map width (128) but less than the window width, and then it (and the vertical) are window coordinates for whatever part of the image you specify, and it is shown on top of everything else, unrotated and unscaled, with the transparent portions black. It's just a debugging tool, but I suppose you could also use it to put a logo or image version number in some unused part of the window.


The first two descriptors in the track descriptor file "TrackImg.txt" are discussed separately below. First we will touch lightly on the other example track descriptors.

The third example descriptor "Minimal Circle" consists of nothing more than four 90-degree turns connected with short straight segments into an approximate circle. It is useful for studying how the track macro operators make curves, so that you can build more complex tracks with diagonals and other kinds of curves, as in the first two examples.

The fourth example descriptor "Divided Highway" illustrates sharp corners in the track, such as one might find in the real world where a two-lane country road splits into a divided highway. It also shows how to make dotted lines in the track using the Painted Image operator. in this case we want the painted line split across the cell boundary, but not as wide as the (low-resolution) placement parameters would make it, so it uses part of the yellow stripe in the distributed "TrackImg.tiff" file, but includes some (transparent) white space to effectively shift the partial paint over to be centered on the grid cell boundary, and thus to line up with the divider.

The first stripe is rotated counter-clockwise 90 degrees, so the 5-pixel transparent region (above the yellow stripe in the file) is to the left of the displayed stripe and effectively positions the paint five pixels to the right of the specified horizontal coordinate (5.9 grid units, rounded to the nearest 25cm = 5.875). The second stripe is rotated clockwise, so it's harder to understand: the pixel width counts backwards from the next 1/8th grid coordinate, and the blank space below the stripe shifts the paint to the left. It's much easier when rotated images fill the 25cm grid units, that is, you are using images that are an integer multiple of 32 pixels (including transparent regiousn at the edges).

The fifth example demonstrates the use of fractional edge coordinates.

Figure-8 With Artifacts

When you are creating a new track for TrakSim, it helps to do a little at a time. I try to do the perimeter first (after the initial park fill), then insert a track-end line and run it through the BuildMap method to see if I succeeded in getting the track to close on itself (usually I fail the first few times). Then I add the inside islands and look again.

In this case I started with two copies of the "Minimal Circle" then converted the southeast corner of the upper one and the northwest corner of the lower one into straights to meet at the crossroad. My next thought was to erase the crossover edges using the Fill operator, and that was OK (see the "Minimal Square" track example), but then I decided a cleaner solution is to do a hard right turn at each corner of the crossroads. That way the whole track can be constructed from Advance and turn operators. In both cases it took several attempts to get the track to close properly.

Then I drop a bucket of track inside and look again. If the track is narrow, or if I failed to get the inner loops spaced correctly to the perimeter, the bucket drop may not reach the whole track. Narrow lanes are not unusable, but you must put more effort into filling it. If the whole track is narrow, it is sometimes easier to bucket the track after the outside perimeter is complete, then bucket non-track in each island after the island perimeters are closed.

The next step is to add the artifacts. Especially when you build the track edges using relative track advance macros, it's hard to get the artifact placement correct. I found some help placing "milestones" alongside the track, like this enhanced screenshot. I left these in the file but disabled, so you can see how to use them. Notice they give the coordinates in park meters, although the operator coordinates are in 2m grid units. You could do them in grid coordinates if that helps.

If you look carefully in this screenshot, there are two obvious yellow dots (artifacts), one near the 50 milestone, which is the pedestrian before he starts walking, and the other in the middle of the track even with the 70 milestone. It was tricky to get these animations to pass by before the simulated car got to the intersection, and the initial placement was a large part of that (there may be slight differences in the distributed file).

Much harder to see in this screenshot are the two artifacts (the parked car near the pedestrian, and the stop sign) that are right on the white track-edge line. The traffic light also has a yellow dot in the middle of the street, but it's right next to the milestone which is the same color.

You can dynamically reposition the car avatar to be looking at some part of the track up close. With the ShowMap option turned on, click in the upper map to place the car, and in the lower map (if showing) to change its orientation (it will turn to face where you clicked).

It's easier to place your artifacts and paint images one at a time, then disable them while working on the next one. The TrakSim Java class code contains an image index suitable for everything here in the Figure-8 track -- indeed, for all the tracks in the "TrackImg.txt" track descriptor file -- but the index lines relating to the car images are commented out. If you have no plans to replace this car or to add other artifacts, and if you make much use of my car images, you could simply uncomment the extra lines.

I set up four stop signs and four matching painted STOP words for use on the street, but enabled only the one facing the initial car position. As I said, placing these things is tricky. I made a guess for each one, then fine-tuned the placement after seeing what TrakSim did to my handiwork.

If you need an overhead traffic light placed on an intersection of streets in compass (N/S/E/W) directions as here, and if you are satisfied with E-W starting green (N-S red), you don't need to worry about rotation. Otherwise turn it 90 degrees. Of course if your intersection is for streets on a diagonal, you need to rotate the traffic light to face the respective streets. If you need more than the four orthogonal directions, you can overlay two traffic lights 10" (25cm) apart, and then whichever one is in front will cover the other(s). TrakSim assumes that multiple artifacts at the same coordinate position are different perspectives on the same artifact. The car will not crash when it rolls over the traffic light coordinates, but if you want that to happen, like on a post that the car cannot drive through, you can add a second artifact, just the post, 10" away.

All other artifacts use extra lines in the track index for orientation and placement. I put a car (reference number 16) parked on the far side of the crossroad. This particular car image I scraped off the internet in compliance with the "Fair Use" doctrine of the copyright law (non-commercial purpose), and if you look closely, you can see their watermark; if you want to use this image commercially (like to sell copies of this program or its data) you will need to obtain permission or replace the images with your own. The 45-degree aspects I generated from the end-on and side views, so it's a little clumsy looking. Better to start with at least eight full-circle images (12 or sixteen would look better, at the cost of slightly more processing time). In any case, TrakSim will calculate which aspect to present based on the relative position of the simulated car with respect to the artifact, using the specifications in your image index, and will also scale it appropriately. Try placing the car avatar in different positions and orientations around the parked car to see how this works.

There is one timer sequence, in which timer #0 is set to start when the avatar car is both south of 40m vertical (20 grid units) and east of 32m horizontal. If you start your car at the top of the figure 8 track, or along its left edge, the timer will wait until the car gets near the position where it is currently set to start before tripping. Immediately after the timer starts, the pedestrian (reference number 5) starts walking south across the road (in front of the parked car), alternating between legs together and legs spread in a step.

A quarter-second after the timer starts, the animated car (reference number 6) activates and heads north, initially presenting its left-front aspect, then switching to its left aspect 1.3 seconds later (four animation steps, at 1/3rd second each), and then to its left-rear aspect four more frames later, then stopping somewhere north of the intersection, a total of 16 frames.

Normally you would not use so many artifacts in one TrakSim run, but you could.

If your steering heuristic is looking for white-line track edges, you might want to turn off the painted "STOP" on the street until your code is good enough to not be confused by all the extra white paint. The same paint on the "PSU TestOval" is already disabled.

PSU TestOval

The summer program TrakSim was designed for is held in a room in the Portland state University Engineering Building. The assigned room is too small to drive a 1:8 scale car around in, but there is a hall on the ground floor on the north side of the building next to the atrium that does not get much foot traffic, and is long enough to tape up a reasonable track (see westward and eastward views from the middle, below). I downloaded the plans for the building and constructed a track descriptor approximately to scale. It has two straight runs along the narrow part of the hallway, which broaden into loop turn-arounds at the ends. You can tape up the hall to match the track as coded, or alter the file to match whatever you tape up.

This track descriptor is distinctive among the examples in the "TrackImg.txt" file in that there is a diagonal section of the track at the east end, where the eastbound straight leg curves around to loop back to the west. At the west end, I just inserted four 90-degree turns, but the last one turns out instead of in. Then I did the same for the east end, but I looked at the generated segments for the first curve turning south, and for where it turns back east. Both curves have a 45-degree segment in the middle, so I stretched the two segments into a single 45-degree segment and discarded the more vertical segments between them, and replaced my two curves with the original beginning and ending segments, plus my elongated middle segment. That was a lot easier than trying to figure out the exact ending points for each segment.

Let's go back through the process in more detail.

First I stripped out all the extraneous operators from my working track descriptor and kept only the two curves I wanted to elongate into a diagonal. I also changed the track and floor colors to be more distinctive, and placed the car avatar over near where I would be working, so the close-up map would show the details I'm interested in. I usually cap off an incomplete lane, so to bucket-fill the interior, but I think it's easier to see the edges without that.

0x4000FF 0x96961966 0x3200C2 45 15 "Test Diagonal"

F 1 1 11 124 0
F 1 90 30 126 0

E 9 90 -- start facing east (9,90)
A 5    -- advance east to (9,95)
R 5    -- curve to south (14,101)
A 2    -- short straight south (17,101)
L 8    -- curve to east (25,109)
A 2    -- short straight east (25,112)

L 0     -- cap off lane (22,112)..
A 2
L 0

A 2    -- short straight west (22,109)
R 5    -- curve to north (17,104)
A 2    -- short straight north (14,104)
L 8    -- curve to west (6,96)
A 5    -- short straight west (6,90)

L 0     -- cap off lane (9,90)..
A 2
L 0

-- B 8 92 -- bucket track (disabled)

.       -- end of descriptor

My first try I failed to equalize the east- and west-advances, so the resulting track didn't meet up. I also counted wrong through the first end cap, but the error was only in the comments and did not affect the resulting track. Here you see a stitched composite of two screen shots.

Then I searched the console log for the "[*]" tags of generated macro track operators -- or more particularly, for the (unique) comments in the four curve operators, these four pairs:

R 5    -- curve to south (14,101)
  [*] '\E 10 98  (+1,+3)\S 11 100  (+1,+2)\S 14 101  (+3,+1)'

L 8    -- curve to east (25,109)
  [*] '\S 20 102  (+4,+1)\E 24 105  (+4,+3)\E 25 109  (+1,+4)'

R 5    -- curve to north (17,104)
  [*] '\W 21 107  (-1,-3)\N 20 105  (-1,-2)\N 17 104  (-3,-1)'

L 8    -- curve to west (6,96)
  [*] '\N 11 103  (-4,-1)\W 7 100  (-4,-3)\W 6 96  (-1,-4)'

One of the reasons for using a diagonal to merge the two curves is to shorten the vertical transit, because (unlike the atrium at the west end of the hall) the slightly wider space at the end of the corridor was not wide enough for a full right+left curve inside the return lane turns. Here you see that the 90-degree curve back to the east ends on grid row 25, but the south wall at that end is on grid row 22.

So the plan is to preserve the first segment of the "R 5 curve to south" and join it to the final segment of the "L 8 curve to east" with a single diagonal segment, essentially preserving the middle segment of the curve east, which now starts after that first segment of the curve south, at coordinate (10,98). Ordinarily an S-curve (one left + one right) adds +14 grid units in each direction, but by eliminating the two inner end segments, I can take "+3 +4" =  +7 off that, so that instead of ending on grid row 25, it can end on grid row 18. I preserved the +2 advance between the two curves, for a net reduction -5, to end on grid row 20. I preserved the horizontal advances.

So my new gentler S-curve starts with "E 10 98" as before, then ends with the "E 24 105 \E 25 109"  from the other curve, but with the southern advance reduced -5, thus:

E 10 98 -- 1st segment of R 5 curve to south (+1,+3)
E 19 105 -- 2nd seg't merged w/2nd seg of.. (+7,+7)
E 20 109 -- 3rd segment of L 8 curve to east (+1,+4)

I then did the same thing on the return. It came out pretty good. I think I made a few mistakes along the way, but looking at each iteration in the close-up map enabled me to see my mistakes. With a little creativity like this, you can construct arbitrarily complex tracks to run your car through.

Back to front of TrakSim

Rev. 2018 June 12