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