Generate Non-Degenerate Point Set in 2D - C
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
In computational geometry, a non-degenerate point set is a collection of points where no three points are collinear — meaning no three points lie on the same straight line. Generating such sets is essential for algorithms that rely on "general position" assumptions, including Delaunay triangulation, convex hull computation, and Voronoi diagrams. This article walks through what non-degeneracy means, how to check for it, and how to generate a valid point set in C++.
What Is a Non-Degenerate Point Set?
A set of 2D points is called non-degenerate (or in general position) when no three points are collinear. Collinearity is the primary degeneracy in 2D. Three points (x1, y1), (x2, y2), and (x3, y3) are collinear if the triangle they form has zero area. You can test this using the cross product of the vectors formed by the three points:
If the absolute value of this expression is zero (or below a tolerance threshold for floating-point arithmetic), the three points are collinear.
Collinearity Check in C++
Here is a function that checks whether three points are collinear using a small epsilon tolerance:
The epsilon value 1e-9 is a practical threshold. For integer coordinates, you can use exact arithmetic and check for area == 0 instead.
Generating a Non-Degenerate Point Set
The strategy is to generate random points and reject any new point that would create a collinear triple with existing points. Here is a complete implementation:
The function generateNonDegenerateSet builds the point set incrementally. For each candidate, it checks all existing pairs to ensure no triple becomes collinear. The worst-case cost is O(n^3) collinearity checks, which is fine for sets up to a few hundred points.
Using Integer Coordinates for Exact Arithmetic
Floating-point comparisons introduce precision concerns. If your application allows it, use integer coordinates to avoid epsilon-based checks:
With integer coordinates in a bounded range, the cross product is computed exactly, eliminating false positives and false negatives from rounding.
Common Pitfalls
- Choosing an epsilon that is too large: A tolerance like
1e-3rejects points that are close to collinear but not truly collinear, resulting in unnecessarily sparse point sets. Use1e-9or smaller for double-precision floats. - Not handling duplicate points: Two identical points are trivially collinear with every other point. Always check for and reject duplicate coordinates before the collinearity test.
- Running out of candidates in a small range: If the coordinate range is too narrow relative to the number of points requested, the rejection rate becomes very high. Ensure the range is large enough to accommodate the desired point count.
- Using
rand()for high-quality randomness: The Crand()function has limited randomness quality and range. For production code, prefer<random>facilities likestd::mt19937withstd::uniform_real_distribution. - Ignoring the cubic time complexity: The brute-force O(n^3) approach works for small sets but becomes impractical beyond a few hundred points. For large sets, consider perturbation-based methods that start with a grid and add small random offsets.
Summary
- A non-degenerate 2D point set has no three collinear points, which is required by many computational geometry algorithms.
- Collinearity is tested by computing the signed area of the triangle formed by three points — a zero area means the points are collinear.
- The generate-and-reject approach builds the set incrementally, checking each candidate against all existing pairs.
- Use integer coordinates when possible to avoid floating-point precision issues in collinearity checks.
- The brute-force algorithm runs in O(n^3) time, which is suitable for small sets but should be optimized with spatial indexing for larger inputs.

