DVUI

fillConvexTriangles

Generates triangles to fill path (must be convex).

Vertexes will have unset uv and color is alpha multiplied opts.color fading to transparent at the edge if fade is > 0.

Parameters

#
path:Path
Path
allocator:std.mem.Allocator
std.mem.Allocator
opts:FillConvexOptions
FillConvexOptions

Source

Implementation

#
pub fn fillConvexTriangles(path: Path, allocator: std.mem.Allocator, opts: FillConvexOptions) std.mem.Allocator.Error!Triangles {
    if (path.points.len < 3) {
        return .empty;
    }

    var vtx_count = path.points.len;
    var idx_count = (path.points.len - 2) * 3;
    if (opts.fade > 0) {
        vtx_count *= 2;
        idx_count += path.points.len * 6;
    }
    if (opts.center) |_| {
        vtx_count += 1;
        idx_count += 6;
    }

    var builder = try Triangles.Builder.init(allocator, vtx_count, idx_count);
    errdefer comptime unreachable; // No errors from this point on

    const col: Color.PMA = .fromColor(opts.color);

    var i: usize = 0;
    while (i < path.points.len) : (i += 1) {
        const ai: u16 = @intCast((i + path.points.len - 1) % path.points.len);
        const bi: u16 = @intCast(i % path.points.len);
        const ci: u16 = @intCast((i + 1) % path.points.len);
        const aa = path.points[ai];
        const bb = path.points[bi];
        const cc = path.points[ci];

        const diffab = aa.diff(bb).normalize();
        const diffbc = bb.diff(cc).normalize();
        // average of normals on each side
        var norm: Point.Physical = .{ .x = (diffab.y + diffbc.y) / 2, .y = (-diffab.x - diffbc.x) / 2 };

        // inner vertex
        const inside_len = @min(0.5, opts.fade / 2);
        builder.appendVertex(.{
            .pos = .{
                .x = bb.x - norm.x * inside_len,
                .y = bb.y - norm.y * inside_len,
            },
            .col = col,
        });

        const idx_ai = if (opts.fade > 0) ai * 2 else ai;
        const idx_bi = if (opts.fade > 0) bi * 2 else bi;

        // indexes for fill
        // triangles must be counter-clockwise (y going down) to avoid backface culling
        if (opts.center) |_| {
            builder.appendTriangles(&.{ @intCast(vtx_count - 1), idx_ai, idx_bi });
        } else if (i > 1) {
            builder.appendTriangles(&.{ 0, idx_ai, idx_bi });
        }

        if (opts.fade > 0) {
            // scale averaged normal by angle between which happens to be the same as
            // dividing by the length^2
            const d2 = norm.x * norm.x + norm.y * norm.y;
            if (d2 > 0.000001) {
                norm = norm.scale(1.0 / d2, Point.Physical);
            }

            // limit distance our vertexes can be from the point to 2 so
            // very small angles don't produce huge geometries
            const l = norm.length();
            if (l > 2.0) {
                norm = norm.scale(2.0 / l, Point.Physical);
            }

            // outer vertex
            const outside_len = if (opts.fade <= 1) opts.fade / 2 else opts.fade - 0.5;
            builder.appendVertex(.{
                .pos = .{
                    .x = bb.x + norm.x * outside_len,
                    .y = bb.y + norm.y * outside_len,
                },
                .col = .transparent,
            });

            // indexes for aa fade from inner to outer
            // triangles must be counter-clockwise (y going down) to avoid backface culling
            builder.appendTriangles(&.{
                idx_ai,     idx_ai + 1, idx_bi,
                idx_ai + 1, idx_bi + 1, idx_bi,
            });
        }
    }

    if (opts.center) |center| {
        builder.appendVertex(.{
            .pos = center,
            .col = col,
        });
    }

    return builder.build();
}