1+ import type { SegProjection } from "./projection" ;
2+
3+ export function createFloorPolygon (
4+ projection : SegProjection ,
5+ screenHeight : number ,
6+ ) : Vertex [ ] {
7+ const { start, end } = projection ;
8+
9+ return [
10+ { x : start . screenX , y : start . bottomY } ,
11+ { x : end . screenX , y : end . bottomY } ,
12+ { x : end . screenX , y : screenHeight } ,
13+ { x : start . screenX , y : screenHeight } ,
14+ ] ;
15+ }
16+
17+ export function createCeilPolygon (
18+ projection : SegProjection ,
19+ ) : Vertex [ ] {
20+ const { start, end } = projection ;
21+
22+ return [
23+ { x : start . screenX , y : 0 } ,
24+ { x : end . screenX , y : 0 } ,
25+ { x : end . screenX , y : end . topY } ,
26+ { x : start . screenX , y : start . topY } ,
27+ ] ;
28+ }
29+
30+ export function createWallPolygon ( projection : SegProjection ) {
31+ const { start, end } = projection ;
32+
33+ return [
34+ { x : start . screenX , y : start . bottomY } ,
35+ { x : end . screenX , y : end . bottomY } ,
36+ { x : end . screenX , y : end . topY } ,
37+ { x : start . screenX , y : start . topY } ,
38+ ] ;
39+ }
40+
41+ // Sutherland-Hodgman algorithm, URL: https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm
42+ // Python implementation, URL https://github.com/mhdadk/sutherland-hodgman
43+
44+ export function intersectPolygons ( subjectPolygon : Vertex [ ] , clipPolygon : Vertex [ ] ) : Vertex [ ] {
45+ let outputList = subjectPolygon ;
46+
47+ let cp1 = clipPolygon [ clipPolygon . length - 1 ] ;
48+
49+ for ( const cp2 of clipPolygon ) {
50+ const inputList = outputList ;
51+ outputList = [ ] ;
52+
53+ if ( inputList . length === 0 ) break ;
54+
55+ let s = inputList [ inputList . length - 1 ] ;
56+
57+ for ( const e of inputList ) {
58+ const insideE = isInside ( cp1 , cp2 , e ) ;
59+ const insideS = isInside ( cp1 , cp2 , s ) ;
60+
61+ if ( insideE ) {
62+ if ( ! insideS ) {
63+ outputList . push ( getIntersection ( cp1 , cp2 , s , e ) ) ;
64+ }
65+ outputList . push ( e ) ;
66+ } else if ( insideS ) {
67+ outputList . push ( getIntersection ( cp1 , cp2 , s , e ) ) ;
68+ }
69+ s = e ;
70+ }
71+ cp1 = cp2 ;
72+ }
73+
74+ return ensureCounterClockwise ( outputList )
75+ }
76+
77+ function isInside ( cp1 : Vertex , cp2 : Vertex , p : Vertex ) : boolean {
78+ const cross = ( cp2 . x - cp1 . x ) * ( p . y - cp1 . y ) - ( cp2 . y - cp1 . y ) * ( p . x - cp1 . x ) ;
79+ return cross >= - 1e-10 ;
80+ }
81+
82+ function getIntersection ( cp1 : Vertex , cp2 : Vertex , s : Vertex , e : Vertex ) : Vertex {
83+ const A1 = cp2 . y - cp1 . y ;
84+ const B1 = cp1 . x - cp2 . x ;
85+ const C1 = A1 * cp1 . x + B1 * cp1 . y ;
86+
87+ const A2 = e . y - s . y ;
88+ const B2 = s . x - e . x ;
89+ const C2 = A2 * s . x + B2 * s . y ;
90+
91+ const det = A1 * B2 - A2 * B1 ;
92+
93+ if ( Math . abs ( det ) < 1e-10 ) {
94+ return {
95+ x : ( s . x + e . x ) / 2 ,
96+ y : ( s . y + e . y ) / 2
97+ } ;
98+ }
99+
100+ const x = ( B2 * C1 - B1 * C2 ) / det ;
101+ const y = ( A1 * C2 - A2 * C1 ) / det ;
102+
103+ return { x, y } ;
104+ }
105+
106+
107+ function isCounterClockwise ( polygon : Vertex [ ] ) : boolean {
108+ let sum = 0 ;
109+ for ( let i = 0 ; i < polygon . length ; i ++ ) {
110+ const p1 = polygon [ i ] ;
111+ const p2 = polygon [ ( i + 1 ) % polygon . length ] ;
112+ sum += ( p2 . x - p1 . x ) * ( p2 . y + p1 . y ) ;
113+ }
114+ return sum < 0 ;
115+ }
116+
117+ function ensureCounterClockwise ( polygon : Vertex [ ] ) : Vertex [ ] {
118+ if ( ! isCounterClockwise ( polygon ) ) {
119+ return [ ...polygon ] . reverse ( ) ;
120+ }
121+ return [ ...polygon ] ;
122+ }
123+
124+ export function sortPointsClockwise ( points : Vertex [ ] ) : Vertex [ ] {
125+ if ( points . length < 3 ) {
126+ return points ;
127+ }
128+
129+ const center = points . reduce (
130+ ( acc , p ) => ( { x : acc . x + p . x , y : acc . y + p . y } ) ,
131+ { x : 0 , y : 0 }
132+ ) ;
133+
134+ center . x /= points . length ;
135+ center . y /= points . length ;
136+
137+ return points . sort ( ( a , b ) => {
138+ const angleA = Math . atan2 ( a . y - center . y , a . x - center . x ) ;
139+ const angleB = Math . atan2 ( b . y - center . y , b . x - center . x ) ;
140+ return angleA - angleB ; // По часовой стрелке
141+ } ) ;
142+ }
0 commit comments