1 #include "line.h"
2 
3 #include <algorithm>
4 #include <array>
5 #include <cstdlib>
6 #include <tuple>
7 #include <utility>
8 
9 #include "cata_assert.h"
10 #include "enums.h"
11 #include "math_defines.h"
12 #include "output.h"
13 #include "string_formatter.h"
14 #include "translations.h"
15 #include "units.h"
16 #include "units_fwd.h"
17 
18 bool trigdist;
19 
iso_tangent(double distance,const units::angle & vertex)20 double iso_tangent( double distance, const units::angle &vertex )
21 {
22     // we can use the cosine formula (a² = b² + c² - 2bc⋅cosθ) to calculate the tangent
23     return std::sqrt( 2 * std::pow( distance, 2 ) * ( 1 - cos( vertex ) ) );
24 }
25 
bresenham(const point & p1,const point & p2,int t,const std::function<bool (const point &)> & interact)26 void bresenham( const point &p1, const point &p2, int t,
27                 const std::function<bool( const point & )> &interact )
28 {
29     // The slope components.
30     const point d = p2 - p1;
31     // Signs of slope values.
32     const point s( ( d.x == 0 ) ? 0 : sgn( d.x ), ( d.y == 0 ) ? 0 : sgn( d.y ) );
33     // Absolute values of slopes x2 to avoid rounding errors.
34     const point a = d.abs() * 2;
35 
36     point cur = p1;
37 
38     if( a.x == a.y ) {
39         while( cur.x != p2.x ) {
40             cur.y += s.y;
41             cur.x += s.x;
42             if( !interact( cur ) ) {
43                 break;
44             }
45         }
46     } else if( a.x > a.y ) {
47         while( cur.x != p2.x ) {
48             if( t > 0 ) {
49                 cur.y += s.y;
50                 t -= a.x;
51             }
52             cur.x += s.x;
53             t += a.y;
54             if( !interact( cur ) ) {
55                 break;
56             }
57         }
58     } else {
59         while( cur.y != p2.y ) {
60             if( t > 0 ) {
61                 cur.x += s.x;
62                 t -= a.y;
63             }
64             cur.y += s.y;
65             t += a.x;
66             if( !interact( cur ) ) {
67                 break;
68             }
69         }
70     }
71 }
72 
bresenham(const tripoint & loc1,const tripoint & loc2,int t,int t2,const std::function<bool (const tripoint &)> & interact)73 void bresenham( const tripoint &loc1, const tripoint &loc2, int t, int t2,
74                 const std::function<bool( const tripoint & )> &interact )
75 {
76     // The slope components.
77     const tripoint d( -loc1 + loc2 );
78     // The signs of the slopes.
79     const tripoint s( ( d.x == 0 ? 0 : sgn( d.x ) ), ( d.y == 0 ? 0 : sgn( d.y ) ),
80                       ( d.z == 0 ? 0 : sgn( d.z ) ) );
81     // Absolute values of slope components, x2 to avoid rounding errors.
82     const tripoint a( std::abs( d.x ) * 2, std::abs( d.y ) * 2, std::abs( d.z ) * 2 );
83 
84     tripoint cur( loc1 );
85 
86     if( a.z == 0 ) {
87         if( a.x == a.y ) {
88             while( cur.x != loc2.x ) {
89                 cur.y += s.y;
90                 cur.x += s.x;
91                 if( !interact( cur ) ) {
92                     break;
93                 }
94             }
95         } else if( a.x > a.y ) {
96             while( cur.x != loc2.x ) {
97                 if( t > 0 ) {
98                     cur.y += s.y;
99                     t -= a.x;
100                 }
101                 cur.x += s.x;
102                 t += a.y;
103                 if( !interact( cur ) ) {
104                     break;
105                 }
106             }
107         } else {
108             while( cur.y != loc2.y ) {
109                 if( t > 0 ) {
110                     cur.x += s.x;
111                     t -= a.y;
112                 }
113                 cur.y += s.y;
114                 t += a.x;
115                 if( !interact( cur ) ) {
116                     break;
117                 }
118             }
119         }
120     } else {
121         if( a.x == a.y && a.y == a.z ) {
122             while( cur.x != loc2.x ) {
123                 cur.z += s.z;
124                 cur.y += s.y;
125                 cur.x += s.x;
126                 if( !interact( cur ) ) {
127                     break;
128                 }
129             }
130         } else if( ( a.z > a.x ) && ( a.z > a.y ) ) {
131             while( cur.z != loc2.z ) {
132                 if( t > 0 ) {
133                     cur.x += s.x;
134                     t -= a.z;
135                 }
136                 if( t2 > 0 ) {
137                     cur.y += s.y;
138                     t2 -= a.z;
139                 }
140                 cur.z += s.z;
141                 t += a.x;
142                 t2 += a.y;
143                 if( !interact( cur ) ) {
144                     break;
145                 }
146             }
147         } else if( a.x == a.y ) {
148             while( cur.x != loc2.x ) {
149                 if( t > 0 ) {
150                     cur.z += s.z;
151                     t -= a.x;
152                 }
153                 cur.y += s.y;
154                 cur.x += s.x;
155                 t += a.z;
156                 if( !interact( cur ) ) {
157                     break;
158                 }
159             }
160         } else if( a.x == a.z ) {
161             while( cur.x != loc2.x ) {
162                 if( t > 0 ) {
163                     cur.y += s.y;
164                     t -= a.x;
165                 }
166                 cur.z += s.z;
167                 cur.x += s.x;
168                 t += a.y;
169                 if( !interact( cur ) ) {
170                     break;
171                 }
172             }
173         } else if( a.y == a.z ) {
174             while( cur.y != loc2.y ) {
175                 if( t > 0 ) {
176                     cur.x += s.x;
177                     t -= a.z;
178                 }
179                 cur.y += s.y;
180                 cur.z += s.z;
181                 t += a.x;
182                 if( !interact( cur ) ) {
183                     break;
184                 }
185             }
186         } else if( a.x > a.y ) {
187             while( cur.x != loc2.x ) {
188                 if( t > 0 ) {
189                     cur.y += s.y;
190                     t -= a.x;
191                 }
192                 if( t2 > 0 ) {
193                     cur.z += s.z;
194                     t2 -= a.x;
195                 }
196                 cur.x += s.x;
197                 t += a.y;
198                 t2 += a.z;
199                 if( !interact( cur ) ) {
200                     break;
201                 }
202             }
203         } else { //dy > dx >= dz
204             while( cur.y != loc2.y ) {
205                 if( t > 0 ) {
206                     cur.x += s.x;
207                     t -= a.y;
208                 }
209                 if( t2 > 0 ) {
210                     cur.z += s.z;
211                     t2 -= a.y;
212                 }
213                 cur.y += s.y;
214                 t += a.x;
215                 t2 += a.z;
216                 if( !interact( cur ) ) {
217                     break;
218                 }
219             }
220         }
221     }
222 }
223 
224 //Trying to pull points out of a tripoint vector is messy and
225 //probably slow, so leaving two full functions for now
line_to(const point & p1,const point & p2,int t)226 std::vector<point> line_to( const point &p1, const point &p2, int t )
227 {
228     std::vector<point> line;
229     // Preallocate the number of cells we need instead of allocating them piecewise.
230     const int numCells = square_dist( p1, p2 );
231     if( numCells == 0 ) {
232         line.push_back( p1 );
233     } else {
234         line.reserve( numCells );
235         bresenham( p1, p2, t, [&line]( const point & new_point ) {
236             line.push_back( new_point );
237             return true;
238         } );
239     }
240     return line;
241 }
242 
line_to(const tripoint & loc1,const tripoint & loc2,int t,int t2)243 std::vector <tripoint> line_to( const tripoint &loc1, const tripoint &loc2, int t, int t2 )
244 {
245     std::vector<tripoint> line;
246     // Preallocate the number of cells we need instead of allocating them piecewise.
247     const int numCells = square_dist( loc1, loc2 );
248     if( numCells == 0 ) {
249         line.push_back( loc1 );
250     } else {
251         line.reserve( numCells );
252         bresenham( loc1, loc2, t, t2, [&line]( const tripoint & new_point ) {
253             line.push_back( new_point );
254             return true;
255         } );
256     }
257     return line;
258 }
259 
rl_dist_exact(const tripoint & loc1,const tripoint & loc2)260 float rl_dist_exact( const tripoint &loc1, const tripoint &loc2 )
261 {
262     if( trigdist ) {
263         return trig_dist( loc1, loc2 );
264     }
265     return square_dist( loc1, loc2 );
266 }
267 
manhattan_dist(const point & loc1,const point & loc2)268 int manhattan_dist( const point &loc1, const point &loc2 )
269 {
270     const point d = ( loc1 - loc2 ).abs();
271     return d.x + d.y;
272 }
273 
atan2(const point & p)274 units::angle atan2( const point &p )
275 {
276     return units::atan2( p.y, p.x );
277 }
278 
279 // This more general version of this function gives correct values for larger values.
make_xyz(const tripoint & p)280 unsigned make_xyz( const tripoint &p )
281 {
282     static constexpr double sixteenth_arc = M_PI / 8;
283     int vertical_position = ( ( p.z > 0 ) ? 2u : ( p.z < 0 ) ? 1u : 0u ) * 9u;
284     if( p.xy() == point_zero ) {
285         return vertical_position;
286     }
287     // Get the arctan of the angle and divide by approximately 22.5 deg to get the octant.
288     // the angle is in, then truncate it and map to the right direction.
289     // You can read 'octant' as being "number of 22.5 degree sections away from due south".
290     // FIXME: atan2 normally takes arguments in ( y, x ) order.  This is
291     // passing ( x, y ).
292     int octant = atan2( p.x, p.y ) / sixteenth_arc;
293     switch( octant ) {
294         case 0:
295             return direction::SOUTH + vertical_position;
296         case 1:
297         case 2:
298             return direction::SOUTHEAST + vertical_position;
299         case 3:
300         case 4:
301             return direction::EAST + vertical_position;
302         case 5:
303         case 6:
304             return direction::NORTHEAST + vertical_position;
305         case -1:
306         case -2:
307             return direction::SOUTHWEST + vertical_position;
308         case -3:
309         case -4:
310             return direction::WEST + vertical_position;
311         case -5:
312         case -6:
313             return direction::NORTHWEST + vertical_position;
314         case 7:
315         case 8:
316         case -7:
317         case -8:
318         default:
319             return direction::NORTH + vertical_position;
320     }
321 }
322 
323 // returns the normalized dx, dy, dz for the current line vector.
slope_of(const std::vector<tripoint> & line)324 static std::tuple<double, double, double> slope_of( const std::vector<tripoint> &line )
325 {
326     cata_assert( !line.empty() && line.front() != line.back() );
327     const double len = trig_dist( line.front(), line.back() );
328     double normDx = ( line.back().x - line.front().x ) / len;
329     double normDy = ( line.back().y - line.front().y ) / len;
330     double normDz = ( line.back().z - line.front().z ) / len;
331     // slope of <x, y, z>
332     return std::make_tuple( normDx, normDy, normDz );
333 }
334 
get_normalized_angle(const point & start,const point & end)335 float get_normalized_angle( const point &start, const point &end )
336 {
337     // Taking the abs value of the difference puts the values in the first quadrant.
338     const float absx = std::abs( std::max( start.x, end.x ) - std::min( start.x, end.x ) );
339     const float absy = std::abs( std::max( start.y, end.y ) - std::min( start.y, end.y ) );
340     const float max = std::max( absx, absy );
341     if( max == 0 ) {
342         return 0;
343     }
344     const float min = std::min( absx, absy );
345     return min / max;
346 }
347 
move_along_line(const tripoint & loc,const std::vector<tripoint> & line,const int distance)348 tripoint move_along_line( const tripoint &loc, const std::vector<tripoint> &line,
349                           const int distance )
350 {
351     // May want to optimize this, but it's called fairly infrequently as part of specific attack
352     // routines, erring on the side of readability.
353     tripoint res( loc );
354     const auto slope = slope_of( line );
355     res.x += distance * std::get<0>( slope );
356     res.y += distance * std::get<1>( slope );
357     res.z += distance * std::get<2>( slope );
358     return res;
359 }
360 
continue_line(const std::vector<tripoint> & line,const int distance)361 std::vector<tripoint> continue_line( const std::vector<tripoint> &line, const int distance )
362 {
363     return line_to( line.back(), move_along_line( line.back(), line, distance ) );
364 }
365 
direction_from(const point & p)366 direction direction_from( const point &p ) noexcept
367 {
368     return static_cast<direction>( make_xyz( tripoint( p, 0 ) ) );
369 }
370 
direction_from(const tripoint & p)371 direction direction_from( const tripoint &p ) noexcept
372 {
373     return static_cast<direction>( make_xyz( p ) );
374 }
375 
direction_from(const point & p1,const point & p2)376 direction direction_from( const point &p1, const point &p2 ) noexcept
377 {
378     return direction_from( p2 - p1 );
379 }
380 
direction_from(const tripoint & p,const tripoint & q)381 direction direction_from( const tripoint &p, const tripoint &q )
382 {
383     return direction_from( q - p );
384 }
385 
direction_XY(const direction dir)386 point direction_XY( const direction dir )
387 {
388     switch( dir % 9 ) {
389         case direction::NORTHWEST:
390         case direction::ABOVENORTHWEST:
391         case direction::BELOWNORTHWEST:
392             return point_north_west;
393         case direction::NORTH:
394         case direction::ABOVENORTH:
395         case direction::BELOWNORTH:
396             return point_north;
397         case direction::NORTHEAST:
398         case direction::ABOVENORTHEAST:
399         case direction::BELOWNORTHEAST:
400             return point_north_east;
401         case direction::WEST:
402         case direction::ABOVEWEST:
403         case direction::BELOWWEST:
404             return point_west;
405         case direction::CENTER:
406         case direction::ABOVECENTER:
407         case direction::BELOWCENTER:
408             return point_zero;
409         case direction::EAST:
410         case direction::ABOVEEAST:
411         case direction::BELOWEAST:
412             return point_east;
413         case direction::SOUTHWEST:
414         case direction::ABOVESOUTHWEST:
415         case direction::BELOWSOUTHWEST:
416             return point_south_west;
417         case direction::SOUTH:
418         case direction::ABOVESOUTH:
419         case direction::BELOWSOUTH:
420             return point_south;
421         case direction::SOUTHEAST:
422         case direction::ABOVESOUTHEAST:
423         case direction::BELOWSOUTHEAST:
424             return point_south_east;
425     }
426 
427     return point_zero;
428 }
429 
430 namespace
431 {
direction_name_impl(const direction dir,const bool short_name)432 std::string direction_name_impl( const direction dir, const bool short_name )
433 {
434     enum : int { size = 3 * 3 * 3 };
435     static const auto names = [] {
436         using pair_t = std::pair<std::string, std::string>;
437         std::array < pair_t, size + 1 > result;
438 
439         //~ abbreviated direction names and long direction names
440         result[static_cast<size_t>( direction::NORTH )]          = pair_t {translate_marker( "N    " ), translate_marker( "north" )};
441         result[static_cast<size_t>( direction::NORTHEAST )]      = pair_t {translate_marker( "NE   " ), translate_marker( "northeast" )};
442         result[static_cast<size_t>( direction::EAST )]           = pair_t {translate_marker( "E    " ), translate_marker( "east" )};
443         result[static_cast<size_t>( direction::SOUTHEAST )]      = pair_t {translate_marker( "SE   " ), translate_marker( "southeast" )};
444         result[static_cast<size_t>( direction::SOUTH )]          = pair_t {translate_marker( "S    " ), translate_marker( "south" )};
445         result[static_cast<size_t>( direction::SOUTHWEST )]      = pair_t {translate_marker( "SW   " ), translate_marker( "southwest" )};
446         result[static_cast<size_t>( direction::WEST )]           = pair_t {translate_marker( "W    " ), translate_marker( "west" )};
447         result[static_cast<size_t>( direction::NORTHWEST )]      = pair_t {translate_marker( "NW   " ), translate_marker( "northwest" )};
448         result[static_cast<size_t>( direction::ABOVENORTH )]     = pair_t {translate_marker( "UP_N " ), translate_marker( "north and above" )};
449         result[static_cast<size_t>( direction::ABOVENORTHEAST )] = pair_t {translate_marker( "UP_NE" ), translate_marker( "northeast and above" )};
450         result[static_cast<size_t>( direction::ABOVEEAST )]      = pair_t {translate_marker( "UP_E " ), translate_marker( "east and above" )};
451         result[static_cast<size_t>( direction::ABOVESOUTHEAST )] = pair_t {translate_marker( "UP_SE" ), translate_marker( "southeast and above" )};
452         result[static_cast<size_t>( direction::ABOVESOUTH )]     = pair_t {translate_marker( "UP_S " ), translate_marker( "south and above" )};
453         result[static_cast<size_t>( direction::ABOVESOUTHWEST )] = pair_t {translate_marker( "UP_SW" ), translate_marker( "southwest and above" )};
454         result[static_cast<size_t>( direction::ABOVEWEST )]      = pair_t {translate_marker( "UP_W " ), translate_marker( "west and above" )};
455         result[static_cast<size_t>( direction::ABOVENORTHWEST )] = pair_t {translate_marker( "UP_NW" ), translate_marker( "northwest and above" )};
456         result[static_cast<size_t>( direction::BELOWNORTH )]     = pair_t {translate_marker( "DN_N " ), translate_marker( "north and below" )};
457         result[static_cast<size_t>( direction::BELOWNORTHEAST )] = pair_t {translate_marker( "DN_NE" ), translate_marker( "northeast and below" )};
458         result[static_cast<size_t>( direction::BELOWEAST )]      = pair_t {translate_marker( "DN_E " ), translate_marker( "east and below" )};
459         result[static_cast<size_t>( direction::BELOWSOUTHEAST )] = pair_t {translate_marker( "DN_SE" ), translate_marker( "southeast and below" )};
460         result[static_cast<size_t>( direction::BELOWSOUTH )]     = pair_t {translate_marker( "DN_S " ), translate_marker( "south and below" )};
461         result[static_cast<size_t>( direction::BELOWSOUTHWEST )] = pair_t {translate_marker( "DN_SW" ), translate_marker( "southwest and below" )};
462         result[static_cast<size_t>( direction::BELOWWEST )]      = pair_t {translate_marker( "DN_W " ), translate_marker( "west and below" )};
463         result[static_cast<size_t>( direction::BELOWNORTHWEST )] = pair_t {translate_marker( "DN_NW" ), translate_marker( "northwest and below" )};
464         result[static_cast<size_t>( direction::ABOVECENTER )]    = pair_t {translate_marker( "UP_CE" ), translate_marker( "above" )};
465         result[static_cast<size_t>( direction::CENTER )]         = pair_t {translate_marker( "CE   " ), translate_marker( "center" )};
466         result[static_cast<size_t>( direction::BELOWCENTER )]    = pair_t {translate_marker( "DN_CE" ), translate_marker( "below" )};
467 
468         result[size] = pair_t {"BUG.  (line.cpp:direction_name)", "BUG.  (line.cpp:direction_name)"};
469         return result;
470     }();
471 
472     int i = static_cast<int>( dir );
473     if( i < 0 || i >= size ) {
474         i = size;
475     }
476 
477     return short_name ? _( names[i].first ) : _( names[i].second );
478 }
479 } //namespace
480 
direction_name(const direction dir)481 std::string direction_name( const direction dir )
482 {
483     return direction_name_impl( dir, false );
484 }
485 
direction_name_short(const direction dir)486 std::string direction_name_short( const direction dir )
487 {
488     return direction_name_impl( dir, true );
489 }
490 
direction_suffix(const tripoint & p,const tripoint & q)491 std::string direction_suffix( const tripoint &p, const tripoint &q )
492 {
493     int dist = square_dist( p, q );
494     if( dist <= 0 ) {
495         return std::string();
496     }
497     return string_format( "%d%s", dist, trim( direction_name_short( direction_from( p, q ) ) ) );
498 }
499 
500 // Cardinals are cardinals. Result is cardinal and adjacent sub-cardinals.
501 // Sub-Cardinals are sub-cardinals && abs(x) == abs(y). Result is sub-cardinal and adjacent cardinals.
502 // Sub-sub-cardinals are direction && abs(x) > abs(y) or vice versa.
503 // Result is adjacent cardinal and sub-cardinals, plus the nearest other cardinal.
504 // e.g. if the direction is NNE, also include E.
squares_closer_to(const tripoint & from,const tripoint & to)505 std::vector<tripoint> squares_closer_to( const tripoint &from, const tripoint &to )
506 {
507     std::vector<tripoint> adjacent_closer_squares;
508     adjacent_closer_squares.reserve( 5 );
509     const tripoint d( -from + to );
510     const point a( std::abs( d.x ), std::abs( d.y ) );
511     if( d.z != 0 ) {
512         adjacent_closer_squares.push_back( from + tripoint( sgn( d.x ), sgn( d.y ), sgn( d.z ) ) );
513     }
514     if( a.x > a.y ) {
515         // X dominant.
516         adjacent_closer_squares.push_back( from + point( sgn( d.x ), 0 ) );
517         adjacent_closer_squares.push_back( from + point( sgn( d.x ), 1 ) );
518         adjacent_closer_squares.push_back( from + point( sgn( d.x ), -1 ) );
519         if( d.y != 0 ) {
520             adjacent_closer_squares.push_back( from + point( 0, sgn( d.y ) ) );
521         }
522     } else if( a.x < a.y ) {
523         // Y dominant.
524         adjacent_closer_squares.push_back( from + point( 0, sgn( d.y ) ) );
525         adjacent_closer_squares.push_back( from + point( 1, sgn( d.y ) ) );
526         adjacent_closer_squares.push_back( from + point( -1, sgn( d.y ) ) );
527         if( d.x != 0 ) {
528             adjacent_closer_squares.push_back( from + point( sgn( d.x ), 0 ) );
529         }
530     } else if( d.x != 0 ) {
531         // Pure diagonal.
532         adjacent_closer_squares.push_back( from + point( sgn( d.x ), sgn( d.y ) ) );
533         adjacent_closer_squares.push_back( from + point( sgn( d.x ), 0 ) );
534         adjacent_closer_squares.push_back( from + point( 0, sgn( d.y ) ) );
535     }
536 
537     return adjacent_closer_squares;
538 }
539 
540 // Returns a vector of the adjacent square in the direction of the target,
541 // and the two squares flanking it.
squares_in_direction(const point & p1,const point & p2)542 std::vector<point> squares_in_direction( const point &p1, const point &p2 )
543 {
544     int junk = 0;
545     point center_square = line_to( p1, p2, junk )[0];
546     std::vector<point> adjacent_squares;
547     adjacent_squares.reserve( 3 );
548     adjacent_squares.push_back( center_square );
549     if( p1.x == center_square.x ) {
550         // Horizontally adjacent.
551         adjacent_squares.push_back( point( p1.x + 1, center_square.y ) );
552         adjacent_squares.push_back( point( p1.x - 1, center_square.y ) );
553     } else if( p1.y == center_square.y ) {
554         // Vertically adjacent.
555         adjacent_squares.push_back( point( center_square.x, p1.y + 1 ) );
556         adjacent_squares.push_back( point( center_square.x, p1.y - 1 ) );
557     } else {
558         // Diagonally adjacent.
559         adjacent_squares.push_back( point( p1.x, center_square.y ) );
560         adjacent_squares.push_back( point( center_square.x, p1.y ) );
561     }
562     return adjacent_squares;
563 }
564 
magnitude() const565 float rl_vec2d::magnitude() const
566 {
567     return std::sqrt( x * x + y * y );
568 }
569 
magnitude() const570 float rl_vec3d::magnitude() const
571 {
572     return std::sqrt( x * x + y * y + z * z );
573 }
574 
normalized() const575 rl_vec2d rl_vec2d::normalized() const
576 {
577     rl_vec2d ret;
578     if( is_null() ) { // shouldn't happen?
579         ret.x = ret.y = 1;
580         return ret;
581     }
582     const float m = magnitude();
583     ret.x = x / m;
584     ret.y = y / m;
585     return ret;
586 }
587 
normalized() const588 rl_vec3d rl_vec3d::normalized() const
589 {
590     rl_vec3d ret;
591     if( is_null() ) { // shouldn't happen?
592         ret.x = ret.y = ret.z = 0;
593         return ret;
594     }
595     const float m = magnitude();
596     ret.x = x / m;
597     ret.y = y / m;
598     ret.z = z / m;
599     return ret;
600 }
601 
rotated(float angle) const602 rl_vec2d rl_vec2d::rotated( float angle ) const
603 {
604     return rl_vec2d(
605                x * std::cos( angle ) - y * std::sin( angle ),
606                x * std::sin( angle ) + y * std::cos( angle )
607            );
608 }
609 
rotated(float angle) const610 rl_vec3d rl_vec3d::rotated( float angle ) const
611 {
612     return rl_vec3d(
613                x * std::cos( angle ) - y * std::sin( angle ),
614                x * std::sin( angle ) + y * std::cos( angle )
615            );
616 }
617 
dot_product(const rl_vec2d & v) const618 float rl_vec2d::dot_product( const rl_vec2d &v ) const
619 {
620     return x * v.x + y * v.y;
621 }
622 
dot_product(const rl_vec3d & v) const623 float rl_vec3d::dot_product( const rl_vec3d &v ) const
624 {
625     return x * v.x + y * v.y + y * v.z;
626 }
627 
is_null() const628 bool rl_vec2d::is_null() const
629 {
630     return !( x || y );
631 }
632 
as_point() const633 point rl_vec2d::as_point() const
634 {
635     return point(
636                std::round( x ),
637                std::round( y )
638            );
639 }
640 
is_null() const641 bool rl_vec3d::is_null() const
642 {
643     return !( x || y || z );
644 }
645 
as_point() const646 tripoint rl_vec3d::as_point() const
647 {
648     return tripoint(
649                std::round( x ),
650                std::round( y ),
651                std::round( z )
652            );
653 }
654 
655 // scale.
operator *(const float rhs) const656 rl_vec2d rl_vec2d::operator*( const float rhs ) const
657 {
658     rl_vec2d ret;
659     ret.x = x * rhs;
660     ret.y = y * rhs;
661     return ret;
662 }
663 
operator *(const float rhs) const664 rl_vec3d rl_vec3d::operator*( const float rhs ) const
665 {
666     rl_vec3d ret;
667     ret.x = x * rhs;
668     ret.y = y * rhs;
669     ret.z = z * rhs;
670     return ret;
671 }
672 
673 // subtract
operator -(const rl_vec2d & rhs) const674 rl_vec2d rl_vec2d::operator-( const rl_vec2d &rhs ) const
675 {
676     rl_vec2d ret;
677     ret.x = x - rhs.x;
678     ret.y = y - rhs.y;
679     return ret;
680 }
681 
operator -(const rl_vec3d & rhs) const682 rl_vec3d rl_vec3d::operator-( const rl_vec3d &rhs ) const
683 {
684     rl_vec3d ret;
685     ret.x = x - rhs.x;
686     ret.y = y - rhs.y;
687     ret.z = z - rhs.z;
688     return ret;
689 }
690 
691 // unary negation
operator -() const692 rl_vec2d rl_vec2d::operator-() const
693 {
694     rl_vec2d ret;
695     ret.x = -x;
696     ret.y = -y;
697     return ret;
698 }
699 
operator -() const700 rl_vec3d rl_vec3d::operator-() const
701 {
702     rl_vec3d ret;
703     ret.x = -x;
704     ret.y = -y;
705     ret.z = -z;
706     return ret;
707 }
708 
operator +(const rl_vec2d & rhs) const709 rl_vec2d rl_vec2d::operator+( const rl_vec2d &rhs ) const
710 {
711     rl_vec2d ret;
712     ret.x = x + rhs.x;
713     ret.y = y + rhs.y;
714     return ret;
715 }
716 
operator +(const rl_vec3d & rhs) const717 rl_vec3d rl_vec3d::operator+( const rl_vec3d &rhs ) const
718 {
719     rl_vec3d ret;
720     ret.x = x + rhs.x;
721     ret.y = y + rhs.y;
722     ret.z = z + rhs.z;
723     return ret;
724 }
725 
operator /(const float rhs) const726 rl_vec2d rl_vec2d::operator/( const float rhs ) const
727 {
728     rl_vec2d ret;
729     ret.x = x / rhs;
730     ret.y = y / rhs;
731     return ret;
732 }
733 
operator /(const float rhs) const734 rl_vec3d rl_vec3d::operator/( const float rhs ) const
735 {
736     rl_vec3d ret;
737     ret.x = x / rhs;
738     ret.y = y / rhs;
739     ret.z = z / rhs;
740     return ret;
741 }
742 
calc_ray_end(units::angle angle,const int range,const tripoint & p,tripoint & out)743 void calc_ray_end( units::angle angle, const int range, const tripoint &p, tripoint &out )
744 {
745     // forces input angle to be between 0 and 360, calculated from actual input
746     angle = fmod( angle, 360_degrees );
747     if( angle < 0_degrees ) {
748         angle += 360_degrees;
749     }
750     out.z = p.z;
751     if( trigdist ) {
752         out.x = p.x + range * cos( angle );
753         out.y = p.y + range * sin( angle );
754     } else {
755         int mult = 0;
756         if( angle >= 135_degrees && angle <= 315_degrees ) {
757             mult = -1;
758         } else {
759             mult = 1;
760         }
761 
762         if( angle <= 45_degrees || ( 135_degrees <= angle && angle <= 215_degrees ) ||
763             315_degrees < angle ) {
764             out.x = p.x + range * mult;
765             out.y = p.y + range * tan( angle ) * mult;
766         } else {
767             out.x = p.x + range * 1 / tan( angle ) * mult;
768             out.y = p.y + range * mult;
769         }
770     }
771 }
772 
coord_to_angle(const tripoint & a,const tripoint & b)773 units::angle coord_to_angle( const tripoint &a, const tripoint &b )
774 {
775     units::angle rad = units::atan2( b.y - a.y, b.x - a.x );
776     if( rad < 0_degrees ) {
777         rad += 2_pi_radians;
778     }
779     return rad;
780 }
781