Skip to content

2.6.0

Latest

Choose a tag to compare

@wdolinar wdolinar released this 30 Sep 16:17

2.6.0 - Traces seed at grid indices, lie on the surface, and coast past the boundary

Follow Flow Path draws a traced arrow at chosen grid points or cells. Three
things made that harder than it had to be. The tracer took only positions, so
where a cell's glyph sits, and which seeds could trace, had to be worked out
outside this library, and a path could be paired with its index only by
matching positions. The positions a trace returned did not sit on the grid's
surface. And a trace that reached the edge of the grid stopped there, so arrows
near the boundary came out short or empty.

Seeding at grid indices

New: StartTracesAtIndices, and start_traces_at_indices in Python. It begins the
same batch StartTraces does, seeded at point or cell indices, with one entry per
index in the order given. A point seed starts at the point. A cell seed starts at
the mean of the cell's points, where a display drawing one glyph per cell puts
the glyph, rather than at its area centroid.

A seed traces only if it is active at both loaded time steps: a point when some
cell around it is active, a cell when it is itself. That is judged by the index,
not by where the seed lands, so a caller can predict it from its own activity.
A seed that does not trace keeps its slot, with an empty path,
GTEXIT_SEED_NOT_TRACEABLE and an XM_NODATA seed magnitude, so each result pairs
with its index by position alone.

The batch is refused whole when the location is neither points nor cells, the
times are not one per index, or an index is not a point or cell of the grid. In
Python that raises ValueError.

Every position lies on the grid's surface

Every position a trace returns on the grid, the seed's included, now takes its z
from the grid at its x and y, whatever z the seed was given. This holds for
TracePoint and StartTraces as well as the new call.

The z is interpolated with the weights of the lookup that found the velocity
there, so it costs no search of its own:

  • A cell-located field is triangulated as each cell's centroid fan, which is the
    triangulation a display draws the grid with.
  • A point-located field is triangulated between the grid's own points, so its z
    comes from the points' elevations. Across a cell that is not planar, that can
    differ from the fan a display draws.

Only the end of a path cut short by the max tracing distance takes a search,
since the field was never looked up there. It searches the field's own
triangulation.

Crossing the boundary no longer ends a trace

A trace that crossed the grid's boundary used to stop there with
GTEXIT_LEFT_GRID. Past the boundary there is no field to follow, so the trace now
carries on in a straight line at the velocity of the step that crossed, the
direction it left in. It keeps the z of the crossing, since there is no surface
to lay it on.

The coast ends like any other trace, on its time or distance budget. Otherwise it
waits at the end of the loaded window and resumes along the same line when the
next time step arrives. It does not follow the field again if its line passes
back over the grid.

The boundary includes the shores of holes in the grid, such as islands. A trace
that reaches an island coasts across it and on over the grid beyond, taking no
notice of the field there. This is deliberate, and the header says so.

An inactive cell is not the boundary. A trace still stops at its edge, with
GTEXIT_LEFT_GRID, even when the cell sits on the trace's way out of the grid.
That exit reason now means only that, and its message reads "Point has traveled
into an inactive cell." The name is unchanged so the enum's values and the
Python names stay stable, and the Python enum values now carry docstrings.

Where a step leaves the field is now found piece by piece between the edges it
crosses, not from its end alone. So a step that leaves across a gap and ends in a
dry cell beyond it coasts from where it left. A particle held still by a calm
field holds at the boundary rather than coasting off at the speed of
interpolation noise.

Every caller that traces out of the grid is affected: it gets back a path that
runs on past the boundary instead of one that ends at it.

Known limitations

  • With no time or distance budget, a coast never ends. It runs to the end of
    every window it is given, possibly far past the grid. A trace circling in the
    field has the same property. Set a distance budget, as next_ms does for flow
    paths.
  • A crossing is looked for only where a step ends with no field. So a hole or an
    inactive cell narrower than a step can be stepped over. The max change distance
    bounds how long a step can be.

Compatibility

StartTracesAtIndices is declared last in the header, after SetInitialDeltaTime,
so it appends a vtable slot rather than shifting the ones above it. It is a pure
virtual on a public abstract class, so an external subclass of XmGridTrace must
implement it. Callers using the New() factory are unaffected.

Results change in two ways:

  • z: every position on the grid is on the surface.
  • Boundary: traces that reach the boundary continue past it.

This is a minor release, as 2.5.0, 2.4.0 and 2.1.0 were for the same kinds of
change.

Dependencies are unchanged from 2.5.0: xmscore 7.0.13, xmsgrid 9.1.1,
xmsinterp 7.0.12, xmsextractor 10.0.10.