Categories

# I/O: Bridging your Software with Fade2D

Fade2D offers powerful triangulation functionalities, essential in fields like land surveying, mining, and road construction. This article provides an overview of the I/O operations engineers use in practice to bridge their software with the Fade2D library. We will discuss the individual I/O methods through a C++ example, that you can also find in the download package under examples_real/importExportLoadSave.

## Generating a Delaunay Triangulation and Zones

The function `createRandomTriangulation()` generates a random terrain triangulation with two zones, which we will use in various contexts throughout this example. However, as its purpose is solely to provide sample data for this example, we will not further discuss it. The triangulation comprises 5313 triangles, with Zone1 containing 675 triangles and Zone2 containing 217 triangles. See the image below.

## Importing Triangles from your Data Structures

In practice, you often have triangles stored in your own data structures in memory that you want to import into Fade2D. The following function `importFromPointTriples()` demonstrates how to achieve this.

```void importFromPointTriples(std::vector<Zone2*>& vZones)
{
cout<<"* importFromPointTriples()"<<endl;
// * 1 *   Setup
vector<Triangle2*> vYourTriangles;
for(Zone2* pZone:vZones) pZone->getTriangles(vYourTriangles);
vector<Point2> vYourCorners;
for(Triangle2* pT:vYourTriangles)
{
vYourCorners.emplace_back(*pT->getCorner(0));
vYourCorners.emplace_back(*pT->getCorner(1));
vYourCorners.emplace_back(*pT->getCorner(2));
}
cout<<"  Setup, vYourCorners: "<<vYourCorners.size()<<" points (= "<<vYourCorners.size()/3<<" triangles)"<<endl;

// * 2 *   Import from vYourCorners to Fade_2D
Zone2* pZone=dt2.importTriangles_robust(vYourCorners);
cout<<"  imported pZone:\t"<<pZone->getNumberOfTriangles()<<endl<<endl;
cout<<"  imported dt2:\t"<<dt2.numberOfTriangles()<<" triangles (with convex fill triangles)"<<endl;
}```
• Step 1, Setup: Let’s assume that `vZones`, provided as an argument of the function, is part of your own data structures, and that you want to import the triangles of these zones into a new triangulation `dt2`. You access your triangles and store the 3 corner points of each triangle in `vYourCorners`.
• Step 2, Import: Create a new Fade_2D object `dt2` and invoke `importTriangles_robust()` with `vYourCorners` as parameter for this call. This command returns one `Zone2* pZone` containing all imported triangles.

Result:
The imported `Zone2* pZone` contains only the triangles that correspond to `vYourCorners`.

The underlying triangulation `dt2` includes additional fill triangles that complete the convex hull. Fade added these because a Delaunay triangulation is always convex.

Note: The `importTriangles_robust()` method, which we use, automatically corrects wrong orientations and overlapping triangles, if any, which may result in a few extra triangles being generated in `pZone`.

Warning: Avoid using the similar `importTriangles()` method unless you’re working with extremely large datasets and are confident that they are 100% error-free. While this method is faster due to fewer checks, feeding corrupt data to it can cause unexpected behavior. Therefore, we strongly recommend to use the newer `importTriangles_robust()` method.

## Importing Triangulations from .PLY and saving to .PLY

PLY, or Polygon File Format, is a popular file format for storing 3D geometry. Writing a triangulation or zone to a .PLY file is demonstrated by the `writePly()` function below. Support for both ASCII and binary file formats exists. However, for precise coordinate storage, we advise using the binary .PLY format. Fade always writes coordinates with 64-bit precision.

```void writePly(Fade_2D& dt,vector<Zone2*>& vZones)
{
// Write the whole triangulation to a *.ply file
const char* all_fn("triangulation.ply");
dt.writePly(all_fn,false); // ...use bASCII=false for binary format

// Write only one zone to a *.ply file
const char* zone_fn("zone.ply");
vZones[0]->writePly(zone_fn,false);

cout<<"* writePly()"<<endl;
cout<<"  triangulation: "<<dt.numberOfTriangles()<<" triangles"<<endl;
cout<<"  zone: "<<vZones[0]->getNumberOfTriangles()<<" triangles"<<endl;
cout<<"  Have created "<<all_fn<<", "<<zone_fn<<endl<<endl;
}```

The following code snippet calls `importTrianglesFromPly()` to import triangles from a .PLY file. Fade detects the exact type of the PLY file (32/64-bit, binary/ASCII) automatically. Internally, this method uses the importTriangles_robust() function described above, thereby automatically correcting any errors that may be present in the input data.

