1 //	Vamos Automotive Simulator
2 //  Copyright (C) 2001--2003 Sam Varner
3 //
4 //  This program is free software; you can redistribute it and/or modify
5 //  it under the terms of the GNU General Public License as published by
6 //  the Free Software Foundation; either version 2 of the License, or
7 //  (at your option) any later version.
8 //
9 //  This program is distributed in the hope that it will be useful,
10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //  GNU General Public License for more details.
13 //
14 //  You should have received a copy of the GNU General Public License
15 //  along with this program; if not, write to the Free Software
16 //  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17 
18 #include "../geometry/Material.h"
19 #include "../geometry/Numeric.h"
20 #include "../media/Texture_Image.h"
21 #include "Road_Segment.h"
22 
23 #include <cmath>
24 #include <cassert>
25 #include <iostream>
26 
27 using namespace Vamos_Geometry;
28 using namespace Vamos_Track;
29 
30 //=============================================================================
Kerb(const std::vector<Two_Vector> & profile,double start,double start_transition_length,double start_transition_width,double end,double end_transition_length,double end_transition_width,bool full_length)31 Kerb::Kerb (const std::vector <Two_Vector>& profile,
32 			double start,
33 			double start_transition_length,
34 			double start_transition_width,
35 			double end,
36 			double end_transition_length,
37 			double end_transition_width,
38             bool full_length)
39   : m_points (profile),
40     m_profile (profile),
41     m_start (start),
42     m_start_transition_length (start_transition_length),
43     m_start_transition_width (start_transition_width),
44     m_end (end),
45     m_end_transition_length (end_transition_length),
46     m_end_transition_width (end_transition_width),
47     m_full_length (full_length)
48 {
49 }
50 
51 void
set_length(double length)52 Kerb::set_length (double length)
53 {
54   if (length < m_end)
55     m_full_length = true;
56 
57   if (m_full_length)
58     m_end = length;
59 }
60 
61 double
width() const62 Kerb::width () const
63 {
64   return (m_points.size () > 0) ? (m_points.end () - 1)->x : 0.0;
65 }
66 
67 bool
on_kerb(double dist) const68 Kerb::on_kerb (double dist) const
69 {
70   return bool ((dist >= m_start)
71                && ((dist < m_end) || m_full_length));
72 }
73 
74 double
elevation(double along,double from_inside)75 Kerb::elevation (double along, double from_inside)
76 {
77   if (!on_kerb (along) || (from_inside < 0.0) || (from_inside > width ()))
78 	{
79 	  return 0.0;
80 	}
81   //!!TODO: Account for tapering on the transition.
82   return m_profile.interpolate (from_inside);
83 }
84 
85 double
angle(double along,double from_inside)86 Kerb::angle (double along, double from_inside)
87 {
88   if (!on_kerb (along) || (from_inside < 0.0) || (from_inside > width ()))
89 	{
90 	  return 0.0;
91 	}
92   Two_Vector norm = m_profile.normal (from_inside);
93   //!!TODO: Account for tapering on the transition.
94   return std::atan2 (norm.y, norm.x) - pi / 2.0;
95 }
96 
97 const Two_Vector&
point(size_t substrip) const98 Kerb::point (size_t substrip) const
99 {
100   assert (substrip < m_points.size ());
101   return m_points [substrip];
102 }
103 
104 //=============================================================================
Banking()105 Banking::Banking ()
106   : m_bank_angle (0.0, 0.0),
107     m_start_angle (0.0),
108     m_end_angle (0.0),
109     m_pivot_from_center (0.0)
110 {
111 }
112 
~Banking()113 Banking::~Banking ()
114 {
115 }
116 
117 void
set(double end_angle,double pivot_from_center)118 Banking::set (double end_angle, double pivot_from_center)
119 {
120   m_end_angle = end_angle;
121   m_pivot_from_center = pivot_from_center;
122 }
123 
124 double
angle(double along) const125 Banking::angle (double along) const
126 {
127   return Vamos_Geometry::deg_to_rad (m_bank_angle.interpolate (along));
128 }
129 
130 double
height(double along,double from_center) const131 Banking::height (double along, double from_center) const
132 {
133   return (m_pivot_from_center - from_center) * sin (angle (along));
134 }
135 
136 void
set_start(double start_angle,double length)137 Banking::set_start (double start_angle, double length)
138 {
139   m_start_angle = start_angle;
140   m_bank_angle.clear ();
141   m_bank_angle.load (0.0, start_angle);
142   m_bank_angle.load (length, m_end_angle);
143 }
144 
145 //=============================================================================
Pit_Lane_Transition()146 Pit_Lane_Transition::Pit_Lane_Transition ()
147   : m_direction (IN),
148     m_side (LEFT),
149     m_split_or_join (0.0),
150     m_merge (0.0),
151     m_angle (0.0),
152     m_pit_width (0.0),
153     m_pit_shoulder_width (0.0),
154     m_merge_is_set (false),
155     m_width_is_set (false)
156 {
157 }
158 
159 void
set_merge(Direction direction,Direction side,double split_or_join,double merge,double angle)160 Pit_Lane_Transition::set_merge (Direction direction,
161                                 Direction side,
162                                 double split_or_join,
163                                 double merge,
164                                 double angle)
165 {
166   m_direction = direction;
167   m_side = side;
168   m_split_or_join = split_or_join;
169   m_merge = merge;
170   m_merge_is_set = true;
171   m_angle = angle;
172 }
173 
174 void
set_width(double pit_width,double left_shoulder,double right_shoulder)175 Pit_Lane_Transition::set_width (double pit_width,
176                                 double left_shoulder,
177                                 double right_shoulder)
178 {
179   m_pit_width = skew (pit_width);
180   m_pit_shoulder_width = skew ((m_side == LEFT) ? left_shoulder : right_shoulder);
181   m_width_is_set = true;
182 }
183 
184 double
width(Direction pit_side,double distance,bool narrow) const185 Pit_Lane_Transition::width (Direction pit_side, double distance, bool narrow) const
186 {
187   if (pit_side != m_side) return 0.0;
188   if (narrow) return m_pit_width;
189   if (((m_direction == IN) && distance <= m_split_or_join)
190       || ((m_direction == OUT) && distance >= m_split_or_join))
191     return 0.0;
192   return m_pit_width;
193 }
194 
195 void
scale(double factor)196 Pit_Lane_Transition::scale (double factor)
197 {
198   m_split_or_join *= factor;
199   m_merge *= factor;
200 }
201 
202 //=============================================================================
Road_Segment(double length,double radius,double left_width,double right_width,double left_road_width,double right_road_width)203 Road_Segment::Road_Segment (double length,
204                             double radius,
205                             double left_width,
206                             double right_width,
207                             double left_road_width,
208                             double right_road_width)
209   : m_length (length),
210     m_radius (radius),
211     m_racing_line_adjustment (0.0),
212     m_racing_line_adjustment_distance (0.0),
213     m_left_wall_height (0.0),
214     m_right_wall_height (0.0),
215     m_start_skew (0.0),
216     m_end_skew (0.0),
217     mp_elevation_curve (0),
218     mp_left_kerb (0),
219     mp_right_kerb (0),
220     m_start_angle (0.0),
221     m_last_segment (false),
222     m_racing_line_curvature_factor (1.0)
223 {
224   m_left_road_width.load (Two_Vector (0.0, left_road_width));
225   m_right_road_width.load (Two_Vector (0.0, right_road_width));
226   m_left_width.load (Two_Vector (0.0, left_width));
227   m_right_width.load (Two_Vector (0.0, right_width));
228 }
229 
~Road_Segment()230 Road_Segment::~Road_Segment ()
231 {
232   delete mp_left_kerb;
233   delete mp_right_kerb;
234 }
235 
236 void
set_length(double length)237 Road_Segment::set_length (double length)
238 {
239   // Expand or contract the specified road widths along with the segment.
240   scale_widths (length / m_length);
241   m_length = length;
242 }
243 
244 void
set_radius(double radius)245 Road_Segment::set_radius (double radius)
246 {
247   double old_arc = arc ();
248   m_radius = radius;
249   if (old_arc != 0.0)
250     set_arc (old_arc);
251 }
252 
253 double
arc() const254 Road_Segment::arc () const
255 {
256   return (m_radius == 0.0) ? 0.0 : m_length / m_radius;
257 }
258 
259 void
set_arc(double new_arc)260 Road_Segment::set_arc (double new_arc)
261 {
262   if (m_radius == 0.0)
263     {
264       // Preserve length for straights.
265       set_radius (m_length / new_arc);
266     }
267   else
268     {
269       // Preserve radius for curves.
270       set_length (m_radius * new_arc);
271     }
272 }
273 
274 void
scale(double factor)275 Road_Segment::scale (double factor)
276 {
277   assert (factor != 0);
278   scale_widths (factor);
279   m_length *= factor;
280   m_radius *= factor;
281   m_pit.scale (factor);
282 }
283 
284 void
set_widths(const TPoints & right,const TPoints & right_road,const TPoints & left_road,const TPoints & left)285 Road_Segment::set_widths (const TPoints& right, const TPoints& right_road,
286                           const TPoints& left_road, const TPoints& left)
287 {
288   m_right_width.replace (right);
289   m_right_road_width.replace (right_road);
290   m_left_road_width.replace (left_road);
291   m_left_width.replace (left);
292 }
293 
294 void
scale_widths(double factor)295 Road_Segment::scale_widths (double factor)
296 {
297   m_left_width.scale (factor);
298   m_right_width.scale (factor);
299   m_left_road_width.scale (factor);
300   m_right_road_width.scale (factor);
301 }
302 
303 void
set_kerb(Kerb * kerb,Direction side)304 Road_Segment::set_kerb (Kerb* kerb, Direction side)
305 {
306   if (side == LEFT)
307     {
308       delete mp_left_kerb;
309       mp_left_kerb = kerb;
310     }
311   else
312     {
313       delete mp_right_kerb;
314       mp_right_kerb = kerb;
315     }
316 }
317 
318 double
kerb_width(Vamos_Geometry::Direction side,double along) const319 Road_Segment::kerb_width (Vamos_Geometry::Direction side, double along) const
320 {
321   const Kerb* kerb = (side == LEFT) ? mp_left_kerb : mp_right_kerb;
322   if ((kerb != 0) && kerb->on_kerb (along))
323     return kerb->width ();
324   return 0.0;
325 }
326 
327 void
set_wall_heights(double left_height,double right_height)328 Road_Segment::set_wall_heights (double left_height, double right_height)
329 {
330   m_left_wall_height = left_height;
331   m_right_wall_height = right_height;
332 }
333 
334 void
build_elevation(Spline * elevation,double start_distance)335 Road_Segment::build_elevation (Spline* elevation,
336 							   double start_distance)
337 {
338   mp_elevation_curve = elevation;
339   for (std::vector <Two_Vector>::iterator it = m_elevation_points.begin ();
340 	   it != m_elevation_points.end ();
341 	   it++)
342 	{
343 	  mp_elevation_curve->load (*it + Two_Vector (start_distance, 0.0));
344 	}
345 
346   if (m_last_segment)
347 	{
348 	  // Remove any elevation points near or greater than m_length.
349 	  mp_elevation_curve->remove_greater (start_distance + m_length - 10.0);
350 	}
351 }
352 
353 double
elevation(double along,double from_center) const354 Road_Segment::elevation (double along, double from_center) const
355 {
356   assert (mp_elevation_curve != 0);
357   double elev = mp_elevation_curve->interpolate (along + m_start_distance)
358     + banking().height (along, from_center);
359 
360   // Add the kerb's height.
361   double diff = from_center - left_road_width (along);
362   if (mp_left_kerb)
363     elev += mp_left_kerb->elevation (along, diff);
364   diff = -from_center - right_road_width (along);
365   if (mp_right_kerb)
366     elev += mp_right_kerb->elevation (along, diff);
367 
368   return elev;
369 }
370 
371 // A convenience function for getting the elevation at a point in
372 // world coordinates.  Use 'coordinates()' if you need the x and y
373 // track coodinates as well.
374 double
world_elevation(const Three_Vector & world_position) const375 Road_Segment::world_elevation (const Three_Vector& world_position) const
376 {
377   Three_Vector track_position;
378   coordinates (world_position, track_position);
379   return track_position.z;
380 }
381 
382 Three_Vector
barrier_normal(double along,double from_center,const Three_Vector & bump) const383 Road_Segment::barrier_normal (double along,
384                               double from_center,
385                               const Three_Vector& bump) const
386 {
387   Three_Vector normal = (from_center > 0.0)
388     ? -m_left_width.normal (along)
389     : m_right_width.normal (along);
390 
391   // The -y direction is up, -z is to the left, x is forward.
392   normal.x = bump.x;
393   normal.z = -bump.y;
394   return normal.rotate (angle (along) * Three_Vector::Z);
395 }
396 
397 Three_Vector
barrier_normal(double along,double from_center) const398 Road_Segment::barrier_normal (double along, double from_center) const
399 {
400   Three_Vector bump = Three_Vector::Z;
401   return barrier_normal (along, from_center, bump);
402 }
403 
404 Three_Vector
normal(double along,double from_center,const Three_Vector & bump,bool include_kerb) const405 Road_Segment::normal (double along,
406                       double from_center,
407                       const Three_Vector& bump,
408                       bool include_kerb /* true */ ) const
409 {
410   assert (mp_elevation_curve != 0);
411 
412   Three_Vector norm (mp_elevation_curve->normal (along + m_start_distance));
413   // The z direction is up, y is to the left, x is forward.
414   norm.z = norm.y;
415   norm.y = bump.y;
416   norm.x += bump.x;
417 
418   double bank = m_banking.angle (along);
419   if (include_kerb && mp_left_kerb)
420     bank -= mp_left_kerb->angle (along, from_center - left_road_width (along));
421   if (include_kerb && mp_right_kerb)
422     bank += mp_right_kerb->angle (along, -from_center - right_road_width (along));
423   return norm.rotate (-bank * Three_Vector::X).rotate (angle (along) * Three_Vector::Z);
424 }
425 
426 Three_Vector
normal(double along,double from_center,bool include_kerb) const427 Road_Segment::normal (double along,
428                       double from_center,
429                       bool include_kerb /* true */ ) const
430 {
431   return normal (along, from_center, Three_Vector::Z, include_kerb);
432 }
433 
434 void
set_start(const Three_Vector & start_coords,double start_distance,double start_angle,double start_bank,const std::vector<double> & texture_offsets)435 Road_Segment::set_start (const Three_Vector& start_coords,
436                          double start_distance,
437                          double start_angle,
438                          double start_bank,
439                          const std::vector <double>& texture_offsets)
440 {
441   m_start_distance = start_distance;
442   m_start_coords = start_coords;
443   m_start_angle = start_angle;
444   m_banking.set_start (start_bank, m_length);
445 }
446 
447 double
end_angle() const448 Road_Segment::end_angle () const
449 {
450   // Keep it within [0, 2pi).
451   return branch (m_start_angle + arc (), 0.0);
452 }
453 
454 Three_Vector
center_of_curve() const455 Road_Segment::center_of_curve () const
456 {
457   return m_start_coords + Three_Vector (m_radius, m_start_angle + pi/2);
458 }
459 
460 Three_Vector
end_coords() const461 Road_Segment::end_coords () const
462 {
463   if (is_straight ())
464     return m_start_coords + Three_Vector (m_length, m_start_angle);
465   else
466     return center_of_curve ()
467       - Three_Vector (m_radius, m_start_angle + arc () + pi/2);
468 }
469 
470 double
left_width(double distance,bool narrow) const471 Road_Segment::left_width (double distance, bool narrow) const
472 {
473   return m_left_width.interpolate (distance)
474     - m_pit.width (LEFT, distance, narrow);
475 }
476 
477 double
right_width(double distance,bool narrow) const478 Road_Segment::right_width (double distance, bool narrow) const
479 {
480   return m_right_width.interpolate (distance)
481     - m_pit.width (RIGHT, distance, narrow);
482 }
483 
484 void
set_left_width(double distance,double width)485 Road_Segment::set_left_width (double distance, double width)
486 {
487   m_left_width.load (Two_Vector (distance, width));
488 }
489 
490 void
set_right_width(double distance,double width)491 Road_Segment::set_right_width (double distance, double width)
492 {
493   m_right_width.load (Two_Vector (distance, width));
494 }
495 
496 void
set_left_road_width(double distance,double width)497 Road_Segment::set_left_road_width (double distance, double width)
498 {
499   m_left_road_width.load (Two_Vector (distance, width));
500 }
501 
502 void
set_right_road_width(double distance,double width)503 Road_Segment::set_right_road_width (double distance, double width)
504 {
505   m_right_road_width.load (Two_Vector (distance, width));
506 }
507 
508 double
left_racing_line_width(double distance) const509 Road_Segment::left_racing_line_width (double distance) const
510 {
511   distance -= start_distance ();
512   const double width = left_road_width_no_pit (distance);
513   if ((m_racing_line_adjustment_distance < 0.0)
514       || (distance < m_racing_line_adjustment_distance))
515     return width + m_racing_line_adjustment;
516   return width;
517 }
518 
519 double
right_racing_line_width(double distance) const520 Road_Segment::right_racing_line_width (double distance) const
521 {
522   distance -= start_distance ();
523   const double width = right_road_width_no_pit (distance);
524   if ((m_racing_line_adjustment_distance < 0.0)
525       || (distance < m_racing_line_adjustment_distance))
526     return width - m_racing_line_adjustment;
527   return width;
528 }
529 
530 void
set_racing_line_adjustment(double across,double distance)531 Road_Segment::set_racing_line_adjustment (double across, double distance)
532 {
533   m_racing_line_adjustment = across;
534   m_racing_line_adjustment_distance = distance;
535 }
536 
537 double
pit_width() const538 Road_Segment::pit_width () const
539 {
540   return m_pit.width (m_pit.side (), m_pit.split_or_join (), true);
541 }
542 
543 double
pit_road_connection() const544 Road_Segment::pit_road_connection () const
545 {
546   return m_pit.split_or_join ();
547 }
548 
549 double
extra_road_width(Direction pit_side,double distance,bool narrow) const550 Road_Segment::extra_road_width (Direction pit_side,
551                                 double distance,
552                                 bool narrow) const
553 {
554   if (narrow || (pit_side != m_pit.side ())) return 0.0;
555 
556   double width = -m_pit.shoulder_width ();
557   if (pit_side == LEFT)
558     width += left_width (distance) - left_road_width (distance, true);
559   else
560     width += right_width (distance) - right_road_width (distance, true);
561 
562   const double extra = width * (distance - m_pit.merge ())
563     / (m_pit.split_or_join () - m_pit.merge ());
564 
565   if ((m_pit.direction () == IN)
566       && (distance > m_pit.merge ())
567       && (distance <= m_pit.split_or_join ()))
568     return extra;
569   if ((m_pit.direction () == OUT)
570       && (distance < m_pit.merge ())
571       && (distance >= m_pit.split_or_join ()))
572     return extra;
573 
574   return 0.0;
575 }
576 
577 double
left_road_width_no_pit(double distance) const578 Road_Segment::left_road_width_no_pit (double distance) const
579 {
580   return m_left_road_width.interpolate (distance);
581 }
582 
583 double
right_road_width_no_pit(double distance) const584 Road_Segment::right_road_width_no_pit (double distance) const
585 {
586   return m_right_road_width.interpolate (distance);
587 }
588 
589 double
left_road_width(double distance,bool narrow) const590 Road_Segment::left_road_width (double distance, bool narrow) const
591 {
592   return left_road_width_no_pit (distance)
593     + extra_road_width (LEFT, distance, narrow);
594 }
595 
596 double
right_road_width(double distance,bool narrow) const597 Road_Segment::right_road_width (double distance, bool narrow) const
598 {
599   return right_road_width_no_pit (distance)
600     + extra_road_width (RIGHT, distance, narrow);
601 }
602 
603 const
pit() const604 Pit_Lane_Transition& Road_Segment::pit () const
605 {
606   return m_pit;
607 }
608 
609 void
set_pit_width(double width,double left_shoulder,double right_shoulder)610 Road_Segment::set_pit_width (double width,
611                              double left_shoulder,
612                              double right_shoulder)
613 {
614   m_pit.set_width (width, left_shoulder, right_shoulder);
615 }
616 
617 void
set_pit_lane(Direction direction,Direction side,double split_or_join,double merge,double angle)618 Road_Segment::set_pit_lane (Direction direction,
619                             Direction side,
620                             double split_or_join,
621                             double merge,
622                             double angle)
623 {
624   m_pit.set_merge (direction, side, split_or_join, merge, deg_to_rad (angle));
625 }
626 
627 std::complex <double>
solve_quadratic(double a,double b,double c,double solution)628 solve_quadratic (double a, double b, double c, double solution)
629 {
630   if (a == 0.0) return -c / b;
631   return (-b + Vamos_Geometry::sign (solution)
632           * sqrt (std::complex <double> (b*b - 4*a*c)))
633     / (2*a);
634 }
635 
636 // Find the angle from the beginning of a curve.
637 double
get_curve_angle(const Three_Vector & position,double across) const638 Road_Segment::get_curve_angle (const Three_Vector& position,
639                                double across) const
640 {
641   return arc () / 2.0
642     + atan2 (sign (m_radius) * position.y,
643              sign (m_radius)
644              * (position.x - across * m_start_skew / sin (arc () / 2.0)));
645 }
646 
647 // Do the world-to-track coordinate transformation.
648 double
coordinates(const Three_Vector & world_position,Three_Vector & track_position) const649 Road_Segment::coordinates (const Three_Vector& world_position,
650 						   Three_Vector& track_position) const
651 {
652   if (!is_straight ())
653     {
654       const double half_angle = arc () / 2.0;
655 
656       const Three_Vector centered_position =
657         (world_position - center_of_curve ()).
658         rotate ((pi / 2.0 - half_angle - m_start_angle) * Three_Vector::Z);
659 
660       const std::complex <double> across =
661         solve_quadratic (1.0 +
662                          2.0 * m_start_skew / tan (half_angle)
663                          - m_start_skew * m_start_skew,
664                          -2.0 * (m_radius + (m_start_skew / sin (half_angle)
665                                              * (m_radius * cos (half_angle)
666                                                 - centered_position.x))),
667                          m_radius * m_radius
668                          - centered_position.x * centered_position.x
669                          - centered_position.y * centered_position.y,
670                          -m_radius);
671 
672       track_position.y = across.real ();
673       if (across.imag () != 0.0)
674         {
675           // We're off the segment but the solution is complex.  Just
676           // indicate whether we're off the beginning or end of the
677           // segement.
678           if ((world_position - m_start_coords).magnitude ()
679               < (world_position - end_coords ()).magnitude ())
680             track_position.x = -1.0;
681           else
682             track_position.x = m_length + 1.0;
683         }
684       else
685         {
686           track_position.x = m_radius
687             * get_curve_angle (centered_position, track_position.y);
688         }
689       track_position.z = 0.0;
690     }
691   else
692     {
693       track_position =
694         (world_position - center_of_curve ()).rotate (-m_start_angle * Three_Vector::Z);
695       track_position.x = (track_position.x - track_position.y * m_start_skew)/
696         (1 + track_position.y / m_length * (m_end_skew - m_start_skew));
697     }
698   track_position.z = elevation (track_position.x, track_position.y);
699   return off_track_distance (track_position);
700 }
701 
702 // How far is the position off the end (+ve) or beginning (-ve) of
703 // the segment?  Return 0 if the position is on the segment.
704 double
off_track_distance(const Three_Vector & track_position) const705 Road_Segment::off_track_distance (const Three_Vector& track_position) const
706 {
707   const double max
708     = ((m_pit.direction () == IN)
709        && on_pit_merge (track_position.x, track_position.y))
710     ? m_pit.split_or_join () : m_length;
711 
712   const double min
713     = ((m_pit.direction () == OUT)
714        && on_pit_merge (track_position.x, track_position.y))
715     ? m_pit.split_or_join () : 0.0;
716 
717   if (track_position.x < min)
718     return track_position.x - min;
719   if (track_position.x > max)
720     return track_position.x - max;
721   return 0.0;
722 }
723 
724 bool
on_pit_merge(double distance,double from_center) const725 Road_Segment::on_pit_merge (double distance, double from_center) const
726 {
727   if ((distance < 0.0) || (distance > m_length))
728     return false;
729 
730   const double from_split = (distance - m_pit.split_or_join ())
731     * ((m_pit.direction () == IN) ? 1.0 : -1.0);
732   const double from_wall = (m_pit.side () == RIGHT)
733     ? -from_center - right_width (distance)
734     : from_center - left_width (distance);
735 
736   return m_pit.active ()
737     && (from_split > 0.0)
738     && (from_wall > 0.0)
739     && (std::abs (atan2 (from_wall, from_split)) > std::abs (m_pit.angle ()) / 2.0);
740 }
741 
742 // Do the track-to-world coordinate transformation.
743 Three_Vector
position(double along,double from_center) const744 Road_Segment::position (double along, double from_center) const
745 {
746   Three_Vector pos (0.0, 0.0, elevation (along, from_center));
747   if (is_straight ())
748     {
749       const double extra = from_center
750         * (m_start_skew + ((m_end_skew - m_start_skew) * along
751                            / m_length));
752 
753       pos += start_coords ()
754         + (Three_Vector (along + extra, from_center, 0.0).
755            rotate (m_start_angle * Three_Vector::Z));
756     }
757   else
758     {
759       const double beta = arc () / 2.0;
760       const double radius =
761         m_radius - from_center * (1.0 + m_start_skew / tan (beta));
762 
763       const Three_Vector center (center_of_curve ()
764                                  + (from_center * m_start_skew / sin (beta)
765                                     * Three_Vector (sin (m_start_angle + beta),
766                                                     -cos (m_start_angle + beta),
767                                                     0.0)));
768 
769       const double angle = m_start_angle + along / m_radius;
770       pos += center
771         - (Three_Vector (0.0, radius, 0.0).rotate (angle * Three_Vector::Z));
772     }
773   return pos;
774 }
775 
776 void
narrow(Direction side,double delta_width)777 Road_Segment::narrow (Direction side, double delta_width)
778 {
779   if (side == RIGHT)
780     m_right_width.shift (-delta_width);
781   else
782     m_left_width.shift (-delta_width);
783 }
784