fa14d1412fc4a5d03d8bc1366edb2b8351dbd180
[sixth-3d.git] /
1 /*
2  * Sixth 3D engine. Author: Svjatoslav Agejenko.
3  * This project is released under Creative Commons Zero (CC0) license.
4  */
5 package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.solidpolygon;
6
7 import eu.svjatoslav.sixth.e3d.geometry.Point2D;
8
9 /**
10  * Interpolates the x coordinate along a 2D line edge for scanline-based polygon rasterization.
11  *
12  * <p>{@code LineInterpolator} represents one edge of a polygon in screen space, defined by
13  * two {@link Point2D} endpoints. Given a scanline y coordinate, it computes the corresponding
14  * x coordinate via linear interpolation. This is a core building block for the solid polygon
15  * rasterizer, which fills triangles by sweeping horizontal scanlines and using two
16  * {@code LineInterpolator} instances to find the left and right x boundaries at each y level.</p>
17  *
18  * <p>Instances are {@link Comparable}, sorted by absolute height (tallest first) and then
19  * by width. This ordering is used during rasterization to select the primary (longest) edge
20  * of the triangle for the outer scanline loop.</p>
21  *
22  * @see eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.solidpolygon.SolidPolygon
23  * @see Point2D
24  */
25 public class LineInterpolator implements Comparable<LineInterpolator> {
26
27     /**
28      * The first endpoint of this edge.
29      */
30     Point2D p1;
31
32     /**
33      * The second endpoint of this edge.
34      */
35     Point2D p2;
36
37     /**
38      * The vertical span (p2.y - p1.y), which may be negative.
39      */
40     private int height;
41
42     /**
43      * The horizontal span (p2.x - p1.x), which may be negative.
44      */
45     private int width;
46
47     /**
48      * The absolute value of the vertical span, used for sorting.
49      */
50     private int absoluteHeight;
51
52     /**
53      * Creates a new line interpolator with uninitialized endpoints.
54      */
55     public LineInterpolator() {
56     }
57
58     @Override
59     public boolean equals(final Object o) {
60         if (o == null) return false;
61
62         return o instanceof LineInterpolator && compareTo((LineInterpolator) o) == 0;
63     }
64
65     @Override
66     public int compareTo(final LineInterpolator o) {
67         if (absoluteHeight < o.absoluteHeight)
68             return 1;
69         if (absoluteHeight > o.absoluteHeight)
70             return -1;
71
72         return Integer.compare(o.width, width);
73
74     }
75
76     @Override
77     public int hashCode() {
78         int result = width;
79         result = 31 * result + absoluteHeight;
80         return result;
81     }
82
83     /**
84      * Tests whether the given y coordinate falls within the vertical span of this edge.
85      *
86      * @param y the scanline y coordinate to test
87      * @return {@code true} if {@code y} is between the y coordinates of the two endpoints (inclusive)
88      */
89     public boolean containsY(final int y) {
90
91         if (p1.y <= p2.y) {
92             if (y >= p1.y)
93                 return y <= p2.y;
94         } else if (y >= p2.y)
95             return y <= p1.y;
96
97         return false;
98     }
99
100     /**
101      * Computes the interpolated x coordinate for the given scanline y value.
102      *
103      * <p>If the edge is horizontal (height is zero), returns the average of the
104      * two endpoint x coordinates.</p>
105      *
106      * @param y the scanline y coordinate
107      * @return the interpolated x coordinate on this edge at the given y
108      */
109     public int getX(final int y) {
110
111         if (height == 0)
112             return (int) (p2.x + p1.x) / 2;
113
114         return (int) (p1.x + ((width * (y - p1.y)) / height));
115     }
116
117     /**
118      * Sets the two endpoints of this edge and precomputes the width, height, and absolute height.
119      *
120      * @param p1 the first endpoint
121      * @param p2 the second endpoint
122      */
123     public void setPoints(final Point2D p1, final Point2D p2) {
124         this.p1 = p1;
125         this.p2 = p2;
126         height = (int) (p2.y - p1.y);
127         width = (int) (p2.x - p1.x);
128
129         absoluteHeight = Math.abs(height);
130     }
131
132 }