```void readPly()
{
const char *all_fn("triangulation.ply");
dt1.importTrianglesFromPly(all_fn);
cout << "* readPly()" << endl;
cout << "  Triangulation: " << dt1.numberOfTriangles() << " triangles, read from "<<all_fn<<endl;

const char *zone_fn("zone.ply");
Zone2 *pZone2 = dt2.importTrianglesFromPly(zone_fn);
cout << "  pZone2: " << pZone2->getNumberOfTriangles() << " triangles, read from "<<zone_fn<<endl;
}```

Note: The .PLY file type is not supported on very old platforms that do not properly support C++11 (up to and including VS2012).

Fade2D’s native file format for triangulations offers the key benefit of supporting zones. The below `saveNative()` function demonstrates writing of:

• A triangulation along with two zones
• The triangles of two zones
• An individual zone
```void saveNative(Fade_2D& dt,vector<Zone2*>& vSaveZones)
{
// Save the triangulation along with the zones
dt.saveTriangulation(all_fn,vSaveZones);

// Alternatively we can save only the two zones
dt.saveZones(zones_fn,vSaveZones);

// Or save an individual zone
vSaveZones[0]->save(zone_fn);
}```

Reading the data written by the above function is demonstrated in `void loadNative()` below. If zones are present, the resulting vector of zones will contain the zones in the same order as they were written. If the given data is non-convex, the underlying Delaunay triangulation includes additional fill triangles to complete their convex hull.

```void loadNative()
{
vector<Zone2*> vZones1;
// Load the triangulation with the zones

// Alternatively, we could only be interested in loading zones. Thereby,
// the triangulation may contain additional triangles if required to
// complete the convex hull of the zones.
vector<Zone2*> vZones2;
cout<<endl;
}```

## Writing Wavefront .OBJ

The Wavefront .OBJ file format enjoys broad support across various applications for 3D geometry, and Fade2D supports writing `Fade_2D` and `Zone2` objects to ASCII .OBJ files.

```void writeObj(Fade_2D& dt,vector<Zone2*>& vZones)
{
const char* all_fn("triangulation.obj");
dt.writeObj(all_fn);

const char* zone_fn("zone.obj");
vZones[0]->writeObj(zone_fn);
}
```

## Writing and Reading Points and Line Segments

The writeElements() function below extracts the vertices and edges from a `Fade_2D` object and writes them to binary files. While there are also ASCII versions of the used commands available, for the best possible accuracy, we recommend using the binary formats.

```void writeElements(Fade_2D& dt)
{
// Fetch the triangles
vector<Triangle2*> vAllT;
dt.getTrianglePointers(vAllT);

// Fetch the vertices
vector<Point2*> vVertices;
dt.getVertexPointers(vVertices);

// Create directed edges
vector<Edge2> vDirectedEdges;
dt.getUndirectedEdges(vDirectedEdges);
vector<Segment2> vSegments;
for(Edge2& e:vDirectedEdges) vSegments.emplace_back(Segment2(*e.getSrc(),*e.getTrg()));

cout<<"* writeElements()"<<endl;
cout<<"  vAllT:\t"<<vAllT.size()<<endl;
cout<<"  vVertices:\t"<<vVertices.size()<<endl;
cout<<"  vSegments:\t"<<vSegments.size()<<endl;

const char* points_fn("points.bin");
const char* segments_fn("segments.bin");

// Write the unique points
writePointsBIN(points_fn,vVertices);

// Write the segments
writeSegmentsBIN(segments_fn,vSegments);

cout<<"  Have created "<<points_fn<<" and "<<segments_fn<<endl;
}```

The readElements() function reads the points and segments from the binary files that we have previously written. Additionally, it’s worth mentioning that there are also ASCII point readers named `readXY()` for 2D and `readXYZ()` for 3D taking whitespace-separated coordinates.

```void readElements()
{
// Reads individual elements that have been written before using writePointsBIN() and writeSegmentsBIN()

const char* points_fn("points.bin");
std::vector<Point2> vPoints;
readPointsBIN(points_fn,vPoints,true); // Use bWithHeader=true for files that have been written with writePointsBIN()

const char* segments_fn("segments.bin");
std::vector<Segment2> vSegments;
}
```

## The FadeExport Struct

The `FadeExport` struct is extensively described in a separate article. Therefore, it is only mentioned here briefly. `FadeExport` is designed primarily for exporting data in memory, facilitating the integration of data from a `Fade_2D` object into your own data structures. For more information about the `FadeExport` struct, please refer to the detailed description available in the Exporting a Triangulation article. However, for self-containment, `FadeExport` is briefly utilized below:

```void demoFadeExport(Fade_2D& dt)
{