OnTubeVis Streaming API

This is the documentation of the OnTubeVis streaming API as implemented in the reference OnTubeVis OpenGL/C++ desktop application.

Structure of the documentation

The starting point for exploring the API is the OnTubeVis include directory. The API functions can be found in each of the modules defined by the individual header files. For structures, the Classes page provides alphabetically ordered access to their respective documentation. In addition, the API functions most relevant for each structure are referenced in their "See also" sections.

Information on specific API elements (including the functions) can always be quickly looked up using the search function (via the search icon in the top menu bar).

Usage example

For a minimal usage example of the API, see the sample client included in therepository. Its main code is also reproduced here:

int shutdown_otv (void)
{
    const OTV_TerminateResult status = otv__terminate();
    if (status.terminated) {
        // OnTubeVis did shut down
        printf("OnTubeVis service exited with code %i.\n", status.exit_code);
        return status.exit_code;
    }
    // Something went wrong...
    printf("OnTubeVis service did not honor shutdown request!\n");
    printf("Performing unclean shutdown.\n");
    return -1;
}

int main (int argc, char** argv)
{
    // Forward control to the service
    // - init the OnTubeVis implementation
    otv__startup(argc-1, (const char *const*)argv+1);
    // - while the init is ongoing, we could do other stuff.
    const bool otv_initialized = otv__wait_for_startup();
    if (!otv_initialized) {
        printf("OnTubeVis service failed to initialize!\n");
        return -1;
    }


    // Preparation: create and activate a visualization setup

    // Create it. The provided name will show up in the OnTubeVis GUI as the name of the visualized dataset
    OTV_VisSetupHandle test_setup = otv__create_VisSetup("test");
    uint32_t traj_ids[3];

    /* Add a surface color layer */ {
        OTV_LayerConfig new_layer = {
            .type=SurfaceColor, .outline=0,
            .static_params=otv__construct_SurfaceColorInfo(
                /* value: (not used here, see 'static_flags')*/otv__Rgb(0, 0, 0),
                /* color_map: */Rainbow, /*interpolation_mode: */Linear, /*static flags: */SCI_STATIC_NONE
            )
        };
        otv__add_layer(test_setup, &new_layer);
        otv__layer_color_source(
            test_setup, /* layer: */0, /* in_range: */otv__Interval(0, 100),
            "Some Quantity"
        );
    }

    // Add two trajectories
    traj_ids[0] = otv__add_trajectory(test_setup, /* radius: */.5f);
    traj_ids[1] = otv__add_trajectory(test_setup, /* radius: */.5f);

    /* Add a sign blob layer. It will be added to all existing trajectories also. */ {
        OTV_LayerConfig new_layer = {
            .type=SignBlob, .outline=.0625f,
            .static_params=otv__construct_SignBlobInfo(
                /* color: */otv__Rgb(1/3.f, 2/3.f, 1), /* color_map: */UndefinedColormap,
                /* radius: */1, /* value: (not used here, see 'static_flags')*/0,
                /* static_flags: */SBI_STATIC_COLOR
            )
        };
        otv__add_layer(test_setup, &new_layer);
        otv__layer_value_source(
            test_setup, /* layer: */1, /* value_id: */0,
            /* in_range: */otv__Interval(-3, 3), "Signed Quantity"
        );
    }

    // Now we add another trajectory. It will inherit the already set-up layers.
    traj_ids[2] = otv__add_trajectory(test_setup, /* radius: */.5f);

    // Start a visualization session with the above setup
    const bool session_started = otv__start_vis_session(test_setup);
    otv__free_VisSetup(test_setup); // now not needed anymore
    if (!session_started) {
        printf("Unable to start visualization session!\n");
        const int exit_code = shutdown_otv();
        return exit_code ? -1 : exit_code;
    }


    // Stream stuff into OnTubeVis

    // Keep track of the most recent glyph's extent to check if there is no overlap when we want to stream another one.
    // we have two layers (streaming to just 1 trajectory in this example), so it's actually a two-element array.
    float last_border[2];

    /* Stream test glyphs (trajectory segment not yet there) */ {
        // First sign blob on layer 1
        OTV_GlyphData sign_blob = otv__construct_SignBlobData(
            /* s: */0.5f, /* color: (configured to be static)*/0, /* value: */2.25f
        );
        OTV_Vec2 extents = otv__instantiate_Glyph(traj_ids[1], 1, &sign_blob);
        printf("Streaming new sign blob - extents relative to anchor are [%f..%f]\n", extents.x, extents.y);
        last_border[1] = sign_blob.s + extents.y;
        otv__stream_glyph(traj_ids[1], /* layer: */1, &sign_blob);

        // First surface color sample on layer 0
        OTV_GlyphData surface_color = otv__construct_SurfaceColorData(/* s: */0, /* color: */0);
        extents = otv__instantiate_Glyph(traj_ids[1], 0, &surface_color);
        printf("Streaming new surface color sample - extents relative to anchor are [%f..%f]\n",
               extents.x, extents.y);
        last_border[0] = surface_color.s+extents.y;
        otv__stream_glyph(traj_ids[1], /* layer: */0, &surface_color);
    }

    /* Stream a test trajectory */ {
        // 1st node
        OTV_HermiteNode n = { // our test segment will be an x-line 4 units long...
            .time=0, .position={0, 0, 0}, .tangent={4, 0, 0}
        }; // ...so the x-derivative must also be 4 if we want a uniform parametrization
        otv__stream_spline_node(traj_ids[1], &n, NULL/* first sample doesn't have an arclength */);
        sleep(1);

        // Stream a second sign blob glyph (first trajectory segment still missing its end node)
        OTV_GlyphData sign_blob = otv__construct_SignBlobData(
            /* s: */2.f, /* color: (configured to be static)*/0, /* value: */.25f
        );
        OTV_Vec2 extents = otv__instantiate_Glyph(traj_ids[1], 1, &sign_blob);
        printf(
            "Streaming new sign blob - free space to previous one is %f\n", sign_blob.s+extents.x - last_border[1]
        );
        last_border[1] = sign_blob.s+extents.y;
        otv__stream_glyph(traj_ids[1], /* layer: */1, &sign_blob);
        sleep(1);

        // 2nd node
        const OTV_HermiteNode n0 = n; // we need to keep a copy of the first node for computing arc length
        n.time += 10;
        n.position.x += 4;
        // - compute arc length - we use the numerical approximation provided by the API here, which is relatively slow
        //   (albeit fairly accurate). Consider using information available to you in your data to infer arc length
        //   yourself.
        const OTV_SegmentArclen alen = otv__compute_arclen(&n0, &n, 0);
        // - dispatch the node
        otv__stream_spline_node(traj_ids[1], &n, &alen);
        sleep(1);

        // Stream a second surface color sample on layer 0
        OTV_GlyphData surface_color = otv__construct_SurfaceColorData(/* s: */2, /* color: */100);
        extents = otv__instantiate_Glyph(traj_ids[1], 0, &surface_color);
        printf(
            "Streaming new surface color sample - free space to previous one is %f\n",
            surface_color.s+extents.x - last_border[0]
        );
        last_border[0] = surface_color.s+extents.y;
        otv__stream_glyph(traj_ids[1], /* layer: */0, &surface_color);
        sleep(1);
    }

    /* Stream a third surface color sample on layer 0 */ {
        OTV_GlyphData surface_color = otv__construct_SurfaceColorData(/* s: */3.95, /* color: */50);
        const OTV_Vec2 extents = otv__instantiate_Glyph(traj_ids[1], 0, &surface_color);
        printf(
            "Streaming new surface color sample - free space to previous one is %f\n",
            surface_color.s+extents.x - last_border[0]
        );
        last_border[0] = surface_color.s+extents.y;
        otv__stream_glyph(traj_ids[1], /* layer: */0, &surface_color);
        sleep(1);
    }

    /* Stream a third sign blob glyph (falls onto the now complete first segment) */ {
        const OTV_GlyphData sign_blob = otv__construct_SignBlobData(
            /* s: */3.75f, /* color: (configured to be static)*/0, /* value: */-1.25f
        );
        const OTV_Vec2 extents = otv__instantiate_Glyph(traj_ids[1], 1, &sign_blob);
        printf(
            "Streaming new sign blob  - free space to previous one is %f\n",
            sign_blob.s+extents.x - last_border[1]
        );
        last_border[1] = sign_blob.s+extents.y;
        otv__stream_glyph(traj_ids[1], /* layer: */1, &sign_blob);
        sleep(1);
    }


    // Shutdown

    // Wait for user to terminate the test client
    printf("\nStreaming done. Press ENTER to shut down.");
    getchar();

    // Request service to stop and quit
    return shutdown_otv();
}