20fb6f46f7b01c5275418561fe17a1eff1e20860
[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     @Override
53     public boolean equals(final Object o) {
54         if (o == null) return false;
55
56         return o instanceof LineInterpolator && compareTo((LineInterpolator) o) == 0;
57     }
58
59     @Override
60     public int compareTo(final LineInterpolator o) {
61         if (absoluteHeight < o.absoluteHeight)
62             return 1;
63         if (absoluteHeight > o.absoluteHeight)
64             return -1;
65
66         return Integer.compare(o.width, width);
67
68     }
69
70     @Override
71     public int hashCode() {
72         int result = width;
73         result = 31 * result + absoluteHeight;
74         return result;
75     }
76
77     /**
78      * Tests whether the given y coordinate falls within the vertical span of this edge.
79      *
80      * @param y the scanline y coordinate to test
81      * @return {@code true} if {@code y} is between the y coordinates of the two endpoints (inclusive)
82      */
83     public boolean containsY(final int y) {
84
85         if (p1.y <= p2.y) {
86             if (y >= p1.y)
87                 return y <= p2.y;
88         } else if (y >= p2.y)
89             return y <= p1.y;
90
91         return false;
92     }
93
94     /**
95      * Computes the interpolated x coordinate for the given scanline y value.
96      *
97      * <p>If the edge is horizontal (height is zero), returns the average of the
98      * two endpoint x coordinates.</p>
99      *
100      * @param y the scanline y coordinate
101      * @return the interpolated x coordinate on this edge at the given y
102      */
103     public int getX(final int y) {
104
105         if (height == 0)
106             return (int) (p2.x + p1.x) / 2;
107
108         return (int) (p1.x + ((width * (y - p1.y)) / height));
109     }
110
111     /**
112      * Sets the two endpoints of this edge and precomputes the width, height, and absolute height.
113      *
114      * @param p1 the first endpoint
115      * @param p2 the second endpoint
116      */
117     public void setPoints(final Point2D p1, final Point2D p2) {
118         this.p1 = p1;
119         this.p2 = p2;
120         height = (int) (p2.y - p1.y);
121         width = (int) (p2.x - p1.x);
122
123         absoluteHeight = Math.abs(height);
124     }
125
126 }