1 #include "tileray.h"
2 
3 #include <cmath>
4 #include <cstdlib>
5 #include <string>
6 
7 #include "line.h"
8 #include "units.h"
9 #include "units_utility.h"
10 
11 static const int sx[4] = { 1, -1, -1, 1 };
12 static const int sy[4] = { 1, 1, -1, -1 };
13 
14 tileray::tileray() = default;
15 
tileray(const point & ad)16 tileray::tileray( const point &ad )
17 {
18     init( ad );
19 }
20 
tileray(units::angle adir)21 tileray::tileray( units::angle adir ): direction( adir )
22 {
23     init( adir );
24 }
25 
init(const point & ad)26 void tileray::init( const point &ad )
27 {
28     delta = ad;
29     abs_d = delta.abs();
30     if( delta == point_zero ) {
31         direction = 0_degrees;
32     } else {
33         direction = atan2( delta );
34         if( direction < 0_degrees ) {
35             direction += 360_degrees;
36         }
37     }
38     last_d = point_zero;
39     steps = 0;
40     infinite = false;
41 }
42 
init(const units::angle & adir)43 void tileray::init( const units::angle &adir )
44 {
45     leftover = 0;
46     // Clamp adir to the range [0, 360)
47     direction = normalize( adir );
48     last_d = point_zero;
49     rl_vec2d delta_f( units::cos( direction ), units::sin( direction ) );
50     delta = ( delta_f * 100 ).as_point();
51     abs_d = delta.abs();
52     steps = 0;
53     infinite = true;
54 }
55 
clear_advance()56 void tileray::clear_advance()
57 {
58     leftover = 0;
59     last_d = point_zero;
60     steps = 0;
61 }
62 
dx() const63 int tileray::dx() const
64 {
65     return last_d.x;
66 }
67 
dy() const68 int tileray::dy() const
69 {
70     return last_d.y;
71 }
72 
dir() const73 units::angle tileray::dir() const
74 {
75     return direction;
76 }
77 
quadrant() const78 int tileray::quadrant() const
79 {
80     return static_cast<int>( std::floor( direction / 90_degrees ) ) % 4;
81 }
82 
dir4() const83 int tileray::dir4() const
84 {
85     if( direction >= 45_degrees && direction <= 135_degrees ) {
86         return 1;
87     } else if( direction > 135_degrees && direction < 225_degrees ) {
88         return 2;
89     } else if( direction >= 225_degrees && direction <= 315_degrees ) {
90         return 3;
91     } else {
92         return 0;
93     }
94 }
95 
dir8() const96 int tileray::dir8() const
97 {
98     int oct = 0;
99     units::angle dir = direction;
100     if( dir < 23_degrees || dir > 337_degrees ) {
101         return 0;
102     }
103     while( dir > 22_degrees ) {
104         dir -= 45_degrees;
105         oct += 1;
106     }
107     return oct;
108 }
109 
110 // This function assumes a vehicle is being drawn.
111 // It assumes horizontal lines are never skewed, vertical lines often skewed.
dir_symbol(int sym) const112 int tileray::dir_symbol( int sym ) const
113 {
114     switch( sym ) {
115         // output.cpp special_symbol() converts yubn to corners, hj to lines, c to cross
116         case 'j':
117             // vertical line
118             return "h\\j/h\\j/"[dir8()];
119         case 'h':
120             // horizontal line
121             return "jhjh"[dir4()];
122         case 'y':
123             // top left corner
124             return "unby"[dir4()];
125         case 'u':
126             // top right corner
127             return "nbyu"[dir4()];
128         case 'n':
129             // bottom right corner
130             return "byun"[dir4()];
131         case 'b':
132             // bottom left corner
133             return "yunb"[dir4()];
134         case '^':
135             return ">v<^"[dir4()];
136         case '>':
137             return "v<^>"[dir4()];
138         case 'v':
139             return "<^>v"[dir4()];
140         case '<':
141             return "^>v<"[dir4()];
142         case 'c':
143             // +
144             return "cXcXcXcX"[dir8()];
145         case 'X':
146             return "XcXcXcXc"[dir8()];
147 
148         case '[':
149             // [ not rotated to ] because they might represent different items
150             return "-\\[/-\\[/"[dir8()];
151         case ']':
152             return "-\\]/-\\]/"[dir8()];
153         case '|':
154             return "-\\|/-\\|/"[dir8()];
155         case '-':
156             return "|/-\\|/-\\"[dir8()];
157         case '=':
158             return "H=H="[dir4()];
159         case 'H':
160             return "=H=H"[dir4()];
161         case '\\':
162             return "/-\\|/-\\|"[dir8()];
163         case '/':
164             return "\\|/-\\|/-"[dir8()];
165         default:
166             ;
167     }
168     return sym;
169 }
170 
to_string_azimuth_from_north() const171 std::string tileray::to_string_azimuth_from_north() const
172 {
173     return std::to_string( std::lround( to_degrees( dir() + 90_degrees ) ) % 360 ) + "°";
174 }
175 
ortho_dx(int od) const176 int tileray::ortho_dx( int od ) const
177 {
178     od *= -sy[quadrant()];
179     return mostly_vertical() ? od : 0;
180 }
181 
ortho_dy(int od) const182 int tileray::ortho_dy( int od ) const
183 {
184     od *= sx[quadrant()];
185     return mostly_vertical() ? 0 : od;
186 }
187 
mostly_vertical() const188 bool tileray::mostly_vertical() const
189 {
190     return abs_d.x <= abs_d.y;
191 }
192 
advance(int num)193 void tileray::advance( int num )
194 {
195     last_d = point_zero;
196     if( num == 0 ) {
197         return;
198     }
199     int anum = std::abs( num );
200     steps = anum;
201     const bool vertical = mostly_vertical();
202     if( abs_d.x && abs_d.y ) {
203         for( int i = 0; i < anum; i++ ) {
204             if( vertical ) {
205                 // mostly vertical line
206                 leftover += abs_d.x;
207                 if( leftover >= abs_d.y ) {
208                     last_d.x++;
209                     leftover -= abs_d.y;
210                 }
211             } else {
212                 // mostly horizontal line
213                 leftover += abs_d.y;
214                 if( leftover >= abs_d.x ) {
215                     last_d.y++;
216                     leftover -= abs_d.x;
217                 }
218             }
219         }
220     }
221     if( vertical ) {
222         last_d.y = anum;
223     } else {
224         last_d.x = anum;
225     }
226 
227     // offset calculated for 0-90 deg quadrant, we need to adjust if direction is other
228     int quadr = quadrant();
229     last_d.x *= sx[quadr];
230     last_d.y *= sy[quadr];
231     if( num < 0 ) {
232         last_d = -last_d;
233     }
234 }
235 
end()236 bool tileray::end()
237 {
238     if( infinite ) {
239         return true;
240     }
241     return mostly_vertical() ? steps >= abs_d.y - 1 : steps >= abs_d.x - 1;
242 }
243