Pyramid Example

// Writes a small OMF file containing two elements: the surface and outline of a square pyramid.
// Then reads that file back and prints the data.

#include <omf.h>
#include <stdio.h>
#include <stdlib.h>

static const float VERTICES[][3] = {
    { -1.0, -1.0, 0.0 },
    {  1.0, -1.0, 0.0 },
    {  1.0,  1.0, 0.0 },
    { -1.0,  1.0, 0.0 },
    {  0.0,  0.0, 1.0 },
};

static const uint32_t TRIANGLES[][3] = {
    { 0, 1, 4 },
    { 1, 2, 4 },
    { 2, 3, 4 },
    { 3, 0, 4 },
    { 0, 2, 1 },
    { 0, 3, 2 },
};

const uint32_t SEGMENTS[][2] = {
    { 0, 1 },
    { 1, 2 },
    { 2, 3 },
    { 3, 0 },
    { 0, 4 },
    { 1, 4 },
    { 2, 4 },
    { 3, 4 },
};

static bool write(const char *path) {
    OmfError *error;
    OmfWriter *writer;
    OmfProject project;
    OmfSurface surface;
    OmfLineSet line_set;
    OmfElement element;
    const OmfArray *vertices;
    OmfHandle *proj_handle, *ele_handle, *tags_handle;

    omf_reader_limits(NULL);
    // Open file.
    writer = omf_writer_open(path);
    // Fill in `project` with the required name and optional description.
    project = omf_project_init("pyramid.omf");
    project.description = "Contains a square pyramid.";
    project.author = "Somebody";
    proj_handle = omf_writer_project(writer, &project);

    // First a surface element. Start writing the vertex and triangle arrays
    // and putting them in `surface`.
    vertices = omf_writer_array_vertices32(writer, VERTICES, 5);
    surface = omf_surface_init(
        vertices, omf_writer_array_triangles(writer, TRIANGLES, 6));
    // Fill in `element` with the surface and other fields.
    element = omf_element_init("Pyramid surface");
    element.surface = &surface;
    element.color_set = true;
    element.color[0] = 255;
    element.color[1] = 128;
    element.color[2] = 0;
    element.color[3] = 255; // Opaque
    // Write the element.
    ele_handle = omf_writer_element(writer, proj_handle, &element);
    // Add metadata to that element.
    omf_writer_metadata_string(writer, ele_handle, "revision", "1.2");
    tags_handle = omf_writer_metadata_list(writer, ele_handle, "tags");
    omf_writer_metadata_string(writer, tags_handle, NULL, "foo");
    omf_writer_metadata_string(writer, tags_handle, NULL, "bar");

    // Second a line-set element. This uses the same vertices array as the
    // surface. If we wrote it a second time the duplicate would be detected
    // and removed but we can also pass it in to both geometries.
    line_set = omf_line_set_init(
        vertices, omf_writer_array_segments(writer, SEGMENTS, 8));
    // Clear and fill in `element` again.
    element = omf_element_init("Pyramid outline");
    element.line_set = &line_set;
    element.color_set = true;
    element.color[0] = 0;
    element.color[1] = 0;
    element.color[2] = 0;
    element.color[3] = 128; // 50% transparent
    // And write it.
    omf_writer_element(writer, proj_handle, &element);

    // Finish writing and close the file.
    omf_writer_finish(writer, NULL);

    // Check for errors. The `omf_error` call will return the *first* error,
    // even if several functions failed since after detecting an invalid
    // argument.
    if ((error = omf_error()) != NULL) {
        fprintf(stderr, "[write failed] %s (%d)\n", error->message, error->code);
        fflush(stderr);
        omf_error_free(error);
        return false;
    }
    return true;
}

static bool read(const char *path) {
    OmfReader *reader;
    OmfError *error;
    const OmfProject *project;
    const OmfElement *e;
    // Dynamically allocated buffer for vertices.
    float (*vertices)[3];
    // For the triangles and segments we'll use fixed-size buffers for simplicity. Initialise
    // these buffers to zero so that if the read fails we don't end up printing uninitialised
    // memory.
    uint32_t segments[8][2] = { 0 };
    size_t i;
    OmfArrayInfo info;
    OmfTriangles *tri_iter;
    uint32_t tri[3];

    // Open the file.
    reader = omf_reader_open(path);
    // Read the project.
    project = omf_reader_project(reader, NULL);
    if (!project) {
        error = omf_error();
        fprintf(stderr, "[read failed] %s (%d)\n", error->message, error->code);
        omf_error_free(error);
        return false;
    }
    // Print project contents.
    printf("name: %s\n", project->name);
    printf("description: %s\n", project->description);
    printf("coordinate_reference_system: %s\n", project->coordinate_reference_system);
    printf("origin: %g, %g, %g\n", project->origin[0], project->origin[1], project->origin[2]);
    printf("author: %s\n", project->author);
    e = &project->elements[0];
    printf("surface:\n");
    printf("    name: %s\n", e->name);
    printf("    description: %s\n", e->description);
    printf("    color: #%02x%02x%02x%02x\n", e->color[0], e->color[1], e->color[2], e->color[3]);
    printf("    origin: %g, %g, %g\n",
           e->surface->origin[0], e->surface->origin[1], e->surface->origin[2]);
    // Allocate a buffer for the vertices, pretending we don't know the required length already.
    // Calloc Initializes the memory to zero for us.
    info = omf_reader_array_info(reader, e->surface->vertices);
    vertices = calloc(info.item_count, sizeof(float[3]));
    if (vertices == NULL) {
        fprintf(stderr, "memory allocation failed");
        return 1;
    }
    omf_reader_array_vertices32(reader, e->surface->vertices, vertices, info.item_count);
    printf("    vertices:\n");
    for (i = 0; i < info.item_count; i++) {
        printf("        % g, % g, % g\n", vertices[i][0], vertices[i][1], vertices[i][2]);
    }
    // Read the triangles using the iterator API.
    printf("    triangles:\n");
    tri_iter = omf_reader_array_triangles_iter(reader, e->surface->triangles);
    while (omf_triangles_next(tri_iter, tri)) {
        printf("        %d, %d, %d\n", tri[0], tri[1], tri[2]);
    }
    e = &project->elements[1];
    printf("line-set:\n");
    printf("    name: %s\n", e->name);
    printf("    description: %s\n", e->description);
    printf("    color: #%02x%02x%02x%02x\n", e->color[0], e->color[1], e->color[2], e->color[3]);
    printf("    origin: %g, %g, %g\n",
           e->line_set->origin[0], e->line_set->origin[1], e->line_set->origin[2]);
    // Read the segments into a fixed-size buffer.
    omf_reader_array_segments(reader, e->line_set->segments, segments, 8);
    printf("    segments:\n");
    for (i = 0; i < 8; i++) {
        printf("        %d, %d\n", segments[i][0], segments[i][1]);
    }

    // Close the reader only once we're done with `project`.
    omf_reader_close(reader);

    // Check for errors.
    if ((error = omf_error()) != NULL) {
        fprintf(stderr, "[read failed] %s (%d)\n", error->message,
                error->code);
        omf_error_free(error);
        return false;
    }
    return true;
}

int main(void) {
    if (!write("pyramid.omf")) return 1;
    if (!read("pyramid.omf")) return 1;
    return 0;
}