• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Beakerboy / Threejs-Geometries / 16400980926

20 Jul 2025 02:35PM UTC coverage: 83.706% (-3.8%) from 87.5%
16400980926

Pull #20

github

web-flow
Update RampGeometry.tests.js
Pull Request #20: Update RampGeometry.js

81 of 87 branches covered (93.1%)

Branch coverage included in aggregate %.

83 of 111 new or added lines in 1 file covered. (74.77%)

9 existing lines in 1 file now uncovered.

705 of 852 relevant lines covered (82.75%)

608.23 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.27
/src/RampGeometry.js
1
import {
1✔
2
        BufferGeometry,
1✔
3
        Vector2,
1✔
4
        Shape,
1✔
5
        ShapeUtils,
1✔
6
        BufferAttribute,
1✔
7
} from 'three';
1✔
8

1✔
9
/**
1✔
10
 * Modify ExtrudeGeometry such that z varies with x and y
1✔
11
 */
1✔
12
class RampGeometry extends BufferGeometry {
1✔
13

1✔
14
        constructor( shape = new Shape( [ new Vector2( 0.5, 0.5 ), new Vector2( - 0.5, 0.5 ), new Vector2( - 0.5, - 0.5 ), new Vector2( 0.5, - 0.5 ) ] ), options = {} ) {
1✔
15

35✔
16
                super();
35✔
17
                this.type = 'RampGeometry';
35✔
18

35✔
19
                this.parameters = {
35✔
20

35✔
21
                        shape: shape,
35✔
22
                        options: options,
35✔
23

35✔
24
                };
35✔
25

35✔
26
                // The max depth of the geometry
35✔
27
                var depth = options.depth;
35✔
28

35✔
29
                // If depth is not specified, the angle of the ramp.
35✔
30
                const pitch = options.pitch;
35✔
31

35✔
32
                // The direction that the downward slope faces,
35✔
33
                const angle = options.angle;
35✔
34

35✔
35
                this.points = RampGeometry.cleanInputs( shape );
35✔
36
                // Get the cleaned outer shape and holes.
35✔
37
                var shapePoints = this.points.shape;
35✔
38
                var shapeHoles = this.points.holes;
35✔
39

35✔
40
                const rampDepths = [];
35✔
41

35✔
42
                // Calculate intermediate depths.
35✔
43
                // The highest and lowest points will be along the outside
35✔
44
                for ( const point of shapePoints ) {
35✔
45

480✔
46
                        const depth = point.x * Math.sin( angle ) - point.y * Math.cos( angle );
480✔
47
                        rampDepths.push( depth );
480✔
48

480✔
49
                }
480✔
50

35✔
51
                const maxDepth = Math.max( ...rampDepths );
35✔
52
                const minDepth = Math.min( ...rampDepths );
35✔
53

35✔
54
                // Calculate the scaling factor to get the correct height.
35✔
55
                if ( ! depth ) {
35✔
56

21✔
57
                        depth = ( maxDepth - minDepth ) * Math.tan( pitch );
21✔
58

21✔
59
                }
21✔
60

35✔
61
                const scale = depth / ( maxDepth - minDepth );
35✔
62

35✔
63
                this.wallFaces = 0;
35✔
64
                this.ramps = rampDepths;
35✔
65
                const positions = [];
35✔
66
                for ( let i = 0; i < shapePoints.length; i ++ ) {
35✔
67

480✔
68
                        const pointDepth = ( rampDepths[ i ] - minDepth ) * scale;
480✔
69
                        if ( pointDepth > 0 ) {
480✔
70

35✔
71
                                const prevPoint = shapePoints[ ( shapePoints.length + i - 1 ) % shapePoints.length ];
35✔
72
                                const prevDepth = ( rampDepths[ ( shapePoints.length + i - 1 ) % shapePoints.length ] - minDepth ) * scale;
35✔
73
                                const point = shapePoints[ i ];
35✔
74
                                const nextPoint = shapePoints[ ( i + 1 ) % shapePoints.length ];
35✔
75

35✔
76
                                positions.push( prevPoint.x, prevPoint.y, prevDepth );
35✔
77
                                positions.push( point.x, point.y, pointDepth );
35✔
78
                                positions.push( point.x, point.y, 0 );
35✔
79
                                positions.push( nextPoint.x, nextPoint.y, 0 );
35✔
80
                                positions.push( point.x, point.y, 0 );
35✔
81
                                positions.push( point.x, point.y, pointDepth );
35✔
82
                                this.wallFaces += 2;
35✔
83

35✔
84
                        }
35✔
85

480✔
86
                }
480✔
87

35✔
88
                // Add the sides of any holes
35✔
89
                for ( const hole of shapeHoles ) {
35!
UNCOV
90

×
NEW
91
                        for ( let i = 0; i < hole.length; i ++ ) {
×
UNCOV
92

×
NEW
93
                                const point = hole[ i ];
×
NEW
94
                                const pointDepth = ( point.x * Math.sin( angle ) - point.y * Math.cos( angle ) - minDepth ) * scale;
×
NEW
95
                                if ( pointDepth > 0 ) {
×
UNCOV
96

×
NEW
97
                                        const prevPoint = hole[ ( hole.length + i - 1 ) % hole.length ];
×
NEW
98
                                        const nextPoint = hole[ i + 1 ];
×
NEW
99
                                        const prevDepth = ( prevPoint.x * Math.sin( angle ) - prevPoint.y * Math.cos( angle ) - minDepth ) * scale;
×
UNCOV
100

×
NEW
101
                                        positions.push( prevPoint.x, prevPoint.y, prevDepth );
×
NEW
102
                                        positions.push( point.x, point.y, pointDepth );
×
NEW
103
                                        positions.push( point.x, point.y, 0 );
×
NEW
104
                                        positions.push( nextPoint.x, nextPoint.y, 0 );
×
NEW
105
                                        positions.push( point.x, point.y, 0 );
×
NEW
106
                                        positions.push( point.x, point.y, pointDepth );
×
UNCOV
107

×
NEW
108
                                }
×
UNCOV
109

×
UNCOV
110
                        }
×
UNCOV
111

×
UNCOV
112
                }
×
113

35✔
114
                // Add top of roof
35✔
115
                const faces = ShapeUtils.triangulateShape( shapePoints, shapeHoles );
35✔
116
                const vertices = shapePoints.concat( ...shapeHoles );
35✔
117
                for ( const face of faces ) {
35✔
118

410✔
119
                        for ( const pointIndex of face ) {
410✔
120

1,230✔
121
                                const point = vertices[ pointIndex ];
1,230✔
122
                                const x = point.x;
1,230✔
123
                                const y = point.y;
1,230✔
124
                                const z = ( x * Math.sin( angle ) - y * Math.cos( angle ) - minDepth ) * scale;
1,230✔
125
                                positions.push( x, y, z );
1,230✔
126

1,230✔
127
                        }
1,230✔
128

410✔
129
                }
410✔
130

35✔
131
                // Add floor.
35✔
132
                // Reverse face directions to reverse normals.
35✔
133
                for ( const face of faces ) {
35✔
134

410✔
135
                        for ( let j = 2; j > - 1; j -- ) {
410✔
136

1,230✔
137
                                const point = vertices[ face[ j ] ];
1,230✔
138
                                positions.push( point.x, point.y, 0 );
1,230✔
139

1,230✔
140
                        }
1,230✔
141

410✔
142
                }
410✔
143

35✔
144
                this.setAttribute( 'position', new BufferAttribute( new Float32Array( positions ), 3 ) );
35✔
145
                // this.computeVertexNormals();
35✔
146

35✔
147
        }
35✔
148

1✔
149
        /**
1✔
150
         * Ensure start end duplicates are removed fron shape and holes, and that the shares are oriented correctly.
1✔
151
         * modifies this.parameters.shape.
1✔
152
         * Since this modifies the parameters, the return is unnecessary.
1✔
153
         * @returns {Vector2[], Vector2[][]}
1✔
154
         */
1✔
155
        static cleanInputs( shape ) {
1✔
156

35✔
157
                // Get the outer shape and holes.
35✔
158
                const points = shape.extractPoints().shape;
35✔
159

35✔
160
                if ( points[ 0 ].equals( points[ points.length - 1 ] ) ) {
35✔
161

5✔
162
                        points.pop();
5✔
163

5✔
164
                }
5✔
165

35✔
166
                var holes = shape.extractPoints().holes;
35✔
167

35✔
168
                // Ensuse all paths are in the correct direction for the normals
35✔
169
                const reverse = ! ShapeUtils.isClockWise( points );
35✔
170
                if ( reverse ) {
35✔
171

30✔
172
                        points.reverse();
30✔
173

30✔
174
                }
30✔
175

35✔
176
                // Check that any holes are correct direction.
35✔
177
                for ( const hole of holes ) {
35!
NEW
178

×
NEW
179
                        if ( hole[ 0 ].equals( hole[ hole.length - 1 ] ) ) {
×
NEW
180

×
NEW
181
                                hole.pop();
×
NEW
182

×
NEW
183
                        }
×
NEW
184

×
NEW
185
                        if ( ShapeUtils.isClockWise( hole ) ) {
×
NEW
186

×
NEW
187
                                hole.reverse();
×
NEW
188

×
NEW
189
                        }
×
NEW
190

×
NEW
191
                }
×
192

35✔
193
                return { shape: points, holes: holes };
35✔
194

35✔
195
        }
35✔
196

1✔
197
        static fromJSON( data, shape ) {
1✔
198

8✔
199
                return new RampGeometry( shape, data.options );
8✔
200

8✔
201
        }
8✔
202

1✔
203
}
1✔
204
export { RampGeometry };
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc