How to resolve the algorithm Sutherland-Hodgman polygon clipping step by step in the D programming language

Published on 12 May 2024 09:40 PM
#D

How to resolve the algorithm Sutherland-Hodgman polygon clipping step by step in the D programming language

Table of Contents

Problem Statement

The   Sutherland-Hodgman clipping algorithm   finds the polygon that is the intersection between an arbitrary polygon (the “subject polygon”) and a convex polygon (the “clip polygon”). It is used in computer graphics (especially 2D graphics) to reduce the complexity of a scene being displayed by eliminating parts of a polygon that do not need to be displayed.

Take the closed polygon defined by the points: and clip it by the rectangle defined by the points: Print the sequence of points that define the resulting clipped polygon.

Display all three polygons on a graphical surface, using a different color for each polygon and filling the resulting polygon. (When displaying you may use either a north-west or a south-west origin, whichever is more convenient for your display mechanism.)

Let's start with the solution:

Step by Step solution about How to resolve the algorithm Sutherland-Hodgman polygon clipping step by step in the D programming language

Source code in the d programming language

import std.stdio, std.array, std.range, std.typecons, std.algorithm;

struct Vec2 { // To be replaced with Phobos code.
    double x, y;

    Vec2 opBinary(string op="-")(in Vec2 other)
    const pure nothrow @safe @nogc {
        return Vec2(this.x - other.x, this.y - other.y);
    }

    typeof(x) cross(in Vec2 other) const pure nothrow @safe @nogc {
        return this.x * other.y - this.y * other.x;
    }
}

immutable(Vec2)[] clip(in Vec2[] subjectPolygon, in Vec2[] clipPolygon)
pure /*nothrow*/ @safe in {
    assert(subjectPolygon.length > 1);
    assert(clipPolygon.length > 1);
    // Probably clipPolygon needs to be convex and probably
    // its vertices need to be listed in a direction.
} out(result) {
    assert(result.length > 1);
} body {
    alias Edge = Tuple!(Vec2,"p", Vec2,"q");

    static enum isInside = (in Vec2 p, in Edge cle)
    pure nothrow @safe @nogc =>
        (cle.q.x - cle.p.x) * (p.y - cle.p.y) >
        (cle.q.y - cle.p.y) * (p.x - cle.p.x);

    static Vec2 intersection(in Edge se, in Edge cle)
    pure nothrow @safe @nogc {
        immutable dc = cle.p - cle.q;
        immutable dp = se.p - se.q;
        immutable n1 = cle.p.cross(cle.q);
        immutable n2 = se.p.cross(se.q);
        immutable n3 = 1.0 / dc.cross(dp);
        return Vec2((n1 * dp.x - n2 * dc.x) * n3,
                    (n1 * dp.y - n2 * dc.y) * n3);
    }

    // How much slower is this compared to lower-level code?
    static enum edges = (in Vec2[] poly) pure nothrow @safe @nogc =>
        // poly[$ - 1 .. $].chain(poly).zip!Edge(poly);
        poly[$ - 1 .. $].chain(poly).zip(poly).map!Edge;

    immutable(Vec2)[] result = subjectPolygon.idup; // Not nothrow.

    foreach (immutable clipEdge; edges(clipPolygon)) {
        immutable inputList = result;
        result.destroy;
        foreach (immutable inEdge; edges(inputList)) {
            if (isInside(inEdge.q, clipEdge)) {
                if (!isInside(inEdge.p, clipEdge))
                    result ~= intersection(inEdge, clipEdge);
                result ~= inEdge.q;
            } else if (isInside(inEdge.p, clipEdge))
                result ~= intersection(inEdge, clipEdge);
        }
    }

    return result;
}

// Code adapted from the C version.
void saveEPSImage(in string fileName, in Vec2[] subjPoly,
                  in Vec2[] clipPoly, in Vec2[] clipped)
in {
    assert(!fileName.empty);
    assert(subjPoly.length > 1);
    assert(clipPoly.length > 1);
    assert(clipped.length > 1);
} body {
    auto eps = File(fileName, "w");

    // The image bounding box is hard-coded, not computed.
    eps.writeln(
"%%!PS-Adobe-3.0
%%%%BoundingBox: 40 40 360 360
/l {lineto} def
/m {moveto} def
/s {setrgbcolor} def
/c {closepath} def
/gs {fill grestore stroke} def
");

    eps.writef("0 setlinewidth %g %g m ", clipPoly[0].tupleof);
    foreach (immutable cl; clipPoly[1 .. $])
        eps.writef("%g %g l ", cl.tupleof);
    eps.writefln("c 0.5 0 0 s gsave 1 0.7 0.7 s gs");

    eps.writef("%g %g m ", subjPoly[0].tupleof);
    foreach (immutable s; subjPoly[1 .. $])
        eps.writef("%g %g l ", s.tupleof);
    eps.writefln("c 0 0.2 0.5 s gsave 0.4 0.7 1 s gs");

    eps.writef("2 setlinewidth [10 8] 0 setdash %g %g m ",
               clipped[0].tupleof);
    foreach (immutable c; clipped[1 .. $])
        eps.writef("%g %g l ", c.tupleof);
    eps.writefln("c 0.5 0 0.5 s gsave 0.7 0.3 0.8 s gs");

    eps.writefln("%%%%EOF");
    eps.close;
    writeln(fileName, " written.");
}

void main() {
    alias V = Vec2;
    immutable subjectPolygon = [V(50, 150), V(200, 50), V(350, 150),
                                V(350, 300), V(250, 300), V(200, 250),
                                V(150, 350), V(100, 250), V(100, 200)];
    immutable clippingPolygon = [V(100, 100), V(300, 100),
                                 V(300, 300), V(100, 300)];
    immutable clipped = subjectPolygon.clip(clippingPolygon);
    writefln("%(%s\n%)", clipped);
    saveEPSImage("sutherland_hodgman_clipping_out.eps",
                 subjectPolygon, clippingPolygon, clipped);
}


  

You may also check:How to resolve the algorithm Call a function step by step in the Raku programming language
You may also check:How to resolve the algorithm Animate a pendulum step by step in the BASIC programming language
You may also check:How to resolve the algorithm Terminal control/Inverse video step by step in the Racket programming language
You may also check:How to resolve the algorithm UTF-8 encode and decode step by step in the Scala programming language
You may also check:How to resolve the algorithm Terminal control/Inverse video step by step in the Julia programming language