1 /* This file is part of Shapes.
3 * Shapes is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
8 * Shapes is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with Shapes. If not, see <http://www.gnu.org/licenses/>.
16 * Copyright 2008, 2009 Henrik Tidefelt
21 #include "shapestypes.h"
22 #include "shapesexceptions.h"
26 #include "angleselect.h"
28 #include "upsamplers.h"
29 #include "quadratic_programs.h"
30 #include "constructorrepresentation.h"
36 using namespace Shapes
;
43 ControlPoints
< Concrete::Coords2D
>::get( double * dst
) const
45 *dst
= Concrete::Length::offtype( p0_
.x_
);
47 *dst
= Concrete::Length::offtype( p0_
.y_
);
49 *dst
= Concrete::Length::offtype( p1_
.x_
);
51 *dst
= Concrete::Length::offtype( p1_
.y_
);
53 *dst
= Concrete::Length::offtype( p2_
.x_
);
55 *dst
= Concrete::Length::offtype( p2_
.y_
);
57 *dst
= Concrete::Length::offtype( p3_
.x_
);
59 *dst
= Concrete::Length::offtype( p3_
.y_
);
64 ControlPoints
< Concrete::Coords2D
>::getEndpoints( double * dst
) const
66 *dst
= Concrete::Length::offtype( p0_
.x_
);
68 *dst
= Concrete::Length::offtype( p0_
.y_
);
70 *dst
= Concrete::Length::offtype( p3_
.x_
);
72 *dst
= Concrete::Length::offtype( p3_
.y_
);
78 isinfPhysical( const double & p1
)
85 modPhysical( const double & p1
, const double & p2
)
87 return fmod( p1
, p2
);
90 Lang::ElementaryPath2D::ElementaryPath2D( )
91 : allComplete_( true )
94 DISPATCHIMPL( ElementaryPath2D
);
96 Lang::ElementaryPath2D::~ElementaryPath2D( )
99 Kernel::VariableHandle
100 Lang::ElementaryPath2D::getField( const char * fieldID
, const RefCountPtr
< const Lang::Value
> & selfRef
) const
102 return getField( fieldID
, selfRef
.down_cast
< const Lang::ElementaryPath2D
>( ) );
105 Kernel::VariableHandle
106 Lang::ElementaryPath2D::getField( const char * fieldID
, const RefCountPtr
< const Lang::ElementaryPath2D
> & selfRef
) const
108 if( strcmp( fieldID
, "begin" ) == 0 )
110 return Helpers::newValHandle( new Lang::PathSlider2D( selfRef
, Concrete::ZERO_TIME
) );
112 if( strcmp( fieldID
, "end" ) == 0 )
114 return Helpers::newValHandle( new Lang::PathSlider2D( selfRef
, Concrete::Time( duration( ) ) ) );
116 if( strcmp( fieldID
, "closed?" ) == 0 )
120 return Kernel::THE_TRUE_VARIABLE
;
122 return Kernel::THE_FALSE_VARIABLE
;
124 if( strcmp( fieldID
, "null?" ) == 0 )
128 return Kernel::THE_TRUE_VARIABLE
;
130 return Kernel::THE_FALSE_VARIABLE
;
132 throw Exceptions::NonExistentMember( getTypeName( ), fieldID
);
136 Lang::ElementaryPath2D::writePath( ostream
& os
) const
138 const_iterator i
= begin( );
141 throw Exceptions::InternalError( "Invoking writePath on the empty path." );
143 const Concrete::Coords2D
* p0
= (*i
)->mid_
;
145 const Concrete::Coords2D
* p1
= (*i
)->front_
;
147 for( ; i
!= end( ); ++i
)
149 const Concrete::Coords2D
* p2
= (*i
)->rear_
;
150 const Concrete::Coords2D
* p3
= (*i
)->mid_
;
151 if( p1
!= p0
&& p2
!= p3
)
153 os
<< *p1
<< " " << *p2
<< " " << *p3
<< " c " ;
157 os
<< *p1
<< " " << *p3
<< " y " ;
161 os
<< *p2
<< " " << *p3
<< " v " ;
173 const Concrete::Coords2D
* p2
= (*i
)->rear_
;
174 const Concrete::Coords2D
* p3
= (*i
)->mid_
;
175 if( p1
!= p0
&& p2
!= p3
)
177 os
<< *p1
<< " " << *p2
<< " " << *p3
<< " c " ;
181 os
<< *p1
<< " " << *p3
<< " y " ;
185 os
<< *p2
<< " " << *p3
<< " v " ;
187 /* In case there is no handle along this segment, it is drawn by the h operator itself.
194 Lang::ElementaryPath2D::writeInputForm( ostream
& os
) const
196 for( const_iterator i
= begin( ); i
!= end( ); ++i
)
202 if( (*i
)->rear_
!= (*i
)->mid_
)
204 os
<< Helpers::shapesFormat( *(*i
)->rear_
) << "<" ;
206 os
<< Helpers::shapesFormat( *(*i
)->mid_
);
207 if( (*i
)->front_
!= (*i
)->mid_
)
209 os
<< ">" << Helpers::shapesFormat( *(*i
)->front_
);
218 RefCountPtr
< const Lang::ElementaryPath2D
>
219 Lang::ElementaryPath2D::elementaryTransformed( const Lang::Transform2D
& tf
) const
221 Lang::ElementaryPath2D
* res
= new Lang::ElementaryPath2D
;
226 for( const_iterator i
= begin( ); i
!= end( ); ++i
)
228 res
->push_back( (*i
)->transformed( tf
) );
230 return RefCountPtr
< const Lang::ElementaryPath2D
>( res
);
233 RefCountPtr
< const Lang::ElementaryPath3D
>
234 Lang::ElementaryPath2D::elementaryTransformed( const Lang::Transform3D
& tf
) const
236 Lang::ElementaryPath3D
* res
= new Lang::ElementaryPath3D
;
241 for( const_iterator i
= begin( ); i
!= end( ); ++i
)
243 res
->push_back( (*i
)->transformed( tf
) );
245 return RefCountPtr
< const Lang::ElementaryPath3D
>( res
);
248 RefCountPtr
< const Lang::Path2D
>
249 Lang::ElementaryPath2D::typed_transformed( const Lang::Transform2D
& tf
) const
251 return elementaryTransformed( tf
);
255 Lang::ElementaryPath2D::elementaryJob( std::stack
< const Lang::Value
* > * nodeStack
, Lang::ElementaryPath2D
* pth
, Concrete::Coords2D
* basePoint
) const
257 for( const_iterator i
= begin( ); i
!= end( ); ++i
)
259 pth
->push_back( new Concrete::PathPoint2D( **i
) );
263 *basePoint
= *(back( )->mid_
);
268 Lang::ElementaryPath2D::timeCheck( Concrete::Time t
) const
272 if( isinfPhysical( t
) )
274 throw Exceptions::OutOfRange( "The time argument must not be infinite for closed_ paths." );
276 Concrete::Time
tMax( duration( ) );
277 t
= modPhysical( t
, tMax
);
278 if( t
< Concrete::ZERO_TIME
)
285 if( t
< Concrete::ZERO_TIME
)
287 throw Exceptions::OutOfRange( "The time argument must be non-negative for open paths." );
289 if( t
>= Concrete::HUGE_TIME
)
291 t
= Concrete::Time( duration( ) );
293 else if( t
> Concrete::Time( duration( ) ) )
295 std::ostringstream msg
;
296 msg
<< "The time argument must not be greater than the duration of the path: "
297 << Concrete::Time::offtype( t
) << " > " << duration( ) << "." ;
298 throw Exceptions::OutOfRange( strrefdup( msg
) );
305 Lang::ElementaryPath2D::findSegment( Concrete::Time t
, Concrete::Time
* tRes
, const Concrete::PathPoint2D
** p1
, const Concrete::PathPoint2D
** p2
) const
308 const_iterator i
= begin( );
309 for( ; t
>= Concrete::UNIT_TIME
; t
-= Concrete::UNIT_TIME
, ++i
)
327 Lang::ElementaryPath2D::findSegmentReverse( Concrete::Time t
, Concrete::Time
* tRes
, const Concrete::PathPoint2D
** p1
, const Concrete::PathPoint2D
** p2
) const
331 const_iterator i
= begin( );
332 for( ; t
> Concrete::ZERO_TIME
; t
-= Concrete::UNIT_TIME
, ++i
)
334 *tRes
= t
+ Concrete::UNIT_TIME
;
348 RefCountPtr
< const Lang::Coords2D
>
349 Lang::ElementaryPath2D::point( Concrete::Time globalTime
) const
353 throw Exceptions::OutOfRange( "The empty path has no points at all." );
356 const Concrete::PathPoint2D
* p1
;
357 const Concrete::PathPoint2D
* p2
;
358 findSegment( globalTime
, &t
, &p1
, &p2
);
360 if( t
== Concrete::ZERO_TIME
)
362 return RefCountPtr
< const Lang::Coords2D
>( new Lang::Coords2D( * p1
->mid_
) );
364 if( t
== Concrete::UNIT_TIME
)
366 return RefCountPtr
< const Lang::Coords2D
>( new Lang::Coords2D( * p2
->mid_
) );
369 Concrete::Bezier x0
= p1
->mid_
->x_
.offtype
< 0, 3 >( );
370 Concrete::Bezier y0
= p1
->mid_
->y_
.offtype
< 0, 3 >( );
371 Concrete::Bezier x1
= p1
->front_
->x_
.offtype
< 0, 3 >( );
372 Concrete::Bezier y1
= p1
->front_
->y_
.offtype
< 0, 3 >( );
373 Concrete::Bezier x2
= p2
->rear_
->x_
.offtype
< 0, 3 >( );
374 Concrete::Bezier y2
= p2
->rear_
->y_
.offtype
< 0, 3 >( );
375 Concrete::Bezier x3
= p2
->mid_
->x_
.offtype
< 0, 3 >( );
376 Concrete::Bezier y3
= p2
->mid_
->y_
.offtype
< 0, 3 >( );
378 Concrete::Time tc
= Concrete::UNIT_TIME
- t
; /* complement to t */
379 Physical
< 0, 3 > k0
= tc
* tc
* tc
;
380 Physical
< 0, 3 > k1
= Concrete::Scalar( 3 ) * tc
* tc
* t
;
381 Physical
< 0, 3 > k2
= Concrete::Scalar( 3 ) * tc
* t
* t
;
382 Physical
< 0, 3 > k3
= t
* t
* t
;
383 Concrete::Length x
= x0
* k0
+ x1
* k1
+ x2
* k2
+ x3
* k3
;
384 Concrete::Length y
= y0
* k0
+ y1
* k1
+ y2
* k2
+ y3
* k3
;
386 return RefCountPtr
< const Lang::Coords2D
>( new Lang::Coords2D( x
, y
) );
389 RefCountPtr
< const Lang::Length
>
390 Lang::ElementaryPath2D::speed( Concrete::Time globalTime
) const
394 throw Exceptions::OutOfRange( "The empty path has no speed at all." );
397 const Concrete::PathPoint2D
* p1
;
398 const Concrete::PathPoint2D
* p2
;
399 findSegment( globalTime
, &t
, &p1
, &p2
);
401 if( t
<= Concrete::ZERO_TIME
)
403 /* here, t = 0, tc == 1, so
405 Concrete::Bezier x0
= p1
->mid_
->x_
.offtype
< 0, 3 >( );
406 Concrete::Bezier y0
= p1
->mid_
->y_
.offtype
< 0, 3 >( );
407 Concrete::Bezier x1
= p1
->front_
->x_
.offtype
< 0, 3 >( );
408 Concrete::Bezier y1
= p1
->front_
->y_
.offtype
< 0, 3 >( );
409 Concrete::Speed vx
= ( x0
* (-3) + x1
* 3 ).offtype
< 0, -2 >( );
410 Concrete::Speed vy
= ( y0
* (-3) + y1
* 3 ).offtype
< 0, -2 >( );
411 // Concrete::Speed vx = x1 - x0;
412 // Concrete::Speed vy = y1 - y0;
413 return RefCountPtr
< const Lang::Length
>( new Lang::Length( hypotPhysical( vx
, vy
).offtype
< 0, -1 >( ) ) );
415 if( t
>= Concrete::UNIT_TIME
)
417 throw Exceptions::InternalError( "t1 == 1 in speed calculation" );
420 Concrete::Bezier x0
= p1
->mid_
->x_
.offtype
< 0, 3 >( );
421 Concrete::Bezier y0
= p1
->mid_
->y_
.offtype
< 0, 3 >( );
422 Concrete::Bezier x1
= p1
->front_
->x_
.offtype
< 0, 3 >( );
423 Concrete::Bezier y1
= p1
->front_
->y_
.offtype
< 0, 3 >( );
424 Concrete::Bezier x2
= p2
->rear_
->x_
.offtype
< 0, 3 >( );
425 Concrete::Bezier y2
= p2
->rear_
->y_
.offtype
< 0, 3 >( );
426 Concrete::Bezier x3
= p2
->mid_
->x_
.offtype
< 0, 3 >( );
427 Concrete::Bezier y3
= p2
->mid_
->y_
.offtype
< 0, 3 >( );
429 Concrete::Time tc
= Concrete::UNIT_TIME
- t
; /* complement to t */
430 Physical
< 0, 2 > kv0
= -3 * tc
* tc
;
431 Physical
< 0, 2 > kv1
= 3 * tc
* tc
- 6 * tc
* t
;
432 Physical
< 0, 2 > kv2
= 6 * tc
* t
- 3 * t
* t
;
433 Physical
< 0, 2 > kv3
= 3 * t
* t
;
434 // Physical< 0, 2 > kv0 = - tc * tc;
435 // Physical< 0, 2 > kv1 = tc * tc - 2 * tc * t;
436 // Physical< 0, 2 > kv2 = 2 * tc * t - t * t;
437 // Physical< 0, 2 > kv3 = t * t;
438 Concrete::Speed vx
= x0
* kv0
+ x1
* kv1
+ x2
* kv2
+ x3
* kv3
;
439 Concrete::Speed vy
= y0
* kv0
+ y1
* kv1
+ y2
* kv2
+ y3
* kv3
;
441 return RefCountPtr
< const Lang::Length
>( new Lang::Length( hypotPhysical( vx
, vy
).offtype
< 0, -1 >( ) ) );
444 RefCountPtr
< const Lang::Length
>
445 Lang::ElementaryPath2D::reverse_speed( Concrete::Time globalTime
) const
449 throw Exceptions::OutOfRange( "The empty path has no speed at all." );
452 const Concrete::PathPoint2D
* p1
;
453 const Concrete::PathPoint2D
* p2
;
454 findSegmentReverse( globalTime
, &t
, &p1
, &p2
);
456 if( t
<= Concrete::ZERO_TIME
)
458 throw Exceptions::InternalError( "t1 == 0 in reverse speed calculation" );
460 if( t
>= Concrete::UNIT_TIME
)
462 /* here, t = 1, tc == 0, so
464 Concrete::Bezier x2
= p2
->rear_
->x_
.offtype
< 0, 3 >( );
465 Concrete::Bezier y2
= p2
->rear_
->y_
.offtype
< 0, 3 >( );
466 Concrete::Bezier x3
= p2
->mid_
->x_
.offtype
< 0, 3 >( );
467 Concrete::Bezier y3
= p2
->mid_
->y_
.offtype
< 0, 3 >( );
468 Concrete::Speed vx
= ( x2
* (-3) + x3
* 3 ).offtype
< 0, -2 >( );
469 Concrete::Speed vy
= ( y2
* (-3) + y3
* 3 ).offtype
< 0, -2 >( );
470 // Concrete::Speed vx = x3 - x2;
471 // Concrete::Speed vy = y3 - y2;
472 return RefCountPtr
< const Lang::Length
>( new Lang::Length( hypotPhysical( vx
, vy
).offtype
< 0, -1 >( ) ) );
475 Concrete::Bezier x0
= p1
->mid_
->x_
.offtype
< 0, 3 >( );
476 Concrete::Bezier y0
= p1
->mid_
->y_
.offtype
< 0, 3 >( );
477 Concrete::Bezier x1
= p1
->front_
->x_
.offtype
< 0, 3 >( );
478 Concrete::Bezier y1
= p1
->front_
->y_
.offtype
< 0, 3 >( );
479 Concrete::Bezier x2
= p2
->rear_
->x_
.offtype
< 0, 3 >( );
480 Concrete::Bezier y2
= p2
->rear_
->y_
.offtype
< 0, 3 >( );
481 Concrete::Bezier x3
= p2
->mid_
->x_
.offtype
< 0, 3 >( );
482 Concrete::Bezier y3
= p2
->mid_
->y_
.offtype
< 0, 3 >( );
484 Concrete::Time tc
= Concrete::UNIT_TIME
- t
; /* complement to t */
485 Physical
< 0, 2 > kv0
= -3 * tc
* tc
;
486 Physical
< 0, 2 > kv1
= 3 * tc
* tc
- 6 * tc
* t
;
487 Physical
< 0, 2 > kv2
= 6 * tc
* t
- 3 * t
* t
;
488 Physical
< 0, 2 > kv3
= 3 * t
* t
;
489 // Physical< 0, 2 > kv0 = - tc * tc;
490 // Physical< 0, 2 > kv1 = tc * tc - 2 * tc * t;
491 // Physical< 0, 2 > kv2 = 2 * tc * t - t * t;
492 // Physical< 0, 2 > kv3 = t * t;
493 Concrete::Speed vx
= x0
* kv0
+ x1
* kv1
+ x2
* kv2
+ x3
* kv3
;
494 Concrete::Speed vy
= y0
* kv0
+ y1
* kv1
+ y2
* kv2
+ y3
* kv3
;
496 return RefCountPtr
< const Lang::Length
>( new Lang::Length( hypotPhysical( vx
, vy
).offtype
< 0, -1 >( ) ) );
499 RefCountPtr
< const Lang::FloatPair
>
500 Lang::ElementaryPath2D::direction( Concrete::Time globalTime
) const
504 throw Exceptions::OutOfRange( "The empty path has no directions at all." );
507 const Concrete::PathPoint2D
* p1
;
508 const Concrete::PathPoint2D
* p2
;
509 findSegment( globalTime
, &t
, &p1
, &p2
);
511 Bezier::ControlPoints
< Concrete::Coords2D
> controls( *(p1
->mid_
), *(p1
->front_
), *(p2
->rear_
), *(p2
->mid_
) );
512 Bezier::PolyCoeffs
< Concrete::Coords2D
> coeffs( controls
);
514 const Concrete::Length segLengthBound
=
515 ( controls
.p1_
- controls
.p0_
).norm( ) + ( controls
.p2_
- controls
.p1_
).norm( ) + ( controls
.p3_
- controls
.p2_
).norm( );
516 const Concrete::Length shortLength
= 1.e
-4 * segLengthBound
;
519 Concrete::Coords2D d
= coeffs
.velocity( t
.offtype
< 0, 1 >( ) );
520 Concrete::Length v
= d
.norm( );
521 if( v
> shortLength
)
523 return RefCountPtr
< const Lang::FloatPair
>( new Lang::FloatPair( d
.direction( v
) ) );
527 /* We reach here if the velocity more or less was vanishing at t. If we are at the beginning of a segment, the acceleration
528 * will be in the direction of the limiting velocity. If we are at the end, the acceleration will be in the opposite direction
529 * of the limiting velocity.
533 Concrete::Coords2D d
= ( t
< 0.5 ? 1 : -1 ) * coeffs
.acceleration( t
.offtype
< 0, 1 >( ) );
534 Concrete::Length v
= d
.norm( );
535 if( v
> shortLength
)
537 return RefCountPtr
< const Lang::FloatPair
>( new Lang::FloatPair( d
.direction( v
) ) );
540 return RefCountPtr
< const Lang::FloatPair
>( new Lang::FloatPair( 0, 0 ) );
543 RefCountPtr
< const Lang::FloatPair
>
544 Lang::ElementaryPath2D::reverse_direction( Concrete::Time globalTime
) const
548 throw Exceptions::OutOfRange( "The empty path has no directions at all." );
551 const Concrete::PathPoint2D
* p1
;
552 const Concrete::PathPoint2D
* p2
;
553 findSegmentReverse( globalTime
, &t
, &p1
, &p2
);
555 Bezier::ControlPoints
< Concrete::Coords2D
> controls( *(p1
->mid_
), *(p1
->front_
), *(p2
->rear_
), *(p2
->mid_
) );
556 Bezier::PolyCoeffs
< Concrete::Coords2D
> coeffs( controls
);
558 const Concrete::Length segLengthBound
=
559 ( controls
.p1_
- controls
.p0_
).norm( ) + ( controls
.p2_
- controls
.p1_
).norm( ) + ( controls
.p3_
- controls
.p2_
).norm( );
560 const Concrete::Length shortLength
= 1.e
-4 * segLengthBound
;
563 Concrete::Coords2D d
= (-1) * coeffs
.velocity( t
.offtype
< 0, 1 >( ) );
564 Concrete::Length v
= d
.norm( );
565 if( v
> shortLength
)
567 return RefCountPtr
< const Lang::FloatPair
>( new Lang::FloatPair( d
.direction( v
) ) );
571 /* We reach here if the velocity more or less was vanishing at t. If we are at the beginning of a segment, the acceleration
572 * will be in the direction of the limiting velocity. If we are at the end, the acceleration will be in the opposite direction
573 * of the limiting velocity.
577 Concrete::Coords2D d
= ( t
< 0.5 ? -1 : 1 ) * coeffs
.acceleration( t
.offtype
< 0, 1 >( ) );
578 Concrete::Length v
= d
.norm( );
579 if( v
> shortLength
)
581 return RefCountPtr
< const Lang::FloatPair
>( new Lang::FloatPair( d
.direction( v
) ) );
584 return RefCountPtr
< const Lang::FloatPair
>( new Lang::FloatPair( 0, 0 ) );
587 RefCountPtr
< const Lang::Length
>
588 Lang::ElementaryPath2D::radiusOfCurvature( Concrete::Time globalTime
) const
592 throw Exceptions::OutOfRange( "The empty path has no curvature at all." );
595 const Concrete::PathPoint2D
* p1
;
596 const Concrete::PathPoint2D
* p2
;
597 findSegment( globalTime
, &t
, &p1
, &p2
);
599 if( t
<= Concrete::ZERO_TIME
)
601 /* here, t = 0, tc == 1, so
603 Concrete::Bezier x0
= p1
->mid_
->x_
.offtype
< 0, 3 >( );
604 Concrete::Bezier y0
= p1
->mid_
->y_
.offtype
< 0, 3 >( );
605 Concrete::Bezier x1
= p1
->front_
->x_
.offtype
< 0, 3 >( );
606 Concrete::Bezier y1
= p1
->front_
->y_
.offtype
< 0, 3 >( );
607 Concrete::Bezier x2
= p2
->rear_
->x_
.offtype
< 0, 3 >( );
608 Concrete::Bezier y2
= p2
->rear_
->y_
.offtype
< 0, 3 >( );
609 Concrete::Speed vx
= ( x0
* (-3) + x1
* 3 ).offtype
< 0, -2 >( );
610 Concrete::Speed vy
= ( y0
* (-3) + y1
* 3 ).offtype
< 0, -2 >( );
611 Concrete::Acceleration ax
= ( x0
* 6 + x2
* 6 ).offtype
< 0, -1 >( );
612 Concrete::Acceleration ay
= ( y0
* 6 + y2
* 6 ).offtype
< 0, -1 >( );
614 Physical
< 2, -3 > denom
= vx
* ay
- vy
* ax
;
615 if( denom
== Physical
< 2, -3 >( 0 ) )
617 return RefCountPtr
< const Lang::Length
>( new Lang::Length( Concrete::HUGE_LENGTH
) );
619 Concrete::Speed tmp
= hypotPhysical( vx
, vy
);
620 return RefCountPtr
< const Lang::Length
>( new Lang::Length( Concrete::Length( tmp
* tmp
* tmp
/ denom
) ) );
622 if( t
>= Concrete::UNIT_TIME
)
624 throw Exceptions::InternalError( "t1 == 1 in curvature calculation" );
627 Concrete::Bezier x0
= p1
->mid_
->x_
.offtype
< 0, 3 >( );
628 Concrete::Bezier y0
= p1
->mid_
->y_
.offtype
< 0, 3 >( );
629 Concrete::Bezier x1
= p1
->front_
->x_
.offtype
< 0, 3 >( );
630 Concrete::Bezier y1
= p1
->front_
->y_
.offtype
< 0, 3 >( );
631 Concrete::Bezier x2
= p2
->rear_
->x_
.offtype
< 0, 3 >( );
632 Concrete::Bezier y2
= p2
->rear_
->y_
.offtype
< 0, 3 >( );
633 Concrete::Bezier x3
= p2
->mid_
->x_
.offtype
< 0, 3 >( );
634 Concrete::Bezier y3
= p2
->mid_
->y_
.offtype
< 0, 3 >( );
636 Concrete::Time tc
= Concrete::UNIT_TIME
- t
; /* complement to t */
637 Physical
< 0, 2 > kv0
= -3 * tc
* tc
;
638 Physical
< 0, 2 > kv1
= 3 * tc
* tc
- 6 * tc
* t
;
639 Physical
< 0, 2 > kv2
= 6 * tc
* t
- 3 * t
* t
;
640 Physical
< 0, 2 > kv3
= 3 * t
* t
;
641 Concrete::Speed vx
= x0
* kv0
+ x1
* kv1
+ x2
* kv2
+ x3
* kv3
;
642 Concrete::Speed vy
= y0
* kv0
+ y1
* kv1
+ y2
* kv2
+ y3
* kv3
;
643 Physical
< 0, 1 > ka0
= 6 * tc
;
644 Physical
< 0, 1 > ka1
= -12 * tc
+ 6 * t
;
645 Physical
< 0, 1 > ka2
= 6 * tc
- 12 * t
;
646 Physical
< 0, 1 > ka3
= 6 * t
;
647 Concrete::Acceleration ax
= x0
* ka0
+ x1
* ka1
+ x2
* ka2
+ x3
* ka3
;
648 Concrete::Acceleration ay
= y0
* ka0
+ y1
* ka1
+ y2
* ka2
+ y3
* ka3
;
650 Physical
< 2, -3 > denom
= vx
* ay
- vy
* ax
;
651 if( denom
== Physical
< 2, -3 >( 0 ) )
653 return RefCountPtr
< const Lang::Length
>( new Lang::Length( Concrete::HUGE_LENGTH
) );
655 Concrete::Speed tmp
= hypotPhysical( vx
, vy
);
656 return RefCountPtr
< const Lang::Length
>( new Lang::Length( tmp
* tmp
* tmp
/ denom
) );
659 RefCountPtr
< const Lang::Length
>
660 Lang::ElementaryPath2D::reverse_radiusOfCurvature( Concrete::Time globalTime
) const
664 throw Exceptions::OutOfRange( "The empty path has no curvature at all." );
667 const Concrete::PathPoint2D
* p1
;
668 const Concrete::PathPoint2D
* p2
;
669 findSegmentReverse( globalTime
, &t
, &p1
, &p2
);
671 if( t
<= Concrete::ZERO_TIME
)
673 throw Exceptions::InternalError( "t1 == 0 in reverse curvature calculation" );
675 if( t
>= Concrete::UNIT_TIME
)
677 /* here, t = 1, tc == 0, so
679 Concrete::Bezier x1
= p1
->front_
->x_
.offtype
< 0, 3 >( );
680 Concrete::Bezier y1
= p1
->front_
->y_
.offtype
< 0, 3 >( );
681 Concrete::Bezier x2
= p2
->rear_
->x_
.offtype
< 0, 3 >( );
682 Concrete::Bezier y2
= p2
->rear_
->y_
.offtype
< 0, 3 >( );
683 Concrete::Bezier x3
= p2
->mid_
->x_
.offtype
< 0, 3 >( );
684 Concrete::Bezier y3
= p2
->mid_
->y_
.offtype
< 0, 3 >( );
686 Concrete::Speed vx
= ( x2
* (-3) + x3
* 3 ).offtype
< 0, -2 >( );
687 Concrete::Speed vy
= ( y2
* (-3) + y3
* 3 ).offtype
< 0, -2 >( );
688 Concrete::Acceleration ax
= ( x1
* 6 + x2
* (-12) + x3
* 6 ).offtype
< 0, -1 >( );
689 Concrete::Acceleration ay
= ( y1
* 6 + y2
* (-12) + y3
* 6 ).offtype
< 0, -1 >( );
691 Physical
< 2, -3 > denom
= vx
* ay
- vy
* ax
;
692 if( denom
== Physical
< 2, -3 >( 0 ) )
694 return RefCountPtr
< const Lang::Length
>( new Lang::Length( Concrete::HUGE_LENGTH
) );
696 Concrete::Speed tmp
= hypotPhysical( vx
, vy
);
697 return RefCountPtr
< const Lang::Length
>( new Lang::Length( tmp
* tmp
* tmp
/ denom
) );
700 Concrete::Bezier x0
= p1
->mid_
->x_
.offtype
< 0, 3 >( );
701 Concrete::Bezier y0
= p1
->mid_
->y_
.offtype
< 0, 3 >( );
702 Concrete::Bezier x1
= p1
->front_
->x_
.offtype
< 0, 3 >( );
703 Concrete::Bezier y1
= p1
->front_
->y_
.offtype
< 0, 3 >( );
704 Concrete::Bezier x2
= p2
->rear_
->x_
.offtype
< 0, 3 >( );
705 Concrete::Bezier y2
= p2
->rear_
->y_
.offtype
< 0, 3 >( );
706 Concrete::Bezier x3
= p2
->mid_
->x_
.offtype
< 0, 3 >( );
707 Concrete::Bezier y3
= p2
->mid_
->y_
.offtype
< 0, 3 >( );
709 Concrete::Time tc
= Concrete::UNIT_TIME
- t
; /* complement to t */
710 Physical
< 0, 2 > kv0
= -3 * tc
* tc
;
711 Physical
< 0, 2 > kv1
= 3 * tc
* tc
- 6 * tc
* t
;
712 Physical
< 0, 2 > kv2
= 6 * tc
* t
- 3 * t
* t
;
713 Physical
< 0, 2 > kv3
= 3 * t
* t
;
714 Concrete::Speed vx
= x0
* kv0
+ x1
* kv1
+ x2
* kv2
+ x3
* kv3
;
715 Concrete::Speed vy
= y0
* kv0
+ y1
* kv1
+ y2
* kv2
+ y3
* kv3
;
716 Physical
< 0, 1 > ka0
= 6 * tc
;
717 Physical
< 0, 1 > ka1
= -12 * tc
+ 6 * t
;
718 Physical
< 0, 1 > ka2
= 6 * tc
- 12 * t
;
719 Physical
< 0, 1 > ka3
= 6 * t
;
720 Concrete::Acceleration ax
= x0
* ka0
+ x1
* ka1
+ x2
* ka2
+ x3
* ka3
;
721 Concrete::Acceleration ay
= y0
* ka0
+ y1
* ka1
+ y2
* ka2
+ y3
* ka3
;
723 Physical
< 2, -3 > denom
= vx
* ay
- vy
* ax
;
724 if( denom
== Physical
< 2, -3 >( 0 ) )
726 return RefCountPtr
< const Lang::Length
>( new Lang::Length( Concrete::HUGE_LENGTH
) );
728 Concrete::Speed tmp
= hypotPhysical( vx
, vy
);
729 return RefCountPtr
< const Lang::Length
>( new Lang::Length( tmp
* tmp
* tmp
/ denom
) );
733 Lang::ElementaryPath2D::duration( ) const
743 Lang::ElementaryPath2D::controllingMaximizer( const Lang::FloatPair
& d
, Lang::Coords2D
* dst
) const
747 throw Exceptions::OutOfRange( "The empty path cannot be maximized along." );
749 Concrete::Length opt
= -Concrete::HUGE_LENGTH
;
750 const double dx
= d
.x_
;
751 const double dy
= d
.y_
;
752 for( const_iterator i
= begin( ); i
!= end( ); ++i
)
754 if( (*i
)->rear_
!= 0 )
756 Concrete::Length y
= (*i
)->rear_
->x_
* dx
+ (*i
)->rear_
->y_
* dy
;
760 *dst
= *((*i
)->rear_
);
763 Concrete::Length y
= (*i
)->mid_
->x_
* dx
+ (*i
)->mid_
->y_
* dy
;
767 *dst
= *((*i
)->mid_
);
769 if( (*i
)->front_
!= 0 )
771 Concrete::Length y
= (*i
)->front_
->x_
* dx
+ (*i
)->front_
->y_
* dy
;
775 *dst
= *((*i
)->front_
);
779 if( opt
== -Concrete::HUGE_LENGTH
)
787 Lang::ElementaryPath2D::boundingRectangle( Concrete::Coords2D
* dstll
, Concrete::Coords2D
* dstur
) const
791 throw Exceptions::OutOfRange( "The empty path has no bounding rectangle." );
793 Concrete::Length xmin
= Concrete::HUGE_LENGTH
;
794 Concrete::Length ymin
= Concrete::HUGE_LENGTH
;
795 Concrete::Length xmax
= -Concrete::HUGE_LENGTH
;
796 Concrete::Length ymax
= -Concrete::HUGE_LENGTH
;
797 for( const_iterator i
= begin( ); i
!= end( ); ++i
)
799 if( (*i
)->rear_
!= 0 )
801 Concrete::Length x
= (*i
)->rear_
->x_
;
802 Concrete::Length y
= (*i
)->rear_
->y_
;
803 xmin
= min( xmin
, x
);
804 ymin
= min( ymin
, y
);
805 xmax
= max( xmax
, x
);
806 ymax
= max( ymax
, y
);
808 Concrete::Length x
= (*i
)->mid_
->x_
;
809 Concrete::Length y
= (*i
)->mid_
->y_
;
810 xmin
= min( xmin
, x
);
811 ymin
= min( ymin
, y
);
812 xmax
= max( xmax
, x
);
813 ymax
= max( ymax
, y
);
814 if( (*i
)->front_
!= 0 )
816 Concrete::Length x
= (*i
)->front_
->x_
;
817 Concrete::Length y
= (*i
)->front_
->y_
;
818 xmin
= min( xmin
, x
);
819 ymin
= min( ymin
, y
);
820 xmax
= max( xmax
, x
);
821 ymax
= max( ymax
, y
);
829 if( xmin
== Concrete::HUGE_LENGTH
)
837 Lang::ElementaryPath2D::lineSegmentIntersection( Concrete::Time
* dstSelf
, Concrete::Time
* dstLine
, const Concrete::Coords2D
& l0
, const Concrete::Coords2D
& l1
) const
839 Concrete::Coords2D r
= l1
- l0
;
840 Concrete::Coords2D rInv
= r
* ( 1. / Concrete::innerScalar( r
, r
) );
841 Concrete::Coords2D
n( l0
.y_
- l1
.y_
, l1
.x_
- l0
.x_
);
842 double offset
= Concrete::innerScalar( n
, l0
);
843 Concrete::Time steps
= Concrete::ZERO_TIME
;
844 const_iterator i1
= begin( );
845 const_iterator i2
= i1
;
847 Concrete::Time res
= Concrete::HUGE_TIME
;
848 for( ; i1
!= end( ); ++i1
, ++i2
, steps
+= Concrete::UNIT_TIME
)
862 if( (*i1
)->front_
== (*i1
)->mid_
&&
863 (*i2
)->rear_
== (*i2
)->mid_
)
865 Concrete::Coords2D r2
= *((*i2
)->mid_
) - *((*i1
)->mid_
);
866 Concrete::Coords2D d0
= *((*i1
)->mid_
) - l0
;
867 Concrete::LengthSquared det
= r
.y_
* r2
.x_
- r
.x_
* r2
.y_
;
868 if( fabs( Concrete::LengthSquared::offtype( det
) ) < 1e-14 )
871 if( Concrete::inner( d0
, n
.direction( ) ).abs( ) > Computation::theDistanceTol
)
873 /* The lines are separated in the normal direction */
876 double ta
= Concrete::innerScalar( rInv
, *((*i1
)->mid_
) - l0
);
877 double tb
= Concrete::innerScalar( rInv
, *((*i2
)->mid_
) - l0
);
878 bool swapped
= false;
879 if( ta
> tb
) /* Sort points ta <= tb */
892 *dstLine
= Concrete::Time( 0 );
893 *dstSelf
= steps
+ Shapes::straightLineArcTime( ( l0
- *((*i1
)->mid_
) ).norm( ) / ( *((*i2
)->mid_
) - *((*i1
)->mid_
) ).norm( ) );
898 Concrete::Time tmp
= Shapes::straightLineArcTime( ta
);
902 /* If ta and tb were swapped previously, ta corresponds to *i2, where the time is 1 past <steps>.
903 * Otherwise, ta corresponds to *i1, where the time is <steps>.
905 *dstSelf
= swapped
? ( steps
+ Concrete::UNIT_TIME
) : steps
;
911 double t1
= ( d0
.y_
* r2
.x_
- d0
.x_
* r2
.y_
) / det
;
912 if( 0 <= t1
&& t1
<= 1 )
914 double t2
= ( d0
.y_
* r
.x_
- d0
.x_
* r
.y_
) / det
;
915 if( 0 <= t2
&& t2
<= 1 )
917 Concrete::Time tmp
= Shapes::straightLineArcTime( t1
);
921 *dstSelf
= steps
+ Shapes::straightLineArcTime( t2
);
928 Bezier::ControlPoints
< Concrete::Coords2D
> controls( *(*i1
)->mid_
, *(*i1
)->front_
, *(*i2
)->rear_
, *(*i2
)->mid_
);
929 Bezier::PolyCoeffs
< Concrete::Coords2D
> coeffs( controls
);
931 coeffs
.hyperplaneIntersections( tmp2
, n
, offset
);
932 for( const double * it2
= tmp2
; *it2
!= HUGE_VAL
; ++it2
)
934 Concrete::Time t
= Shapes::straightLineArcTime( Concrete::innerScalar( rInv
, coeffs
.point( *it2
) - l0
) );
935 if( Concrete::ZERO_TIME
<= t
&& t
<= Concrete::UNIT_TIME
)
940 *dstSelf
= steps
+ *it2
;
946 if( res
< Concrete::HUGE_TIME
)
954 RefCountPtr
< const Lang::Coords2D
>
955 Lang::ElementaryPath2D::controllingMaximizer( const Lang::FloatPair
& d
) const
957 Lang::Coords2D
* res
= new Lang::Coords2D( Concrete::ZERO_LENGTH
, Concrete::ZERO_LENGTH
);
958 controllingMaximizer( d
, res
);
959 return RefCountPtr
< const Lang::Coords2D
>( res
);
962 RefCountPtr
< const Lang::Coords2D
>
963 Lang::ElementaryPath2D::discreteMean( ) const
966 Concrete::Length
x( 0 );
967 Concrete::Length
y( 0 );
970 throw Exceptions::OutOfRange( "The empty path cannot be averaged along." );
972 for( const_iterator i
= begin( ); i
!= end( ); ++i
)
974 if( (*i
)->rear_
!= 0 )
977 x
= x
+ (*i
)->rear_
->x_
;
978 y
= y
+ (*i
)->rear_
->y_
;
981 x
= x
+ (*i
)->mid_
->x_
;
982 y
= y
+ (*i
)->mid_
->y_
;
983 if( (*i
)->front_
!= 0 )
986 x
= x
+ (*i
)->front_
->x_
;
987 y
= y
+ (*i
)->front_
->y_
;
990 return RefCountPtr
< const Lang::Coords2D
>( new Lang::Coords2D( x
/ count
, y
/ count
) );
994 Lang::ElementaryPath2D::discreteMaximizer( const Lang::FloatPair
& d
) const
998 throw Exceptions::OutOfRange( "The empty path cannot be maximized along." );
1000 Concrete::Length opt
= -Concrete::HUGE_LENGTH
;
1001 const double dx
= d
.x_
;
1002 const double dy
= d
.y_
;
1003 Concrete::SplineTime res
= Concrete::ZERO_TIME
;
1004 Concrete::SplineTime t
= Concrete::ZERO_TIME
;
1005 for( const_iterator i
= begin( ); i
!= end( ); ++i
, ++t
)
1007 Concrete::Length y
= (*i
)->mid_
->x_
* dx
+ (*i
)->mid_
->y_
* dy
;
1017 Concrete::SplineTime
1018 Lang::ElementaryPath2D::discreteApproximator( const Lang::Coords2D
& p
) const
1022 throw Exceptions::OutOfRange( "The empty path cannot be maximized along." );
1024 Concrete::Length bestDist
= Concrete::HUGE_LENGTH
;
1025 const Concrete::Length px
= p
.x_
.get( );
1026 const Concrete::Length py
= p
.y_
.get( );
1027 Concrete::SplineTime res
= Concrete::ZERO_TIME
;
1028 Concrete::SplineTime t
= Concrete::ZERO_TIME
;
1029 for( const_iterator i
= begin( ); i
!= end( ); ++i
, ++t
)
1031 Concrete::Length dist
= hypotPhysical( (*i
)->mid_
->x_
- px
, (*i
)->mid_
->y_
- py
);
1032 if( dist
< bestDist
)
1041 RefCountPtr
< const Lang::Coords2D
>
1042 Lang::ElementaryPath2D::continuousMean( ) const
1046 throw Exceptions::OutOfRange( "The empty path cannot be averaged along." );
1048 Physical
< 2, 0 > intx( 0 );
1049 Physical
< 2, 0 > inty( 0 );
1050 Physical
< 1, 0 > totlen( 0 );
1052 const_iterator i1
= begin( );
1053 const_iterator i2
= i1
;
1055 for( ; i1
!= end( ); ++i1
, ++i2
)
1069 if( (*i1
)->front_
== (*i1
)->mid_
&&
1070 (*i2
)->rear_
== (*i2
)->mid_
)
1072 Concrete::Length x0
= (*i1
)->mid_
->x_
;
1073 Concrete::Length y0
= (*i1
)->mid_
->y_
;
1074 Concrete::Length x3
= (*i2
)->mid_
->x_
;
1075 Concrete::Length y3
= (*i2
)->mid_
->y_
;
1076 Concrete::Length len
= hypotPhysical( (x3
-x0
), (y3
-y0
) );
1077 intx
+= len
* 0.5 * ( x0
+ x3
);
1078 inty
+= len
* 0.5 * ( y0
+ y3
);
1083 Concrete::Bezier x0
= (*i1
)->mid_
->x_
.offtype
< 0, 3 >( );
1084 Concrete::Bezier y0
= (*i1
)->mid_
->y_
.offtype
< 0, 3 >( );
1085 Concrete::Bezier x1
= (*i1
)->front_
->x_
.offtype
< 0, 3 >( );
1086 Concrete::Bezier y1
= (*i1
)->front_
->y_
.offtype
< 0, 3 >( );
1087 Concrete::Bezier x2
= (*i2
)->rear_
->x_
.offtype
< 0, 3 >( );
1088 Concrete::Bezier y2
= (*i2
)->rear_
->y_
.offtype
< 0, 3 >( );
1089 Concrete::Bezier x3
= (*i2
)->mid_
->x_
.offtype
< 0, 3 >( );
1090 Concrete::Bezier y3
= (*i2
)->mid_
->y_
.offtype
< 0, 3 >( );
1092 Concrete::Time dt
= Shapes::computeDt( hypotPhysical( (x1
-x0
), (y1
-y0
) ).offtype
< 0, -3 >( ) +
1093 hypotPhysical( (x2
-x1
), (y2
-y1
) ).offtype
< 0, -3 >( ) +
1094 hypotPhysical( (x3
-x2
), (y3
-y2
) ).offtype
< 0, -3 >( ) );
1096 Physical
< 2, 0 > tmpSum_x( 0 );
1097 Physical
< 2, 0 > tmpSum_y( 0 );
1098 Physical
< 1, 0 > tmpSum_l( 0 );
1099 for( Concrete::Time t
= Concrete::ZERO_TIME
; t
< Concrete::UNIT_TIME
; t
+= dt
)
1101 Concrete::Time tc
= Concrete::UNIT_TIME
- t
; /* complement to t */
1102 Physical
< 0, 3 > k0
= tc
* tc
* tc
;
1103 Physical
< 0, 3 > k1
= 3 * tc
* tc
* t
;
1104 Physical
< 0, 3 > k2
= 3 * tc
* t
* t
;
1105 Physical
< 0, 3 > k3
= t
* t
* t
;
1106 Concrete::Length x
= x0
* k0
+ x1
* k1
+ x2
* k2
+ x3
* k3
;
1107 Concrete::Length y
= y0
* k0
+ y1
* k1
+ y2
* k2
+ y3
* k3
;
1108 Physical
< 0, 2 > kv0
= -3 * tc
* tc
;
1109 Physical
< 0, 2 > kv1
= 3 * tc
* tc
- 6 * tc
* t
;
1110 Physical
< 0, 2 > kv2
= 6 * tc
* t
- 3 * t
* t
;
1111 Physical
< 0, 2 > kv3
= 3 * t
* t
;
1112 Concrete::Speed vx
= x0
* kv0
+ x1
* kv1
+ x2
* kv2
+ x3
* kv3
;
1113 Concrete::Speed vy
= y0
* kv0
+ y1
* kv1
+ y2
* kv2
+ y3
* kv3
;
1115 Concrete::Length dl
= hypotPhysical( vx
, vy
).offtype
< 0, -1 >( );
1120 intx
+= tmpSum_x
* dt
.offtype
< 0, 1 >( );
1121 inty
+= tmpSum_y
* dt
.offtype
< 0, 1 >( );
1122 totlen
+= tmpSum_l
* dt
.offtype
< 0, 1 >( );
1125 return RefCountPtr
< const Lang::Coords2D
>( new Lang::Coords2D( intx
/ totlen
, inty
/ totlen
) );
1128 Concrete::SplineTime
1129 Lang::ElementaryPath2D::continuousMaximizer( const Lang::FloatPair
& dNonUnit
) const
1133 throw Exceptions::OutOfRange( "The empty path cannot be maximized along." );
1136 Concrete::UnitFloatPair
d( dNonUnit
.x_
, dNonUnit
.y_
);
1137 Concrete::Coords2D
dBezier( d
.x_
, d
.y_
); // The Bezier functions requires the direction to be given with the same type as the spline space.
1139 Concrete::SplineTime res
= Concrete::ZERO_TIME
;
1140 Concrete::Length opt
= -Concrete::HUGE_LENGTH
;
1141 Concrete::Time steps
= Concrete::ZERO_TIME
;
1142 const_iterator i1
= begin( );
1143 const_iterator i2
= i1
;
1145 for( ; i1
!= end( ); ++i1
, ++i2
, steps
+= Concrete::UNIT_TIME
)
1155 Concrete::Length v
= Concrete::inner( *(*i1
)->mid_
, d
);
1164 Concrete::Length v
= Concrete::inner( *(*i1
)->mid_
, d
);
1171 /* if the segment is straight, checking its end points is enough.
1173 if( (*i1
)->front_
== (*i1
)->mid_
&&
1174 (*i2
)->rear_
== (*i2
)->mid_
)
1179 /* First check the control points. If none of the four control points improves the optimum,
1180 * searching further is meaningless. Further, since the mid_ points are allways tested anyway,
1181 * we can assume that they already are, and conclude that they cannot improve the optimum further.
1182 * Therefore, there are only the two handles to check.
1185 if( Concrete::inner( *(*i1
)->front_
, d
) <= opt
&&
1186 Concrete::inner( *(*i2
)->rear_
, d
) <= opt
)
1192 Bezier::ControlPoints
< Concrete::Coords2D
> controls( *(*i1
)->mid_
, *(*i1
)->front_
, *(*i2
)->rear_
, *(*i2
)->mid_
);
1193 Bezier::PolyCoeffs
< Concrete::Coords2D
> coeffs( controls
);
1195 coeffs
.stationaryPoints( optTimes
, dBezier
); // A HUGE_VAL is used as terminator in the result.
1197 for( double * src
= & optTimes
[0]; *src
!= HUGE_VAL
; ++src
)
1199 Concrete::Length v
= Concrete::inner( coeffs
.point( *src
), d
);
1213 class ApproximationPoly2D
1215 Concrete::Coords2D p_
;
1216 Bezier::PolyCoeffs
< Concrete::Coords2D
> polyCoeffs_
;
1217 Physical
< 1, 0 > kxD0_0_
;
1218 Physical
< 1, -1 > kxD0_1_
;
1219 Physical
< 1, -2 > kxD0_2_
;
1220 Physical
< 1, -3 > kxD0_3_
;
1221 Physical
< 1, -1 > kxD1_0_
;
1222 Physical
< 1, -2 > kxD1_1_
;
1223 Physical
< 1, -3 > kxD1_2_
;
1224 Physical
< 1, -2 > kxD2_0_
;
1225 Physical
< 1, -3 > kxD2_1_
;
1227 Physical
< 1, 0 > kyD0_0_
;
1228 Physical
< 1, -1 > kyD0_1_
;
1229 Physical
< 1, -2 > kyD0_2_
;
1230 Physical
< 1, -3 > kyD0_3_
;
1231 Physical
< 1, -1 > kyD1_0_
;
1232 Physical
< 1, -2 > kyD1_1_
;
1233 Physical
< 1, -3 > kyD1_2_
;
1234 Physical
< 1, -2 > kyD2_0_
;
1235 Physical
< 1, -3 > kyD2_1_
;
1237 Concrete::Speed maxSpeed_
;
1239 ApproximationPoly2D( const Concrete::Coords2D
& p0
, const Concrete::Coords2D
& p1
, const Concrete::Coords2D
& p2
, const Concrete::Coords2D
& p3
, const Concrete::Coords2D
& p
);
1240 Concrete::Time
splitTime( const Concrete::Time t_low
, const Concrete::Time t_high
, const Concrete::Time t_tol
) const;
1241 bool isCircular( ) const;
1242 Concrete::Length
distanceAt( Concrete::Time t
) const;
1243 Concrete::LengthSquared
squaredDistanceAt( Concrete::Time t
) const;
1244 Concrete::Speed
maxSpeed( ) const { return maxSpeed_
; }
1245 Bezier::ControlPoints
< Concrete::Coords2D
> getControls( const Concrete::Time t_low
, const Concrete::Time t_high
) const;
1246 const Concrete::Coords2D
& getPoint( ) const;
1248 class ApproximationSegmentSection2D
1250 Bezier::ControlPoints
< Concrete::Coords2D
> controls_
;
1251 const ApproximationPoly2D
* baseSeg_
;
1252 Concrete::Time steps_
;
1257 ApproximationSegmentSection2D( const Shapes::ApproximationPoly2D
* baseSeg
, Concrete::Time steps
, Concrete::Time t0
, Concrete::Time t1
);
1258 ApproximationSegmentSection2D
* cutAfter( Concrete::Time t
) const;
1259 ApproximationSegmentSection2D
* cutBefore( Concrete::Time t
) const;
1261 Concrete::Length
convexHullDistance( ) const;
1262 Concrete::Time
splitTime( const Concrete::Time t_tol
) const;
1263 Concrete::Time
globalTime( Concrete::Time t
) const;
1264 Concrete::Length
distanceAt( Concrete::Time t
) const;
1265 Concrete::Speed
maxSpeed( ) const;
1266 Concrete::Time
duration( ) const;
1270 Concrete::SplineTime
1271 Lang::ElementaryPath2D::continuousApproximator( const Lang::Coords2D
& coordPoint
) const
1273 const Concrete::Length DISTANCE_TOL
= 0.001 * Computation::the_arcdelta
;
1277 throw Exceptions::OutOfRange( "The empty path cannot be approximated along." );
1280 Concrete::Coords2D
p( coordPoint
.x_
.get( ), coordPoint
.y_
.get( ) );
1281 PtrOwner_back_Access
< std::list
< const Shapes::ApproximationPoly2D
* > > memory
;
1282 typedef std::pair
< Shapes::ApproximationSegmentSection2D
*, Concrete::Length
> WorkItem
;
1283 std::list
< WorkItem
> work
;
1285 Concrete::SplineTime res
= Concrete::ZERO_TIME
;
1286 Concrete::Length bestDist
= Concrete::HUGE_LENGTH
;
1287 Concrete::Time steps
= Concrete::ZERO_TIME
;
1288 const_iterator i1
= begin( );
1289 const_iterator i2
= i1
;
1291 for( ; i1
!= end( ); ++i1
, ++i2
, steps
+= Concrete::UNIT_TIME
)
1301 Concrete::Length dist
= hypotPhysical( (*i1
)->mid_
->x_
- p
.x_
, (*i1
)->mid_
->y_
- p
.y_
);
1302 if( dist
< bestDist
)
1311 Concrete::Length dist
= hypotPhysical( (*i1
)->mid_
->x_
- p
.x_
, (*i1
)->mid_
->y_
- p
.y_
);
1312 if( dist
< bestDist
)
1318 Concrete::Bezier x0
= (*i1
)->mid_
->x_
.offtype
< 0, 3 >( );
1319 Concrete::Bezier y0
= (*i1
)->mid_
->y_
.offtype
< 0, 3 >( );
1320 Concrete::Bezier x3
= (*i2
)->mid_
->x_
.offtype
< 0, 3 >( );
1321 Concrete::Bezier y3
= (*i2
)->mid_
->y_
.offtype
< 0, 3 >( );
1323 /* if the segment is straight, finding the closest point is a linear least-squares problem.
1325 if( (*i1
)->front_
== (*i1
)->mid_
&&
1326 (*i2
)->rear_
== (*i2
)->mid_
)
1328 const Concrete::Length tx
= ( x3
- x0
).offtype
< 0, -3 >( );
1329 const Concrete::Length ty
= ( y3
- y0
).offtype
< 0, -3 >( );
1330 double s
= ( tx
* ( p
.x_
- x0
.offtype
< 0, -3 >( ) ) + ty
* ( p
.y_
- y0
.offtype
< 0, -3 >( ) ) ) / ( tx
*tx
+ ty
*ty
);
1331 if( 0 < s
&& s
< 1 )
1333 Concrete::Length dist
= hypotPhysical( x0
.offtype
< 0, -3 >( ) + s
* tx
- p
.x_
, y0
.offtype
< 0, -3 >( ) + s
* ty
- p
.y_
);
1334 if( dist
< bestDist
)
1337 res
= steps
+ Shapes::straightLineArcTime( s
);
1343 Shapes::ApproximationPoly2D
* coeffs
=
1344 new Shapes::ApproximationPoly2D( *((*i1
)->mid_
), *((*i1
)->front_
), *((*i2
)->rear_
), *((*i2
)->mid_
), p
);
1345 Shapes::ApproximationSegmentSection2D
* seg
= new Shapes::ApproximationSegmentSection2D( coeffs
, steps
, Concrete::ZERO_TIME
, Concrete::UNIT_TIME
);
1346 Concrete::Length lowerBound
= seg
->convexHullDistance( ) + DISTANCE_TOL
;
1347 if( lowerBound
>= bestDist
)
1354 /* Here, we must detect the ill-contditionned case of center-to-circle. This is detected
1355 * by checking the distance at four different points.
1358 if( coeffs
->isCircular( ) )
1360 Concrete::Length dist
= coeffs
->distanceAt( Concrete::ZERO_TIME
);
1361 if( dist
< bestDist
)
1372 memory
.push_back( coeffs
);
1373 work
.push_back( WorkItem( seg
, lowerBound
) );
1376 while( ! work
.empty( ) )
1378 Shapes::ApproximationSegmentSection2D
* seg
= work
.front( ).first
;
1379 Concrete::Length lowerBound
= work
.front( ).second
;
1381 if( lowerBound
< bestDist
)
1383 const Concrete::Time t_tol
= Computation::the_arcdelta
/ seg
->maxSpeed( );
1384 Concrete::Time split
= seg
->splitTime( 0.001 * t_tol
); // increased precicion seems to have no influence on computation time
1385 Concrete::Length dist
= seg
->distanceAt( split
);
1386 if( dist
< bestDist
)
1389 res
= seg
->globalTime( split
);
1391 if( lowerBound
< bestDist
&&
1392 t_tol
< seg
->duration( ) )
1395 Shapes::ApproximationSegmentSection2D
* part
= seg
->cutAfter( split
);
1396 Concrete::Length lowerBound
= part
->convexHullDistance( ) + DISTANCE_TOL
;
1397 if( lowerBound
>= bestDist
)
1403 work
.push_back( WorkItem( part
, lowerBound
) );
1407 Shapes::ApproximationSegmentSection2D
* part
= seg
->cutBefore( split
);
1408 Concrete::Length lowerBound
= part
->convexHullDistance( ) + DISTANCE_TOL
;
1409 if( lowerBound
>= bestDist
)
1415 work
.push_back( WorkItem( part
, lowerBound
) );
1426 Shapes::ApproximationPoly2D::ApproximationPoly2D( const Concrete::Coords2D
& p0
, const Concrete::Coords2D
& p1
, const Concrete::Coords2D
& p2
, const Concrete::Coords2D
& p3
, const Concrete::Coords2D
& p
)
1427 : p_( p
), polyCoeffs_( Bezier::PolyCoeffs
< Concrete::Coords2D
>( Bezier::ControlPoints
< Concrete::Coords2D
>( p0
, p1
, p2
, p3
) ) )
1429 kxD0_0_
= polyCoeffs_
.z0_
.x_
- p
.x_
;
1430 kxD0_1_
= polyCoeffs_
.z1_
.x_
.offtype
< 0, 1 >( );
1431 kxD0_2_
= polyCoeffs_
.z2_
.x_
.offtype
< 0, 2 >( );
1432 kxD0_3_
= polyCoeffs_
.z3_
.x_
.offtype
< 0, 3 >( );
1435 kxD1_1_
= 2 * kxD0_2_
;
1436 kxD1_2_
= 3 * kxD0_3_
;
1439 kxD2_1_
= 2 * kxD1_2_
;
1441 kyD0_0_
= polyCoeffs_
.z0_
.y_
- p
.y_
;
1442 kyD0_1_
= polyCoeffs_
.z1_
.y_
.offtype
< 0, 1 >( );
1443 kyD0_2_
= polyCoeffs_
.z2_
.y_
.offtype
< 0, 2 >( );
1444 kyD0_3_
= polyCoeffs_
.z3_
.y_
.offtype
< 0, 3 >( );
1447 kyD1_1_
= 2 * kyD0_2_
;
1448 kyD1_2_
= 3 * kyD0_3_
;
1451 kyD2_1_
= 2 * kyD1_2_
;
1453 Concrete::Length tmp
= max( hypotPhysical( p1
.x_
- p0
.x_
, p1
.y_
- p0
.y_
),
1454 hypotPhysical( p2
.x_
- p1
.x_
, p2
.y_
- p1
.y_
) );
1455 maxSpeed_
= max( tmp
, hypotPhysical( p3
.x_
- p2
.x_
, p3
.y_
- p2
.y_
) ).offtype
< 0, 1 >( );
1459 Shapes::ApproximationPoly2D::splitTime( const Concrete::Time t_low
, const Concrete::Time t_high
, const Concrete::Time t_tol
) const
1462 Concrete::LengthSquared
last_f( HUGE_VAL
);
1464 const double GRID
= 7;
1465 const Concrete::Time STEP
= ( t_high
- t_low
) / GRID
;
1466 for( Concrete::Time tt
= t_low
+ 0.5 * STEP
; tt
< t_high
; tt
+= STEP
)
1468 Concrete::LengthSquared tmp
= squaredDistanceAt( tt
);
1476 Concrete::Time last_t
= - Concrete::UNIT_TIME
;
1477 bool lastOffBounds
= false;
1479 while( t
< last_t
- t_tol
|| t
> last_t
+ t_tol
)
1482 Physical
< 2, -1 > objectiveD1( 0 ); // these are actually computed for the objective divided by two
1483 Physical
< 2, -2 > objectiveD2( 0 ); // but since we will divide one by the other, it doesn't matter.
1487 Physical
< 1, 0 > fD0
= kxD0_0_
+ t
* ( kxD0_1_
+ t
* ( kxD0_2_
+ t
* kxD0_3_
) );
1488 Physical
< 1, -1 > fD1
= kxD1_0_
+ t
* ( kxD1_1_
+ t
* kxD1_2_
);
1489 Physical
< 1, -2 > fD2
= kxD2_0_
+ t
* kxD2_1_
;
1490 objectiveD1
+= fD0
* fD1
;
1491 objectiveD2
+= fD1
* fD1
+ fD0
* fD2
;
1496 Physical
< 1, 0 > fD0
= kyD0_0_
+ t
* ( kyD0_1_
+ t
* ( kyD0_2_
+ t
* kyD0_3_
) );
1497 Physical
< 1, -1 > fD1
= kyD1_0_
+ t
* ( kyD1_1_
+ t
* kyD1_2_
);
1498 Physical
< 1, -2 > fD2
= kyD2_0_
+ t
* kyD2_1_
;
1499 objectiveD1
+= fD0
* fD1
;
1500 objectiveD2
+= fD1
* fD1
+ fD0
* fD2
;
1503 Concrete::Time step
;
1504 if( objectiveD2
<= Physical
< 2, -2 >( 0 ) )
1506 /* The minimization problem is not convex, locally.
1507 * Go to one of the extremes.
1509 if( objectiveD1
< Physical
< 2, -1 >( 0 ) )
1511 step
= 1.1 * ( t_high
- t
);
1515 step
= 1.1 * ( t_low
- t
);
1520 step
= -objectiveD1
/ objectiveD2
; // here's the division where the missing factors of 2 cancel.
1521 if( t
+ step
< t_low
)
1523 step
= 1.1 * ( t_low
- t
); // 1.1 to make sure that the off-bounds test detects minima outside [ t_low t_high ]
1525 else if( t
+ step
> t_high
)
1527 step
= 1.1 * ( t_high
- t
); // 1.1 to make sure that the off-bounds test detects minima outside [ t_low t_high ]
1531 Concrete::LengthSquared f
= squaredDistanceAt( t
+ step
);
1532 if( step
> Concrete::ZERO_TIME
)
1534 while( f
> last_f
&& step
> t_tol
)
1537 f
= squaredDistanceAt( t
+ step
);
1542 while( f
> last_f
&& step
< -t_tol
)
1545 f
= squaredDistanceAt( t
+ step
);
1560 lastOffBounds
= true;
1563 else if( t
> t_high
)
1572 lastOffBounds
= true;
1577 lastOffBounds
= false;
1583 if( t
> 0.5 * ( t_low
+ t_high
) )
1593 Bezier::ControlPoints
< Concrete::Coords2D
>
1594 Shapes::ApproximationPoly2D::getControls( const Concrete::Time t_low
, const Concrete::Time t_high
) const
1596 return Bezier::ControlPoints
< Concrete::Coords2D
>( polyCoeffs_
.subSection( t_low
.offtype
< 0, 1 >( ), t_high
.offtype
< 0, 1 >( ) ) );
1600 Shapes::ApproximationPoly2D::isCircular( ) const
1602 Concrete::Length rmax
= -Concrete::HUGE_LENGTH
;
1603 Concrete::Length rmin
= Concrete::HUGE_LENGTH
;
1604 for( Concrete::Time t
= Concrete::ZERO_TIME
; t
< Concrete::Time( 1.05 ); t
+= Concrete::Time( 0.33333 ) )
1606 Concrete::Length r
= distanceAt( t
);
1607 rmax
= max( rmax
, r
);
1608 rmin
= min( rmin
, r
);
1610 return rmax
<= rmin
* 1.0001;
1613 const Concrete::Coords2D
&
1614 Shapes::ApproximationPoly2D::getPoint( ) const
1621 Shapes::ApproximationPoly2D::distanceAt( Concrete::Time t
) const
1623 return hypotPhysical( kxD0_0_
+ t
* ( kxD0_1_
+ t
* ( kxD0_2_
+ t
* kxD0_3_
) ),
1624 kyD0_0_
+ t
* ( kyD0_1_
+ t
* ( kyD0_2_
+ t
* kyD0_3_
) ) );
1627 Concrete::LengthSquared
1628 Shapes::ApproximationPoly2D::squaredDistanceAt( Concrete::Time t
) const
1630 Concrete::Length deltax
= kxD0_0_
+ t
* ( kxD0_1_
+ t
* ( kxD0_2_
+ t
* kxD0_3_
) );
1631 Concrete::Length deltay
= kyD0_0_
+ t
* ( kyD0_1_
+ t
* ( kyD0_2_
+ t
* kyD0_3_
) );
1632 return deltax
* deltax
+ deltay
* deltay
;
1635 Shapes::ApproximationSegmentSection2D::ApproximationSegmentSection2D( const Shapes::ApproximationPoly2D
* baseSeg
, Concrete::Time steps
, Concrete::Time t0
, Concrete::Time t1
)
1636 : controls_( baseSeg
->getControls( t0
, t1
) ), baseSeg_( baseSeg
), steps_( steps
), t0_( t0
), t1_( t1
)
1639 Shapes::ApproximationSegmentSection2D
*
1640 Shapes::ApproximationSegmentSection2D::cutAfter( Concrete::Time t
) const
1642 return new ApproximationSegmentSection2D( baseSeg_
, steps_
, t0_
, t
);
1645 Shapes::ApproximationSegmentSection2D
*
1646 Shapes::ApproximationSegmentSection2D::cutBefore( Concrete::Time t
) const
1648 return new ApproximationSegmentSection2D( baseSeg_
, steps_
, t
, t1_
);
1652 Shapes::ApproximationSegmentSection2D::convexHullDistance( ) const
1654 Concrete::Coords2D
p( baseSeg_
->getPoint( ) );
1656 std::vector
< const Concrete::Coords2D
* > pointSet( 0 );
1657 pointSet
.reserve( 4 );
1658 pointSet
.push_back( & controls_
.p0_
);
1659 pointSet
.push_back( & controls_
.p1_
);
1660 pointSet
.push_back( & controls_
.p2_
);
1661 pointSet
.push_back( & controls_
.p3_
);
1663 Concrete::Length res
= Concrete::HUGE_LENGTH
;
1665 for( std::vector
< const Concrete::Coords2D
* >::const_iterator i0
= pointSet
.begin( ); i0
!= pointSet
.end( ); ++i0
)
1667 std::vector
< const Concrete::Coords2D
* >::const_iterator i1
= i0
;
1669 for( ; i1
!= pointSet
.end( ); ++i1
)
1671 const Concrete::Coords2D
& p0( **i0
);
1672 const Concrete::Coords2D
& p1( **i1
);
1674 /* The two points bound the convex hull iff the other points are on the same side of the segment.
1675 * If they are on the same side and p is on the other side, then the distance to p is of interest.
1677 const Concrete::Length tx
= p1
.x_
- p0
.x_
;
1678 const Concrete::Length ty
= p1
.y_
- p0
.y_
;
1679 bool counterClockwise
; // true when ( p0, p1 ) are ordered counter-clockwise around the interior.
1681 std::vector
< const Concrete::Coords2D
* >::const_iterator i2
= pointSet
.begin( );
1682 for( ; ; ++i2
) // we don't have to check against pointSet.end( )
1684 if( i2
== i0
|| i2
== i1
)
1688 const Concrete::Length dx
= (*i2
)->x_
- p0
.x_
;
1689 const Concrete::Length dy
= (*i2
)->y_
- p0
.y_
;
1690 counterClockwise
= ( ty
* dx
- tx
* dy
< Concrete::LengthSquared( 0 ) );
1694 for( ; ; ++i2
) // we don't have to check against pointSet.end( )
1696 if( i2
== i0
|| i2
== i1
)
1700 const Concrete::Length dx
= (*i2
)->x_
- p0
.x_
;
1701 const Concrete::Length dy
= (*i2
)->y_
- p0
.y_
;
1702 if( counterClockwise
== ( ty
* dx
- tx
* dy
< Concrete::LengthSquared( 0 ) ) )
1708 continue; // the points where on different sides.
1711 const Concrete::Length dx
= p
.x_
- p0
.x_
;
1712 const Concrete::Length dy
= p
.y_
- p0
.y_
;
1713 if( ( ty
* dx
- tx
* dy
> Concrete::LengthSquared( 0 ) ) == counterClockwise
)
1715 double s
= ( tx
* dx
+ ty
* dy
) / ( tx
*tx
+ ty
*ty
);
1716 Concrete::Length dist
;
1719 dist
= hypotPhysical( p0
.x_
- p
.x_
, p0
.y_
- p
.y_
);
1723 dist
= hypotPhysical( p1
.x_
- p
.x_
, p1
.y_
- p
.y_
);
1727 dist
= hypotPhysical( s
* tx
- dx
, s
* ty
- dy
);
1729 res
= min( res
, dist
);
1735 if( res
== Concrete::HUGE_LENGTH
)
1737 /* This means that p was on the inside of each line, meaning it
1738 * is also inside the convex hull.
1740 return Concrete::ZERO_LENGTH
;
1746 Shapes::ApproximationSegmentSection2D::splitTime( const Concrete::Time t_tol
) const
1748 Concrete::Time d
= 0.2 * ( t1_
- t0_
);
1749 return baseSeg_
->splitTime( t0_
+ d
, t1_
- d
, t_tol
);
1753 Shapes::ApproximationSegmentSection2D::globalTime( Concrete::Time t
) const
1759 Shapes::ApproximationSegmentSection2D::distanceAt( Concrete::Time t
) const
1761 return baseSeg_
->distanceAt( t
);
1765 Shapes::ApproximationSegmentSection2D::maxSpeed( ) const
1767 return baseSeg_
->maxSpeed( );
1771 Shapes::ApproximationSegmentSection2D::duration( ) const
1778 class HullSorter2D
: public std::binary_function
< const Concrete::Coords2D
*, const Concrete::Coords2D
*, bool >
1780 const Concrete::Coords2D
& p_
;
1782 HullSorter2D( const Concrete::Coords2D
& p
) : p_( p
) { }
1783 bool operator () ( const Concrete::Coords2D
* p1
, const Concrete::Coords2D
* p2
)
1785 /* The comparison orders the points according to their counter-clockwise
1786 * angle to the positive x-axis.
1787 * Since (by construction) no point can have smaller y-coordinate than p, all angles
1788 * are in the range [ 0, pi ], and it suffices to reason in terms of the argument
1789 * to the monotonic function arccotan.
1790 * The ordinary counter-clockwise-of by means of computing the projection of the
1791 * second on the clockwise normal of the other leads to the same equation.
1793 const Concrete::Length d1x
= p1
->x_
- p_
.x_
;
1794 const Concrete::Length d1y
= p1
->y_
- p_
.y_
;
1795 const Concrete::Length d2x
= p2
->x_
- p_
.x_
;
1796 const Concrete::Length d2y
= p2
->y_
- p_
.y_
;
1797 return d1y
* d2x
< d2y
* d1x
;
1802 RefCountPtr
< const Lang::ElementaryPath2D
>
1803 Lang::ElementaryPath2D::controlling_hull( ) const
1807 return Lang::THE_EMPTYPATH2D
;
1812 Lang::ElementaryPath2D
* res
= new Lang::ElementaryPath2D( );
1813 const_iterator i
= begin( );
1814 res
->push_back( new Concrete::PathPoint2D( new Concrete::Coords2D( *((*i
)->mid_
) ) ) );
1817 if( (*i
)->front_
!= (*i
)->mid_
)
1819 res
->push_back( new Concrete::PathPoint2D( new Concrete::Coords2D( *((*i
)->front_
) ) ) );
1821 if( (*i
)->rear_
!= (*i
)->mid_
)
1823 res
->push_back( new Concrete::PathPoint2D( new Concrete::Coords2D( *((*i
)->rear_
) ) ) );
1827 return RefCountPtr
< const Lang::ElementaryPath2D
>( res
);
1830 /* Compute convex hull using Graham scan algorithm.
1831 * The stack of points in the hull is split into the last point and the rest.
1834 std::vector
< const Concrete::Coords2D
* > sortedPoints( 0 );
1835 sortedPoints
.reserve( 3 * size( ) );
1837 /* Step 0: Place all distinct points in sortedPoints. The sort will take place soon...
1840 const_iterator i
= begin( );
1843 if( (*i
)->rear_
!= (*i
)->mid_
)
1845 sortedPoints
.push_back( (*i
)->rear_
);
1848 sortedPoints
.push_back( (*i
)->mid_
);
1849 if( (*i
)->front_
!= (*i
)->mid_
)
1851 sortedPoints
.push_back( (*i
)->front_
);
1855 const_iterator theEnd
= end( );
1857 for( ; i
!= theEnd
; ++i
)
1859 if( (*i
)->rear_
!= (*i
)->mid_
)
1861 sortedPoints
.push_back( (*i
)->rear_
);
1863 sortedPoints
.push_back( (*i
)->mid_
);
1864 if( (*i
)->front_
!= (*i
)->mid_
)
1866 sortedPoints
.push_back( (*i
)->front_
);
1869 if( (*i
)->rear_
!= (*i
)->mid_
)
1871 sortedPoints
.push_back( (*i
)->rear_
);
1873 sortedPoints
.push_back( (*i
)->mid_
);
1876 if( (*i
)->front_
!= (*i
)->mid_
)
1878 sortedPoints
.push_back( (*i
)->front_
);
1883 /* Step 1: Find leftmost of the lowest vertexes. The sort will take place very soon...
1885 const Concrete::Coords2D
* last
;
1886 const Concrete::Coords2D
* start
;
1888 std::vector
< const Concrete::Coords2D
* >::iterator i
= sortedPoints
.begin( );
1889 std::vector
< const Concrete::Coords2D
* >::iterator start_i
= i
;
1892 for( ; i
!= sortedPoints
.end( ); ++i
)
1894 if( (*i
)->y_
> start
->y_
)
1898 if( (*i
)->y_
< start
->y_
)
1904 if( (*i
)->x_
>= start
->x_
)
1912 // Remove the starting point from the set by moving the last point to its position.
1913 *start_i
= sortedPoints
.back( );
1914 sortedPoints
.pop_back( );
1917 /* Step 2: Sort remaining points.
1919 std::sort( sortedPoints
.begin( ), sortedPoints
.end( ), Shapes::HullSorter2D( *start
) );
1921 /* Step 3: Construct hull. The pointers are first placed in a list, and only copied to the new path once we know
1922 * what points belong to the hull.
1924 const Concrete::LengthSquared
TOL( 1e-20 );
1925 Lang::ElementaryPath2D
* res
= new Lang::ElementaryPath2D( );
1927 std::list
< const Concrete::Coords2D
* > hullPoints
;
1928 for( std::vector
< const Concrete::Coords2D
* >::const_iterator i
= sortedPoints
.begin( );
1929 i
!= sortedPoints
.end( );
1932 /* Don't trust points that are too close to the starting point, since
1933 * the angle sort is not reliable for these.
1935 if( ( **i
- *start
).normSquared( ) < TOL
)
1939 while( ! hullPoints
.empty( ) )
1941 /* Avoid including points that are too close to each other, as this makes
1942 * the direction between the points ill-defined.
1944 if( ( **i
- *last
).normSquared( ) < TOL
)
1946 last
= hullPoints
.back( );
1947 hullPoints
.pop_back( );
1951 /* In case the two points are on a line from the starting point, always keep the point
1952 * with the greatest distance to the starting point, and never keep the point
1953 * with the smaller distance -- this takes care of the degenerate
1954 * situation when the ordering in the previous sort was unclear.
1956 Concrete::Coords2D dLast
= *last
- *start
;
1957 Concrete::Coords2D di
= **i
- *start
;
1958 if( dLast
.angleCosineSignSquared( di
) > 1 - 1e-8 ) /* Test if angle is smaller approximately than approimately 1e-4. */
1960 if( dLast
.normSquared( ) <= di
.normSquared( ) )
1962 last
= hullPoints
.back( );
1963 hullPoints
.pop_back( );
1971 /* If new point is to the right of the line from second last to last point,
1972 * then the last point is removed.
1973 * Let n be the outward pointing normal.
1975 const Concrete::Coords2D
* secondLast
= hullPoints
.back( );
1976 const Concrete::Length nx
= last
->y_
- secondLast
->y_
;
1977 const Concrete::Length ny
= secondLast
->x_
- last
->x_
;
1978 const Concrete::Length dx
= (*i
)->x_
- last
->x_
;
1979 const Concrete::Length dy
= (*i
)->y_
- last
->y_
;
1980 if( nx
* dx
+ ny
* dy
>= Concrete::LengthSquared( 0 ) )
1982 last
= hullPoints
.back( );
1983 hullPoints
.pop_back( );
1990 hullPoints
.push_back( last
);
1993 hullPoints
.push_back( last
);
1995 for( std::list
< const Concrete::Coords2D
* >::const_iterator i
= hullPoints
.begin( );
1996 i
!= hullPoints
.end( );
1999 res
->push_back( new Concrete::PathPoint2D( new Concrete::Coords2D( *(*i
) ) ) );
2003 return RefCountPtr
< const Lang::ElementaryPath2D
>( res
);
2006 RefCountPtr
< const Lang::ElementaryPath2D
>
2007 Lang::ElementaryPath2D::upsample( const Computation::Upsampler2D
& sampler
) const
2011 return Lang::THE_EMPTYPATH2D
;
2014 std::vector
< double > sampleTimes
;
2015 Concrete::Coords2D
rearHandle( 0, 0 );
2017 Lang::ElementaryPath2D
* res
= new Lang::ElementaryPath2D( );
2027 res
->push_back( new Concrete::PathPoint2D( *front( ) ) );
2028 return RefCountPtr
< const Lang::ElementaryPath2D
>( res
);
2031 const_iterator i1
= begin( );
2032 Bezier::ControlPoints
< Concrete::Coords2D
> controls( *(*i1
)->mid_
, *(*i1
)->front_
, *(*i1
)->rear_
, *(*i1
)->mid_
);
2033 sampler( & sampleTimes
, controls
);
2034 if( sampleTimes
.empty( ) )
2036 res
->push_back( new Concrete::PathPoint2D( *front( ) ) );
2037 return RefCountPtr
< const Lang::ElementaryPath2D
>( res
);
2040 Bezier::PolyCoeffs
< Concrete::Coords2D
> coeffs( controls
);
2042 // We will compute the same sub section again later, but doing it twice makes the code cleaner and more similar to the general case below.
2043 Bezier::ControlPoints
< Concrete::Coords2D
> lastSeg( coeffs
.subSection( sampleTimes
.back( ), 1 ) );
2044 rearHandle
= lastSeg
.p2_
;
2046 sampleTimes
.push_back( 1 );
2047 typedef typeof sampleTimes ListType
;
2049 ListType::const_iterator ti
= sampleTimes
.begin( );
2051 for( ; ti
!= sampleTimes
.end( ); t0
= t1
, ++ti
)
2054 Bezier::ControlPoints
< Concrete::Coords2D
> seg( coeffs
.subSection( t0
, t1
) );
2055 Concrete::PathPoint2D
* newPoint
= new Concrete::PathPoint2D( new Concrete::Coords2D( seg
.p0_
) );
2056 newPoint
->front_
= new Concrete::Coords2D( seg
.p1_
);
2057 newPoint
->rear_
= new Concrete::Coords2D( rearHandle
);
2058 res
->push_back( newPoint
);
2059 rearHandle
= seg
.p2_
;
2062 return RefCountPtr
< const Lang::ElementaryPath2D
>( res
);
2065 const_iterator i1
= begin( );
2066 const_iterator i2
= i1
;
2069 // Determine the initial rear handle.
2072 const_iterator i0
= end( );
2074 Bezier::ControlPoints
< Concrete::Coords2D
> controls( *(*i0
)->mid_
, *(*i0
)->front_
, *(*i1
)->rear_
, *(*i1
)->mid_
);
2075 sampleTimes
.clear( ); // Not needed here, but included for symmetry with the general case below.
2076 sampler( & sampleTimes
, controls
);
2077 if( sampleTimes
.empty( ) )
2079 rearHandle
= *(*i1
)->rear_
;
2083 Bezier::PolyCoeffs
< Concrete::Coords2D
> coeffs( controls
);
2084 Bezier::ControlPoints
< Concrete::Coords2D
> lastSeg( coeffs
.subSection( sampleTimes
.back( ), 1 ) );
2085 rearHandle
= lastSeg
.p2_
;
2090 rearHandle
= *(*i1
)->rear_
;
2093 for( ; i1
!= end( ); ++i1
, ++i2
)
2103 Concrete::PathPoint2D
* newPoint
= new Concrete::PathPoint2D( new Concrete::Coords2D( *(*i1
)->mid_
) );
2104 newPoint
->front_
= new Concrete::Coords2D( *(*i1
)->front_
);
2105 newPoint
->rear_
= new Concrete::Coords2D( rearHandle
);
2106 res
->push_back( newPoint
);
2111 Bezier::ControlPoints
< Concrete::Coords2D
> controls( *(*i1
)->mid_
, *(*i1
)->front_
, *(*i2
)->rear_
, *(*i2
)->mid_
);
2112 sampleTimes
.clear( );
2113 sampler( & sampleTimes
, controls
);
2114 if( sampleTimes
.empty( ) )
2116 Concrete::PathPoint2D
* newPoint
= new Concrete::PathPoint2D( new Concrete::Coords2D( controls
.p0_
) );
2117 newPoint
->front_
= new Concrete::Coords2D( controls
.p1_
);
2118 newPoint
->rear_
= new Concrete::Coords2D( rearHandle
);
2119 res
->push_back( newPoint
);
2120 rearHandle
= controls
.p2_
;
2124 Bezier::PolyCoeffs
< Concrete::Coords2D
> coeffs( controls
);
2125 sampleTimes
.push_back( 1 );
2126 typedef typeof sampleTimes ListType
;
2128 ListType::const_iterator ti
= sampleTimes
.begin( );
2130 for( ; ti
!= sampleTimes
.end( ); t0
= t1
, ++ti
)
2133 Bezier::ControlPoints
< Concrete::Coords2D
> seg( coeffs
.subSection( t0
, t1
) );
2134 Concrete::PathPoint2D
* newPoint
= new Concrete::PathPoint2D( new Concrete::Coords2D( seg
.p0_
) );
2135 newPoint
->front_
= new Concrete::Coords2D( seg
.p1_
);
2136 newPoint
->rear_
= new Concrete::Coords2D( rearHandle
);
2137 res
->push_back( newPoint
);
2138 rearHandle
= seg
.p2_
;
2144 return RefCountPtr
< const Lang::ElementaryPath2D
>( res
);
2149 Lang::ElementaryPath2D::isConvexPoly( Concrete::Length tol
) const
2151 for( const_iterator i
= begin( ); i
!= end( ); ++i
)
2153 if( (*i
)->front_
!= (*i
)->mid_
|| (*i
)->rear_
!= (*i
)->mid_
)
2162 for( const_iterator i
= begin( ); i
!= end( ); ++i
)
2164 const_iterator i1
= i
;
2170 const_iterator i2
= i1
;
2176 const_iterator i3
= i2
;
2183 // The following test is a local convexity test which does not need an orientation, but checks that the two points neighboring an edge are on the same side of the edge.
2184 Concrete::UnitFloatPair n
= (*i
)->mid_
->normalizedOrthogonal( *((*i1
)->mid_
) );
2185 Concrete::Length tmp
= Concrete::inner( n
, *((*i2
)->mid_
) - *((*i
)->mid_
) );
2188 if( Concrete::inner( n
, *((*i3
)->mid_
) - *((*i
)->mid_
) ) > tol
)
2193 else if( tmp
> tol
)
2195 if( Concrete::inner( n
, *((*i3
)->mid_
) - *((*i
)->mid_
) ) < - tol
)
2205 Lang::ElementaryPath2D::convexPolyContains( const Concrete::Coords2D
& p
, Concrete::Length tol
) const
2212 // Since we know that we are a convex poly, the same-side test used in isConvexPoly can now be used with *((*i3)->mid_) replaced by p
2213 for( const_iterator i
= begin( ); i
!= end( ); ++i
)
2215 const_iterator i1
= i
;
2221 const_iterator i2
= i1
;
2228 Concrete::UnitFloatPair n
= (*i
)->mid_
->normalizedOrthogonal( *((*i1
)->mid_
) );
2229 Concrete::Length tmp
= Concrete::inner( n
, *((*i2
)->mid_
) - *((*i
)->mid_
) );
2232 if( Concrete::inner( n
, p
- *((*i
)->mid_
) ) > tol
)
2237 else if( tmp
> tol
)
2239 if( Concrete::inner( n
, p
- *((*i
)->mid_
) ) < - tol
)
2250 Lang::ElementaryPath2D::arcLength( ) const
2254 throw Exceptions::OutOfRange( "The empty path has no defined arclength." );
2256 /* Multiplication by dt is extracted to outside the integrals for efficiency.
2258 Concrete::Length totlen
= Concrete::ZERO_LENGTH
;
2260 const Concrete::Length arcdelta
= Computation::the_arcdelta
;
2262 const_iterator i1
= begin( );
2263 const_iterator i2
= i1
;
2265 for( ; i1
!= end( ); ++i1
, ++i2
)
2278 if( (*i1
)->front_
== (*i1
)->mid_
&&
2279 (*i2
)->rear_
== (*i2
)->mid_
)
2281 const Concrete::Length segLength
= hypotPhysical( (*i1
)->mid_
->x_
- (*i2
)->mid_
->x_
, (*i1
)->mid_
->y_
- (*i2
)->mid_
->y_
);
2282 totlen
+= segLength
;
2286 Concrete::Bezier x0
= (*i1
)->mid_
->x_
.offtype
< 0, 3 >( );
2287 Concrete::Bezier y0
= (*i1
)->mid_
->y_
.offtype
< 0, 3 >( );
2288 Concrete::Bezier x1
= (*i1
)->front_
->x_
.offtype
< 0, 3 >( );
2289 Concrete::Bezier y1
= (*i1
)->front_
->y_
.offtype
< 0, 3 >( );
2290 Concrete::Bezier x2
= (*i2
)->rear_
->x_
.offtype
< 0, 3 >( );
2291 Concrete::Bezier y2
= (*i2
)->rear_
->y_
.offtype
< 0, 3 >( );
2292 Concrete::Bezier x3
= (*i2
)->mid_
->x_
.offtype
< 0, 3 >( );
2293 Concrete::Bezier y3
= (*i2
)->mid_
->y_
.offtype
< 0, 3 >( );
2295 const Concrete::Length segLengthBound
=
2296 ( hypotPhysical( x1
-x0
, y1
-y0
) + hypotPhysical( x2
-x1
, y2
-y1
) + hypotPhysical( x3
-x2
, y3
-y2
) ).offtype
< 0, -3 >( );
2298 if( segLengthBound
< arcdelta
)
2300 totlen
+= segLengthBound
;
2304 Concrete::Time dt
= Shapes::computeDt( segLengthBound
);
2305 Concrete::Length tmpSum_l
= Concrete::ZERO_LENGTH
;
2306 for( Concrete::Time t
= Concrete::ZERO_TIME
; t
< Concrete::UNIT_TIME
; t
+= dt
)
2308 Concrete::Time tc
= Concrete::UNIT_TIME
- t
; /* complement to t */
2309 Physical
< 0, 2 > kv0
= -3 * tc
* tc
;
2310 Physical
< 0, 2 > kv1
= 3 * tc
* tc
- 6 * tc
* t
;
2311 Physical
< 0, 2 > kv2
= 6 * tc
* t
- 3 * t
* t
;
2312 Physical
< 0, 2 > kv3
= 3 * t
* t
;
2313 Concrete::Speed vx
= x0
* kv0
+ x1
* kv1
+ x2
* kv2
+ x3
* kv3
;
2314 Concrete::Speed vy
= y0
* kv0
+ y1
* kv1
+ y2
* kv2
+ y3
* kv3
;
2316 Concrete::Length dl
= hypotPhysical( vx
, vy
).offtype
< 0, -1 >( );
2319 totlen
+= tmpSum_l
* dt
.offtype
< 0, 1 >( );
2327 Lang::ElementaryPath2D::arcLength( Concrete::Time tRemaining
) const
2331 throw Exceptions::OutOfRange( "The empty path has no defined arclength." );
2333 if( tRemaining
< Concrete::ZERO_TIME
)
2335 return negative_arcLength( -tRemaining
);
2337 if( isinfPhysical( tRemaining
) )
2339 throw Exceptions::OutOfRange( "The arctime to infinity is not defined." );
2341 if( tRemaining
> Concrete::Time( duration( ) ) && ! closed_
)
2343 throw Exceptions::OutOfRange( "Too big arctime for open path." );
2345 /* Multiplication by dt is extracted to outside the integrals for efficiency.
2347 Concrete::Length totlen
= Concrete::ZERO_LENGTH
;
2349 const Concrete::Length arcdelta
= Computation::the_arcdelta
;
2352 const double revolutions
= floor( tRemaining
/ Concrete::Time( duration( ) ) );
2353 if( revolutions
> 0 )
2355 totlen
+= revolutions
* arcLength( );
2356 tRemaining
-= revolutions
* Concrete::Time( duration( ) );
2360 const_iterator i1
= begin( );
2361 const_iterator i2
= i1
;
2363 for( ; tRemaining
> Concrete::ZERO_TIME
; ++i1
, ++i2
, tRemaining
-= Concrete::UNIT_TIME
)
2367 /* This could be considered an error situation, but it may be due to numeric
2368 * errors, so we take action accordingly.
2374 /* This could also be an error situation...
2378 if( (*i1
)->front_
== (*i1
)->mid_
&&
2379 (*i2
)->rear_
== (*i2
)->mid_
)
2381 if( tRemaining
<= Concrete::UNIT_TIME
)
2383 Concrete::Time t1
= tRemaining
;
2384 Concrete::Time tc
= Concrete::UNIT_TIME
- t1
; /* complement to t1 */
2385 Concrete::Coords2D tmp
=
2386 ( tc
* tc
* ( tc
+ 3 * t1
) ).offtype
< 0, 3 >( ) * (*((*i1
)->mid_
)) +
2387 ( t1
* t1
* ( t1
+ 3 * tc
) ).offtype
< 0, 3 >( ) * (*((*i2
)->mid_
));
2388 const Concrete::Length segLength
= hypotPhysical( (*i1
)->mid_
->x_
- tmp
.x_
, (*i1
)->mid_
->y_
- tmp
.y_
);
2389 totlen
+= segLength
;
2392 const Concrete::Length segLength
= hypotPhysical( (*i1
)->mid_
->x_
- (*i2
)->mid_
->x_
, (*i1
)->mid_
->y_
- (*i2
)->mid_
->y_
);
2393 totlen
+= segLength
;
2397 Concrete::Bezier x0
= (*i1
)->mid_
->x_
.offtype
< 0, 3 >( );
2398 Concrete::Bezier y0
= (*i1
)->mid_
->y_
.offtype
< 0, 3 >( );
2399 Concrete::Bezier x1
= (*i1
)->front_
->x_
.offtype
< 0, 3 >( );
2400 Concrete::Bezier y1
= (*i1
)->front_
->y_
.offtype
< 0, 3 >( );
2401 Concrete::Bezier x2
= (*i2
)->rear_
->x_
.offtype
< 0, 3 >( );
2402 Concrete::Bezier y2
= (*i2
)->rear_
->y_
.offtype
< 0, 3 >( );
2403 Concrete::Bezier x3
= (*i2
)->mid_
->x_
.offtype
< 0, 3 >( );
2404 Concrete::Bezier y3
= (*i2
)->mid_
->y_
.offtype
< 0, 3 >( );
2406 const Concrete::Length segLengthBound
=
2407 ( hypotPhysical( x1
-x0
, y1
-y0
) + hypotPhysical( x2
-x1
, y2
-y1
) + hypotPhysical( x3
-x2
, y3
-y2
) ).offtype
< 0, -3 >( );
2409 if( segLengthBound
< arcdelta
)
2411 if( tRemaining
<= Concrete::UNIT_TIME
)
2413 totlen
+= double(tRemaining
/ Concrete::UNIT_TIME
) * segLengthBound
;
2416 totlen
+= segLengthBound
;
2420 Concrete::Time dt
= Shapes::computeDt( segLengthBound
);
2421 Concrete::Length tmpSum_l
= Concrete::ZERO_LENGTH
;
2422 const Concrete::Time tend
= min( Concrete::UNIT_TIME
, tRemaining
);
2423 for( Concrete::Time t
= Concrete::ZERO_TIME
; t
< tend
; t
+= dt
)
2425 Concrete::Time tc
= Concrete::UNIT_TIME
- t
; /* complement to t */
2426 Physical
< 0, 2 > kv0
= -3 * tc
* tc
;
2427 Physical
< 0, 2 > kv1
= 3 * tc
* tc
- 6 * tc
* t
;
2428 Physical
< 0, 2 > kv2
= 6 * tc
* t
- 3 * t
* t
;
2429 Physical
< 0, 2 > kv3
= 3 * t
* t
;
2430 Concrete::Speed vx
= x0
* kv0
+ x1
* kv1
+ x2
* kv2
+ x3
* kv3
;
2431 Concrete::Speed vy
= y0
* kv0
+ y1
* kv1
+ y2
* kv2
+ y3
* kv3
;
2433 Concrete::Length dl
= hypotPhysical( vx
, vy
).offtype
< 0, -1 >( );
2436 totlen
+= tmpSum_l
* dt
.offtype
< 0, 1 >( );
2444 Lang::ElementaryPath2D::negative_arcLength( Concrete::Time tRemaining
) const
2448 throw Exceptions::OutOfRange( "The empty path has no defined arclength." );
2450 if( tRemaining
< Concrete::ZERO_TIME
)
2452 throw Exceptions::InternalError( "Negative tRemaining in negative_arcLength." );
2456 throw Exceptions::OutOfRange( "The arctime at negative lengths is not defined for open paths." );
2458 if( isinfPhysical( tRemaining
) )
2460 throw Exceptions::OutOfRange( "The arctime to infinity is not defined." );
2463 /* Multiplication by dt is extracted to outside the integrals for efficiency.
2465 Concrete::Length totlen
= Concrete::ZERO_LENGTH
;
2467 const Concrete::Length arcdelta
= Computation::the_arcdelta
;
2470 const double revolutions
= floor( tRemaining
/ Concrete::Time( duration( ) ) );
2471 if( revolutions
> 0 )
2473 totlen
-= revolutions
* arcLength( );
2474 tRemaining
-= revolutions
* Concrete::Time( duration( ) );
2478 const_reverse_iterator i1
= rbegin( );
2479 const_reverse_iterator i2
= i1
;
2490 for( ; tRemaining
> Concrete::ZERO_TIME
; ++i1
, ++i2
, tRemaining
-= Concrete::UNIT_TIME
)
2494 /* This could be considered an error situation, but it may be due to numeric
2495 * errors, so we take action accordingly.
2503 if( (*i1
)->rear_
== (*i1
)->mid_
&&
2504 (*i2
)->front_
== (*i2
)->mid_
)
2506 if( tRemaining
<= Concrete::UNIT_TIME
)
2508 Concrete::Time t1
= tRemaining
;
2509 Concrete::Time tc
= Concrete::UNIT_TIME
- t1
; /* complement to t1 */
2510 Concrete::Coords2D tmp
=
2511 ( tc
* tc
* ( tc
+ 3 * t1
) ).offtype
< 0, 3 >( ) * (*((*i1
)->mid_
)) +
2512 ( t1
* t1
* ( t1
+ 3 * tc
) ).offtype
< 0, 3 >( ) * (*((*i2
)->mid_
));
2513 const Concrete::Length segLength
= hypotPhysical( (*i1
)->mid_
->x_
- tmp
.x_
, (*i1
)->mid_
->y_
- tmp
.y_
);
2514 totlen
-= segLength
;
2517 const Concrete::Length segLength
= hypotPhysical( (*i1
)->mid_
->x_
- (*i2
)->mid_
->x_
, (*i1
)->mid_
->y_
- (*i2
)->mid_
->y_
);
2518 totlen
-= segLength
;
2522 Concrete::Bezier x0
= (*i1
)->mid_
->x_
.offtype
< 0, 3 >( );
2523 Concrete::Bezier y0
= (*i1
)->mid_
->y_
.offtype
< 0, 3 >( );
2524 Concrete::Bezier x1
= (*i1
)->rear_
->x_
.offtype
< 0, 3 >( );
2525 Concrete::Bezier y1
= (*i1
)->rear_
->y_
.offtype
< 0, 3 >( );
2526 Concrete::Bezier x2
= (*i2
)->front_
->x_
.offtype
< 0, 3 >( );
2527 Concrete::Bezier y2
= (*i2
)->front_
->y_
.offtype
< 0, 3 >( );
2528 Concrete::Bezier x3
= (*i2
)->mid_
->x_
.offtype
< 0, 3 >( );
2529 Concrete::Bezier y3
= (*i2
)->mid_
->y_
.offtype
< 0, 3 >( );
2531 const Concrete::Length segLengthBound
=
2532 ( hypotPhysical( x1
-x0
, y1
-y0
) + hypotPhysical( x2
-x1
, y2
-y1
) + hypotPhysical( x3
-x2
, y3
-y2
) ).offtype
< 0, -3 >( );
2534 if( segLengthBound
< arcdelta
)
2536 if( tRemaining
<= Concrete::UNIT_TIME
)
2538 totlen
-= ( tRemaining
/ Concrete::UNIT_TIME
) * segLengthBound
;
2541 totlen
-= segLengthBound
;
2545 Concrete::Time dt
= Shapes::computeDt( segLengthBound
);
2546 Concrete::Length tmpSum_l
= Concrete::ZERO_LENGTH
;
2547 const Concrete::Time tend
= min( Concrete::UNIT_TIME
, tRemaining
);
2548 for( Concrete::Time t
= Concrete::ZERO_TIME
; t
< tend
; t
+= dt
)
2550 Concrete::Time tc
= Concrete::UNIT_TIME
- t
; /* complement to t */
2551 Physical
< 0, 2 > kv0
= -3 * tc
* tc
;
2552 Physical
< 0, 2 > kv1
= 3 * tc
* tc
- 6 * tc
* t
;
2553 Physical
< 0, 2 > kv2
= 6 * tc
* t
- 3 * t
* t
;
2554 Physical
< 0, 2 > kv3
= 3 * t
* t
;
2555 Concrete::Speed vx
= x0
* kv0
+ x1
* kv1
+ x2
* kv2
+ x3
* kv3
;
2556 Concrete::Speed vy
= y0
* kv0
+ y1
* kv1
+ y2
* kv2
+ y3
* kv3
;
2558 Concrete::Length dl
= hypotPhysical( vx
, vy
).offtype
< 0, -1 >( );
2561 totlen
-= tmpSum_l
* dt
.offtype
< 0, 1 >( );
2568 Concrete::SplineTime
2569 Lang::ElementaryPath2D::arcTime( const Concrete::Length
& t
, Concrete::Time t0
) const
2573 throw Exceptions::OutOfRange( "The empty path has no arctimes defined." );
2575 if( duration( ) == 0 )
2577 throw Exceptions::OutOfRange( "The singleton path has no arctimes defined." );
2579 if( t
>= Concrete::HUGE_LENGTH
)
2581 return Concrete::SplineTime( Concrete::Time( duration( ) ), true );
2583 if( t
< Concrete::ZERO_LENGTH
)
2585 return negative_arcTime( - t
, t0
);
2588 Concrete::Time splineTime
= t0
;
2589 t0
= modPhysical( t0
, Concrete::Time( duration( ) ) );
2590 if( t0
< Concrete::ZERO_TIME
)
2592 t0
+= Concrete::Time( duration( ) );
2595 const_iterator i1
= begin( );
2596 while( t0
>= Concrete::UNIT_TIME
)
2598 t0
-= Concrete::UNIT_TIME
;
2604 return Concrete::SplineTime( Concrete::Time( duration( ) ), true );
2609 const_iterator i2
= i1
;
2612 const Concrete::Length arcdelta
= Computation::the_arcdelta
;
2613 Concrete::Length remainingLength
= t
;
2615 if( t0
> Concrete::ZERO_TIME
)
2621 return Concrete::SplineTime( Concrete::Time( duration( ) ), true );
2625 if( (*i1
)->front_
== (*i1
)->mid_
&&
2626 (*i2
)->rear_
== (*i2
)->mid_
)
2628 Concrete::Time tc
= Concrete::UNIT_TIME
- t0
; /* complement to t0 */
2629 Concrete::Coords2D tmp
=
2630 ( tc
* tc
* ( tc
+ 3 * t0
) ).offtype
< 0, 3 >( ) * (*((*i1
)->mid_
)) +
2631 ( t0
* t0
* ( t0
+ 3 * tc
) ).offtype
< 0, 3 >( ) * (*((*i2
)->mid_
));
2632 const Concrete::Length segRestLength
= hypotPhysical( tmp
.x_
- (*i2
)->mid_
->x_
, tmp
.y_
- (*i2
)->mid_
->y_
);
2633 if( segRestLength
< remainingLength
)
2635 remainingLength
-= segRestLength
;
2638 const Concrete::Length segPastLength
= hypotPhysical( tmp
.x_
- (*i1
)->mid_
->x_
, tmp
.y_
- (*i1
)->mid_
->y_
);
2639 const Concrete::Length segLength
= hypotPhysical( (*i1
)->mid_
->x_
- (*i2
)->mid_
->x_
, (*i1
)->mid_
->y_
- (*i2
)->mid_
->y_
);
2640 splineTime
+= Shapes::straightLineArcTime( ( segPastLength
+ remainingLength
) / segLength
) - t0
;
2645 Concrete::Bezier x0
= (*i1
)->mid_
->x_
.offtype
< 0, 3 >( );
2646 Concrete::Bezier y0
= (*i1
)->mid_
->y_
.offtype
< 0, 3 >( );
2647 Concrete::Bezier x1
= (*i1
)->front_
->x_
.offtype
< 0, 3 >( );
2648 Concrete::Bezier y1
= (*i1
)->front_
->y_
.offtype
< 0, 3 >( );
2649 Concrete::Bezier x2
= (*i2
)->rear_
->x_
.offtype
< 0, 3 >( );
2650 Concrete::Bezier y2
= (*i2
)->rear_
->y_
.offtype
< 0, 3 >( );
2651 Concrete::Bezier x3
= (*i2
)->mid_
->x_
.offtype
< 0, 3 >( );
2652 Concrete::Bezier y3
= (*i2
)->mid_
->y_
.offtype
< 0, 3 >( );
2654 const Concrete::Length segLengthBound
=
2655 ( hypotPhysical( (x1
-x0
), (y1
-y0
) ) + hypotPhysical( (x2
-x1
), (y2
-y1
) ) + hypotPhysical( (x3
-x2
), (y3
-y2
) ) ).offtype
< 0, -3 >( );
2657 if( segLengthBound
< arcdelta
)
2659 remainingLength
-= segLengthBound
;
2660 if( remainingLength
<= Concrete::ZERO_LENGTH
)
2662 splineTime
+= Concrete::Time( 0.5 );
2668 Concrete::Time dt
= Shapes::computeDt( segLengthBound
);
2670 const Concrete::Length remainingDiv_dt
= remainingLength
/ dt
.offtype
< 0, 1 >( );
2671 Concrete::Length tmpSum_l
= Concrete::ZERO_LENGTH
;
2672 for( Concrete::Time t
= t0
; t
< Concrete::UNIT_TIME
; t
+= dt
)
2674 Concrete::Time tc
= Concrete::UNIT_TIME
- t
; /* complement to t */
2675 Physical
< 0, 2 > kv0
= -3 * tc
* tc
;
2676 Physical
< 0, 2 > kv1
= 3 * tc
* tc
- 6 * tc
* t
;
2677 Physical
< 0, 2 > kv2
= 6 * tc
* t
- 3 * t
* t
;
2678 Physical
< 0, 2 > kv3
= 3 * t
* t
;
2679 Concrete::Speed vx
= x0
* kv0
+ x1
* kv1
+ x2
* kv2
+ x3
* kv3
;
2680 Concrete::Speed vy
= y0
* kv0
+ y1
* kv1
+ y2
* kv2
+ y3
* kv3
;
2682 Concrete::Length dl
= hypotPhysical( vx
, vy
).offtype
< 0, -1 >( );
2684 if( tmpSum_l
>= remainingDiv_dt
)
2686 splineTime
+= t
- t0
;
2690 remainingLength
-= tmpSum_l
* dt
.offtype
< 0, 1 >( );
2695 splineTime
= Concrete::Time( ceil( splineTime
.offtype
< 0, 1 >( ) ) );
2701 return Concrete::SplineTime( Concrete::Time( duration( ) ), true );
2708 for( ; ; ++i1
, ++i2
, splineTime
+= Concrete::UNIT_TIME
)
2712 /* If not closed, then the break occurs when i2 reaches end
2720 return Concrete::SplineTime( Concrete::Time( duration( ) ), true );
2724 if( (*i1
)->front_
== (*i1
)->mid_
&&
2725 (*i2
)->rear_
== (*i2
)->mid_
)
2727 const Concrete::Length segLength
= hypotPhysical( (*i1
)->mid_
->x_
- (*i2
)->mid_
->x_
, (*i1
)->mid_
->y_
- (*i2
)->mid_
->y_
);
2728 if( segLength
< remainingLength
)
2730 remainingLength
-= segLength
;
2733 splineTime
+= Shapes::straightLineArcTime( remainingLength
/ segLength
);
2737 Concrete::Bezier x0
= (*i1
)->mid_
->x_
.offtype
< 0, 3 >( );
2738 Concrete::Bezier y0
= (*i1
)->mid_
->y_
.offtype
< 0, 3 >( );
2739 Concrete::Bezier x1
= (*i1
)->front_
->x_
.offtype
< 0, 3 >( );
2740 Concrete::Bezier y1
= (*i1
)->front_
->y_
.offtype
< 0, 3 >( );
2741 Concrete::Bezier x2
= (*i2
)->rear_
->x_
.offtype
< 0, 3 >( );
2742 Concrete::Bezier y2
= (*i2
)->rear_
->y_
.offtype
< 0, 3 >( );
2743 Concrete::Bezier x3
= (*i2
)->mid_
->x_
.offtype
< 0, 3 >( );
2744 Concrete::Bezier y3
= (*i2
)->mid_
->y_
.offtype
< 0, 3 >( );
2746 const Concrete::Length segLengthBound
=
2747 ( hypotPhysical( (x1
-x0
), (y1
-y0
) ) + hypotPhysical( (x2
-x1
), (y2
-y1
) ) + hypotPhysical( (x3
-x2
), (y3
-y2
) ) ).offtype
< 0, -3 >( );
2749 if( segLengthBound
< arcdelta
)
2751 remainingLength
-= segLengthBound
;
2752 if( remainingLength
<= Concrete::ZERO_LENGTH
)
2754 splineTime
+= Concrete::Time( 0.5 );
2760 Concrete::Time dt
= Shapes::computeDt( segLengthBound
);
2762 const Concrete::Length remainingDiv_dt
= remainingLength
/ dt
.offtype
< 0, 1 >( );
2763 Concrete::Length tmpSum_l
= Concrete::ZERO_LENGTH
;
2764 for( Concrete::Time t
= Concrete::ZERO_TIME
; t
< Concrete::UNIT_TIME
; t
+= dt
)
2766 Concrete::Time tc
= Concrete::UNIT_TIME
- t
; /* complement to t */
2767 Physical
< 0, 2 > kv0
= -3 * tc
* tc
;
2768 Physical
< 0, 2 > kv1
= 3 * tc
* tc
- 6 * tc
* t
;
2769 Physical
< 0, 2 > kv2
= 6 * tc
* t
- 3 * t
* t
;
2770 Physical
< 0, 2 > kv3
= 3 * t
* t
;
2771 Concrete::Speed vx
= x0
* kv0
+ x1
* kv1
+ x2
* kv2
+ x3
* kv3
;
2772 Concrete::Speed vy
= y0
* kv0
+ y1
* kv1
+ y2
* kv2
+ y3
* kv3
;
2774 Concrete::Length dl
= hypotPhysical( vx
, vy
).offtype
< 0, -1 >( );
2776 if( tmpSum_l
>= remainingDiv_dt
)
2782 remainingLength
-= tmpSum_l
* dt
.offtype
< 0, 1 >( );
2790 Concrete::SplineTime
2791 Lang::ElementaryPath2D::negative_arcTime( const Concrete::Length deltaLen
, Concrete::Time t0
) const
2793 if( deltaLen
< Concrete::ZERO_LENGTH
)
2795 throw Exceptions::InternalError( "Negative t in negative_arcTime." );
2799 throw Exceptions::OutOfRange( "The empty path has no arctimes defined." );
2801 if( duration( ) == 0 )
2803 throw Exceptions::OutOfRange( "The singleton path has no arctimes defined." );
2806 if( deltaLen
>= Concrete::HUGE_LENGTH
)
2808 return Concrete::SplineTime( Concrete::ZERO_TIME
, true );
2811 Concrete::Time splineTime
= t0
;
2814 t0
= modPhysical( t0
, Concrete::Time( duration( ) ) );
2817 t0
+= Concrete::Time( duration( ) );
2826 else if( t0
> Concrete::Time( duration( ) ) )
2828 t0
= Concrete::Time( duration( ) );
2831 t0
= Concrete::Time( duration( ) ) - t0
;
2832 // From here on, t0 is the (positive) backwards-time
2834 const_reverse_iterator i1
= rbegin( );
2840 while( t0
>= Concrete::UNIT_TIME
)
2842 t0
-= Concrete::UNIT_TIME
;
2848 return Concrete::SplineTime( Concrete::ZERO_TIME
, true );
2853 const_reverse_iterator i2
= i1
;
2856 const Concrete::Length arcdelta
= Computation::the_arcdelta
;
2857 Concrete::Length remainingLength
= deltaLen
;
2859 if( t0
> Concrete::ZERO_TIME
)
2865 return Concrete::SplineTime( Concrete::ZERO_TIME
, true );
2869 if( (*i1
)->rear_
== (*i1
)->mid_
&&
2870 (*i2
)->front_
== (*i2
)->mid_
)
2872 Concrete::Time tc
= Concrete::UNIT_TIME
- t0
; /* complement to t0 */
2873 Concrete::Coords2D tmp
=
2874 ( tc
* tc
* ( tc
+ 3 * t0
) ).offtype
< 0, 3 >( ) * (*((*i1
)->mid_
)) +
2875 ( t0
* t0
* ( t0
+ 3 * tc
) ).offtype
< 0, 3 >( ) * (*((*i2
)->mid_
));
2876 const Concrete::Length segRestLength
= hypotPhysical( tmp
.x_
- (*i2
)->mid_
->x_
, tmp
.y_
- (*i2
)->mid_
->y_
);
2877 if( segRestLength
< remainingLength
)
2879 remainingLength
-= segRestLength
;
2882 const Concrete::Length segPastLength
= hypotPhysical( tmp
.x_
- (*i1
)->mid_
->x_
, tmp
.y_
- (*i1
)->mid_
->y_
);
2883 const Concrete::Length segLength
= hypotPhysical( (*i1
)->mid_
->x_
- (*i2
)->mid_
->x_
, (*i1
)->mid_
->y_
- (*i2
)->mid_
->y_
);
2884 splineTime
-= Shapes::straightLineArcTime( ( segPastLength
+ remainingLength
) / segLength
) - t0
;
2889 Concrete::Bezier x0
= (*i1
)->mid_
->x_
.offtype
< 0, 3 >( );
2890 Concrete::Bezier y0
= (*i1
)->mid_
->y_
.offtype
< 0, 3 >( );
2891 Concrete::Bezier x1
= (*i1
)->rear_
->x_
.offtype
< 0, 3 >( );
2892 Concrete::Bezier y1
= (*i1
)->rear_
->y_
.offtype
< 0, 3 >( );
2893 Concrete::Bezier x2
= (*i2
)->front_
->x_
.offtype
< 0, 3 >( );
2894 Concrete::Bezier y2
= (*i2
)->front_
->y_
.offtype
< 0, 3 >( );
2895 Concrete::Bezier x3
= (*i2
)->mid_
->x_
.offtype
< 0, 3 >( );
2896 Concrete::Bezier y3
= (*i2
)->mid_
->y_
.offtype
< 0, 3 >( );
2898 const Concrete::Length segLengthBound
=
2899 ( hypotPhysical( (x1
-x0
), (y1
-y0
) ) + hypotPhysical( (x2
-x1
), (y2
-y1
) ) + hypotPhysical( (x3
-x2
), (y3
-y2
) ) ).offtype
< 0, -3 >( );
2901 if( segLengthBound
< arcdelta
)
2903 remainingLength
-= segLengthBound
;
2904 if( remainingLength
<= Concrete::ZERO_LENGTH
)
2906 splineTime
-= Concrete::Time( 0.5 );
2912 Concrete::Time dt
= Shapes::computeDt( segLengthBound
);
2914 const Concrete::Length remainingDiv_dt
= remainingLength
/ dt
.offtype
< 0, 1 >( );
2915 Concrete::Length tmpSum_l
= Concrete::ZERO_LENGTH
;
2916 for( Concrete::Time t
= t0
; t
< Concrete::UNIT_TIME
; t
+= dt
)
2918 Concrete::Time tc
= Concrete::UNIT_TIME
- t
; /* complement to t */
2919 Physical
< 0, 2 > kv0
= -3 * tc
* tc
;
2920 Physical
< 0, 2 > kv1
= 3 * tc
* tc
- 6 * tc
* t
;
2921 Physical
< 0, 2 > kv2
= 6 * tc
* t
- 3 * t
* t
;
2922 Physical
< 0, 2 > kv3
= 3 * t
* t
;
2923 Concrete::Speed vx
= x0
* kv0
+ x1
* kv1
+ x2
* kv2
+ x3
* kv3
;
2924 Concrete::Speed vy
= y0
* kv0
+ y1
* kv1
+ y2
* kv2
+ y3
* kv3
;
2926 Concrete::Length dl
= hypotPhysical( vx
, vy
).offtype
< 0, -1 >( );
2928 if( tmpSum_l
>= remainingDiv_dt
)
2930 splineTime
-= t
- t0
;
2934 remainingLength
-= tmpSum_l
* dt
.offtype
< 0, 1 >( );
2939 splineTime
= Concrete::Time( floor( splineTime
.offtype
< 0, 1 >( ) ) );
2945 return Concrete::SplineTime( Concrete::ZERO_TIME
, true );
2952 for( ; ; ++i1
, ++i2
, splineTime
-= Concrete::UNIT_TIME
)
2956 /* If not closed, then the break occurs when i2 reaches end
2964 return Concrete::SplineTime( Concrete::ZERO_TIME
, true );
2968 if( (*i1
)->rear_
== (*i1
)->mid_
&&
2969 (*i2
)->front_
== (*i2
)->mid_
)
2971 const Concrete::Length segLength
= hypotPhysical( (*i1
)->mid_
->x_
- (*i2
)->mid_
->x_
, (*i1
)->mid_
->y_
- (*i2
)->mid_
->y_
);
2972 if( segLength
< remainingLength
)
2974 remainingLength
-= segLength
;
2977 splineTime
-= Shapes::straightLineArcTime( remainingLength
/ segLength
);
2981 Concrete::Bezier x0
= (*i1
)->mid_
->x_
.offtype
< 0, 3 >( );
2982 Concrete::Bezier y0
= (*i1
)->mid_
->y_
.offtype
< 0, 3 >( );
2983 Concrete::Bezier x1
= (*i1
)->rear_
->x_
.offtype
< 0, 3 >( );
2984 Concrete::Bezier y1
= (*i1
)->rear_
->y_
.offtype
< 0, 3 >( );
2985 Concrete::Bezier x2
= (*i2
)->front_
->x_
.offtype
< 0, 3 >( );
2986 Concrete::Bezier y2
= (*i2
)->front_
->y_
.offtype
< 0, 3 >( );
2987 Concrete::Bezier x3
= (*i2
)->mid_
->x_
.offtype
< 0, 3 >( );
2988 Concrete::Bezier y3
= (*i2
)->mid_
->y_
.offtype
< 0, 3 >( );
2990 const Concrete::Length segLengthBound
=
2991 ( hypotPhysical( (x1
-x0
), (y1
-y0
) ) + hypotPhysical( (x2
-x1
), (y2
-y1
) ) + hypotPhysical( (x3
-x2
), (y3
-y2
) ) ).offtype
< 0, -3 >( );
2993 if( segLengthBound
< arcdelta
)
2995 remainingLength
-= segLengthBound
;
2996 if( remainingLength
<= Concrete::ZERO_LENGTH
)
2998 splineTime
-= Concrete::Time( 0.5 );
3004 Concrete::Time dt
= Shapes::computeDt( segLengthBound
);
3006 const Concrete::Length remainingDiv_dt
= remainingLength
/ dt
.offtype
< 0, 1 >( );
3007 Concrete::Length tmpSum_l
= Concrete::ZERO_LENGTH
;
3008 for( Concrete::Time t
= Concrete::ZERO_TIME
; t
< Concrete::UNIT_TIME
; t
+= dt
)
3010 Concrete::Time tc
= Concrete::UNIT_TIME
- t
; /* complement to t */
3011 Physical
< 0, 2 > kv0
= -3 * tc
* tc
;
3012 Physical
< 0, 2 > kv1
= 3 * tc
* tc
- 6 * tc
* t
;
3013 Physical
< 0, 2 > kv2
= 6 * tc
* t
- 3 * t
* t
;
3014 Physical
< 0, 2 > kv3
= 3 * t
* t
;
3015 Concrete::Speed vx
= x0
* kv0
+ x1
* kv1
+ x2
* kv2
+ x3
* kv3
;
3016 Concrete::Speed vy
= y0
* kv0
+ y1
* kv1
+ y2
* kv2
+ y3
* kv3
;
3018 Concrete::Length dl
= hypotPhysical( vx
, vy
).offtype
< 0, -1 >( );
3020 if( tmpSum_l
>= remainingDiv_dt
)
3026 remainingLength
-= tmpSum_l
* dt
.offtype
< 0, 1 >( );
3036 namespace Computation
3038 typedef unsigned char WindingSigns
;
3040 const WindingSigns NO_SIGN
= 0x00;
3041 const WindingSigns POS_X
= 0x01;
3042 const WindingSigns POS_Y
= 0x02;
3045 windingNumberHelper( const Concrete::Coords2D
& p
, const Computation::WindingSigns QUAD_1
, const Computation::WindingSigns QUAD_2
,
3046 const Concrete::Coords2D
& p1
, const Concrete::Coords2D
& p2
,
3047 int * count
, WindingSigns
* lastSigns
)
3049 Concrete::Coords2D d
= p2
- p
;
3050 WindingSigns signs
= ( ( d
.x_
> 0 ) ? POS_X
: NO_SIGN
) | ( ( d
.y_
> 0 ) ? POS_Y
: NO_SIGN
);
3051 switch( signs
^ *lastSigns
)
3057 if( *lastSigns
== QUAD_1
&& signs
== QUAD_2
)
3061 else if( *lastSigns
== QUAD_2
&& signs
== QUAD_1
)
3067 /* There is a switch from one quadrand to the opposite quadrant,
3068 * and we must determine an intermediate quadrant so that we know the direction of rotation.
3069 * The intermediate point is selected as the point on the segment which is closest to the origin.
3072 Concrete::UnitFloatPair n
= d
.normalizedOrthogonalNoFail( p1
- p
);
3073 Concrete::Coords2D dMid
= n
* Concrete::inner( d
, n
);
3074 WindingSigns signsMid
= ( ( dMid
.x_
> 0 ) ? POS_X
: NO_SIGN
) | ( ( dMid
.y_
> 0 ) ? POS_Y
: NO_SIGN
);
3075 if( *lastSigns
== QUAD_1
&& signsMid
== QUAD_2
)
3079 else if( *lastSigns
== QUAD_2
&& signsMid
== QUAD_1
)
3083 if( signsMid
== QUAD_1
&& signs
== QUAD_2
)
3087 else if( signsMid
== QUAD_2
&& signs
== QUAD_1
)
3094 throw Exceptions::InternalError( "windingNumberHelper: Bitwise computation went astray." );
3100 convexHullMember( const Concrete::Coords2D
& p
, const Concrete::Coords2D
& g0
, const Concrete::Coords2D
& g1
, const Concrete::Coords2D
& g2
, const Concrete::Coords2D
& g3
)
3102 /* If and only if p is not a member of the convex hull of the generators, there is a normal direction that proves it, and this normal direction
3103 * is orthogonal to one pair of generators.
3104 * ... except for the deflated case, when we must also test one more direction. See below.
3105 * When this function is used by the winding number algorith, a true result will cause the segment to be sub-divided, and if in doubt,
3106 * sub-dividing is the safer option. Hence, we shall prefer to return true when in doubt.
3108 std::vector
< const Concrete::Coords2D
* > pointSet( 0 );
3109 pointSet
.reserve( 4 );
3110 pointSet
.push_back( & g0
);
3111 pointSet
.push_back( & g1
);
3112 pointSet
.push_back( & g2
);
3113 pointSet
.push_back( & g3
);
3115 const Concrete::Length SEP_TOL
= 1e-10 * std::max( ( g1
- g0
).norm( ),
3116 std::max( ( g2
- g1
).norm( ),
3117 ( g3
- g2
).norm( ) ) );
3119 for( std::vector
< const Concrete::Coords2D
* >::const_iterator i0
= pointSet
.begin( ); i0
!= pointSet
.end( ); ++i0
)
3121 const Concrete::Coords2D
& p0( **i0
);
3122 std::vector
< const Concrete::Coords2D
* >::const_iterator i1
= i0
;
3124 for( ; i1
!= pointSet
.end( ); ++i1
)
3126 const Concrete::Coords2D
& p1( **i1
);
3127 Concrete::UnitFloatPair n
= p0
.normalizedOrthogonalNoFail( p1
); /* In order to make sense of SEP_TOL, this needs to be normalized. */
3128 Concrete::Length dp
= Concrete::inner( n
, p
- p0
);
3129 if( dp
< - SEP_TOL
)
3134 else if( dp
< SEP_TOL
)
3136 /* p is on the line from p0 to p1 */
3140 bool separated
= true;
3141 for( std::vector
< const Concrete::Coords2D
* >::const_iterator i2
= pointSet
.begin( ); i2
!= pointSet
.end( ); ++i2
)
3143 if( i2
== i0
|| i2
== i1
)
3147 if( Concrete::inner( n
, **i2
- p0
) >= dp
)
3161 /* Take care of the degenerate case.
3162 * The normal direction may be taken as the direction from any vertex to the query point, and need not be normalized.
3163 * The convex hull is separated if the query point is further away in the normal direction than any of the vertices.
3165 std::vector
< const Concrete::Coords2D
* >::const_iterator i2
= pointSet
.begin( );
3166 const Concrete::Coords2D
& p0( **i2
);
3168 Concrete::UnitFloatPair
n( Concrete::Length::offtype( p
.x_
- p0
.x_
), Concrete::Length::offtype( p
.y_
- p0
.y_
), bool( ) );
3169 Concrete::Length dp
= Concrete::inner( n
, p
- p0
); /* Will be positive by construction. */
3170 bool separated
= true;
3171 for( ; i2
!= pointSet
.end( ); ++i2
)
3173 if( Concrete::inner( n
, **i2
- p0
) >= dp
)
3185 /* No direction proving separation was found. */
3192 Lang::ElementaryPath2D::windingNumber( const Concrete::Coords2D
& p
) const
3196 throw Exceptions::InternalError( "ElementaryPath2D::windingNumber called with non-closed path." );
3204 const_iterator i1
= begin( );
3205 const_iterator i2
= i1
;
3208 Concrete::Coords2D d
= *((*i1
)->mid_
) - p
;
3209 Computation::WindingSigns lastSigns
= ( ( d
.x_
> 0 ) ? Computation::POS_X
: Computation::NO_SIGN
) | ( ( d
.y_
> 0 ) ? Computation::POS_Y
: Computation::NO_SIGN
);
3210 /* Select two quadrants, between which crossings shall be counted. Avoid the quadrant where the path starts and ends. */
3211 const Computation::WindingSigns QUAD_1
= lastSigns
^ ( Computation::POS_X
| Computation::POS_Y
); /* Invert both signs. */
3212 const Computation::WindingSigns QUAD_2
= ( ( d
.y_
> 0 ) ? Computation::POS_X
: Computation::NO_SIGN
) | ( ( d
.x_
<= 0 ) ? Computation::POS_Y
: Computation::NO_SIGN
); /* Next quadrant in clock-wise direction. */
3214 std::stack
< Concrete::Coords2D
> pointStack
; /* End point at bottom of stack. */
3216 const Concrete::LengthSquared SEG_TOL
= Computation::the_arcdelta
* Computation::the_arcdelta
;
3218 Concrete::Time res
= Concrete::HUGE_TIME
;
3219 for( ; i1
!= end( ); ++i1
, ++i2
)
3225 if( (*i1
)->front_
== (*i1
)->mid_
&&
3226 (*i2
)->rear_
== (*i2
)->mid_
)
3228 Computation::windingNumberHelper( p
, QUAD_1
, QUAD_2
, *((*i1
)->mid_
), *((*i2
)->mid_
), & count
, & lastSigns
);
3232 pointStack
.push( *((*i2
)->mid_
) );
3233 pointStack
.push( *((*i2
)->rear_
) );
3234 pointStack
.push( *((*i1
)->front_
) );
3235 Concrete::Coords2D p0
= *((*i1
)->mid_
);
3236 while( ! pointStack
.empty( ) )
3238 Concrete::Coords2D p1
= pointStack
.top( );
3240 Concrete::Coords2D p2
= pointStack
.top( );
3242 Concrete::Coords2D p3
= pointStack
.top( );
3243 /* Leave the last point at the stack for now... */
3245 /* Only subdivide unless the segment is not tiny (in relation to the_arcdelta). */
3246 if( ( ( p1
- p0
).normSquared( ) >= SEG_TOL
|| ( p2
- p1
).normSquared( ) >= SEG_TOL
|| ( p3
- p2
).normSquared( ) >= SEG_TOL
)
3247 && Computation::convexHullMember( p
, p0
, p1
, p2
, p3
) )
3249 Bezier::ControlPoints
< Concrete::Coords2D
> controls( p0
, p1
, p2
, p3
);
3250 Bezier::PolyCoeffs
< Concrete::Coords2D
> coeffs( controls
);
3251 Bezier::ControlPoints
< Concrete::Coords2D
> sub1( coeffs
.subSection( 0, 0.5 ) );
3252 Bezier::ControlPoints
< Concrete::Coords2D
> sub2( coeffs
.subSection( 0.5, 1 ) );
3253 pointStack
.push( sub2
.p2_
);
3254 pointStack
.push( sub2
.p1_
);
3255 pointStack
.push( sub2
.p0_
);
3256 pointStack
.push( sub1
.p2_
);
3257 pointStack
.push( sub1
.p1_
);
3262 Computation::windingNumberHelper( p
, QUAD_1
, QUAD_2
, p0
, p3
, & count
, & lastSigns
);
3274 namespace Computation
3277 class SegmentPolynomialPair2D
3279 Bezier::PolyCoeffs
< Concrete::Coords2D
> polyCoeffs_a
;
3280 bool straightLineSeg_a_
;
3282 Physical
< 1, 0 > kxaD0_0
;
3283 Physical
< 1, -1 > kxaD0_1
;
3284 Physical
< 1, -2 > kxaD0_2
;
3285 Physical
< 1, -3 > kxaD0_3
;
3286 Physical
< 1, -1 > kxaD1_0
;
3287 Physical
< 1, -2 > kxaD1_1
;
3288 Physical
< 1, -3 > kxaD1_2
;
3289 Physical
< 1, -2 > kxaD2_0
;
3290 Physical
< 1, -3 > kxaD2_1
;
3292 Physical
< 1, 0 > kyaD0_0
;
3293 Physical
< 1, -1 > kyaD0_1
;
3294 Physical
< 1, -2 > kyaD0_2
;
3295 Physical
< 1, -3 > kyaD0_3
;
3296 Physical
< 1, -1 > kyaD1_0
;
3297 Physical
< 1, -2 > kyaD1_1
;
3298 Physical
< 1, -3 > kyaD1_2
;
3299 Physical
< 1, -2 > kyaD2_0
;
3300 Physical
< 1, -3 > kyaD2_1
;
3302 Bezier::PolyCoeffs
< Concrete::Coords2D
> polyCoeffs_b
;
3303 bool straightLineSeg_b_
;
3305 Physical
< 1, 0 > kxbD0_0
;
3306 Physical
< 1, -1 > kxbD0_1
;
3307 Physical
< 1, -2 > kxbD0_2
;
3308 Physical
< 1, -3 > kxbD0_3
;
3309 Physical
< 1, -1 > kxbD1_0
;
3310 Physical
< 1, -2 > kxbD1_1
;
3311 Physical
< 1, -3 > kxbD1_2
;
3312 Physical
< 1, -2 > kxbD2_0
;
3313 Physical
< 1, -3 > kxbD2_1
;
3315 Physical
< 1, 0 > kybD0_0
;
3316 Physical
< 1, -1 > kybD0_1
;
3317 Physical
< 1, -2 > kybD0_2
;
3318 Physical
< 1, -3 > kybD0_3
;
3319 Physical
< 1, -1 > kybD1_0
;
3320 Physical
< 1, -2 > kybD1_1
;
3321 Physical
< 1, -3 > kybD1_2
;
3322 Physical
< 1, -2 > kybD2_0
;
3323 Physical
< 1, -3 > kybD2_1
;
3325 Concrete::Time steps_a_
;
3326 Concrete::Time steps_b_
;
3328 Concrete::Speed maxSpeed_a_
;
3329 Concrete::Speed maxSpeed_b_
;
3332 SegmentPolynomialPair2D( Concrete::Time steps_a
, const Bezier::ControlPoints
< Concrete::Coords2D
> & seg_a
, Concrete::Time steps_b
, const Bezier::ControlPoints
< Concrete::Coords2D
> & seg_b
);
3333 SegmentPolynomialPair2D( Concrete::Time steps_a
, const Concrete::Coords2D
& seg_a_p0
, const Concrete::Coords2D
& seg_a_p3
, Concrete::Time steps_b
, const Bezier::ControlPoints
< Concrete::Coords2D
> & seg_b
);
3334 SegmentPolynomialPair2D( Concrete::Time steps_a
, const Bezier::ControlPoints
< Concrete::Coords2D
> & seg_a
, Concrete::Time steps_b
, const Concrete::Coords2D
& seg_b_p0
, const Concrete::Coords2D
& seg_b_p3
);
3335 bool splitTimes( Concrete::Time
* dst_a
, Concrete::Time
* dst_b
, const Concrete::Time t_alow
, const Concrete::Time t_ahigh
, const Concrete::Time t_atol
, const Concrete::Time t_blow
, const Concrete::Time t_bhigh
, const Concrete::Time t_btol
, Concrete::Length distance_tol
) const; /* Return true if intersection is found. */
3336 Concrete::Length
distanceAt( Concrete::Time ta
, Concrete::Time tb
) const;
3337 Concrete::LengthSquared
squaredDistanceAt( Concrete::Time ta
, Concrete::Time tb
) const;
3338 bool straightLineSeg_a( ) const { return straightLineSeg_a_
; }
3339 bool straightLineSeg_b( ) const { return straightLineSeg_b_
; }
3340 Concrete::Speed
maxSpeed_a( ) const { return maxSpeed_a_
; }
3341 Concrete::Speed
maxSpeed_b( ) const { return maxSpeed_b_
; }
3342 Concrete::Time
steps_a( ) const { return steps_a_
; }
3343 Concrete::Time
steps_b( ) const { return steps_b_
; }
3344 Bezier::ControlPoints
< Concrete::Coords2D
> getControls_a( const Concrete::Time t_low
, const Concrete::Time t_high
) const;
3345 Bezier::ControlPoints
< Concrete::Coords2D
> getControls_b( const Concrete::Time t_low
, const Concrete::Time t_high
) const;
3348 void kInit( ); /* Used by constructors to initialize coefficients. */
3351 class SegmentSectionPair2D
3353 Bezier::ControlPoints
< Concrete::Coords2D
> controls_a
;
3354 Bezier::ControlPoints
< Concrete::Coords2D
> controls_b
;
3355 const SegmentPolynomialPair2D
* baseSegs
;
3356 Concrete::Time t_a0
;
3357 Concrete::Time t_a1
;
3358 Concrete::Time t_b0
;
3359 Concrete::Time t_b1
;
3360 mutable Concrete::LengthSquared hull_dist_
;
3363 SegmentSectionPair2D( const SegmentPolynomialPair2D
* _baseSegs
, Concrete::Time _t_a0
, Concrete::Time _t_a1
, Concrete::Time _t_b0
, Concrete::Time _t_b1
);
3364 SegmentSectionPair2D
* cutAfterAfter( Concrete::Time ta
, Concrete::Time tb
, Concrete::Time tol_a
, Concrete::Time tol_b
) const;
3365 SegmentSectionPair2D
* cutAfterBefore( Concrete::Time ta
, Concrete::Time tb
, Concrete::Time tol_a
, Concrete::Time tol_b
) const;
3366 SegmentSectionPair2D
* cutBeforeAfter( Concrete::Time ta
, Concrete::Time tb
, Concrete::Time tol_a
, Concrete::Time tol_b
) const;
3367 SegmentSectionPair2D
* cutBeforeBefore( Concrete::Time ta
, Concrete::Time tb
, Concrete::Time tol_a
, Concrete::Time tol_b
) const;
3369 bool splitTimes( Concrete::Time
* dst_a
, Concrete::Time
* dst_b
, const Concrete::Time t_tol_a
, const Concrete::Time t_tol_b
, Concrete::Length distance_tol
) const; /* True if intersection was found. */
3370 Concrete::Time
steps_a( ) const { return baseSegs
->steps_a( ); };
3371 Concrete::Time
steps_b( ) const { return baseSegs
->steps_b( ); };
3372 Concrete::Time
globalTime_a( Concrete::Time t
) const;
3373 Concrete::Length
distanceAt( Concrete::Time ta
, Concrete::Time tb
) const;
3374 Concrete::LengthSquared
squaredDistanceAt( Concrete::Time ta
, Concrete::Time tb
) const;
3375 Concrete::Speed
maxSpeed_a( ) const;
3376 Concrete::Speed
maxSpeed_b( ) const;
3377 bool convexHullOverlap( ) const;
3378 bool convexHullSeparatedBy( Concrete::LengthSquared sep
) const; /* If the result is false, the actual distance between the hulls will be stored in hull_dist_. */
3379 Concrete::LengthSquared
precomputedHullDistanceSquared( ) const { return hull_dist_
; }
3380 void update_t_a1( Concrete::Time t
);
3381 bool isEmpty( ) const;
3383 void writeTimes( std::ostream
& os
) const;
3385 static bool convexHullOneWayOverlap( const std::vector
< const Concrete::Coords2D
* > & poly1
, const std::vector
< const Concrete::Coords2D
* > & poly2
);
3395 Kernel::StructureFactory
intersection_info_resultFactory( "other" );
3396 Kernel::StructureFactory
approximator_info_resultFactory( "dist", "other" );
3398 namespace Computation
3400 struct CurvedSegType
: public Bezier::ControlPoints
< Concrete::Coords2D
>
3402 Concrete::Time steps
;
3403 CurvedSegType( Concrete::Time _steps
, const Bezier::ControlPoints
< Concrete::Coords2D
> & ctrl
)
3404 : Bezier::ControlPoints
< Concrete::Coords2D
>( ctrl
), steps( _steps
)
3407 struct StraightSegType
3409 Concrete::Time steps
;
3410 Concrete::Coords2D
* first
;
3411 Concrete::Coords2D
* second
;
3413 StraightSegType( Concrete::Time _steps
, Concrete::Coords2D
* _first
, Concrete::Coords2D
* _second
)
3414 : steps( _steps
), first( _first
), second( _second
)
3420 Concrete::SplineTime
3421 Lang::ElementaryPath2D::intersection( const RefCountPtr
< const Lang::ElementaryPath2D
> & p2
, RefCountPtr
< const Lang::Structure
> * dstInfo
) const
3423 using namespace Computation
;
3425 const Concrete::Length DISTANCE_TOL
= 0.001 * Computation::the_arcdelta
;
3426 const double CUT_LOSS
= 0.01;
3430 throw Exceptions::OutOfRange( "The empty path does not intersect with anying." );
3433 std::vector
< CurvedSegType
> curvedSegments
;
3434 curvedSegments
.reserve( p2
->size( ) );
3435 std::vector
< StraightSegType
> straightSegments
;
3436 straightSegments
.reserve( p2
->size( ) );
3438 Concrete::Time steps2
= Concrete::ZERO_TIME
;
3439 const_iterator i1
= p2
->begin( );
3440 const_iterator i2
= i1
;
3442 for( ; i1
!= p2
->end( ); ++i1
, ++i2
, steps2
+= Concrete::UNIT_TIME
)
3444 if( i2
== p2
->end( ) )
3446 if( p2
->isClosed( ) )
3455 if( (*i1
)->front_
== (*i1
)->mid_
&&
3456 (*i2
)->rear_
== (*i2
)->mid_
)
3458 straightSegments
.push_back( StraightSegType( steps2
, (*i1
)->mid_
, (*i2
)->mid_
) );
3462 curvedSegments
.push_back( CurvedSegType( steps2
, Bezier::ControlPoints
< Concrete::Coords2D
>( *((*i1
)->mid_
), *((*i1
)->front_
), *((*i2
)->rear_
), *((*i2
)->mid_
) ) ) );
3467 Concrete::Time steps
= Concrete::ZERO_TIME
;
3468 const_iterator i1
= begin( );
3469 const_iterator i2
= i1
;
3471 for( ; i1
!= end( ); ++i1
, ++i2
, steps
+= Concrete::UNIT_TIME
)
3485 /* if the segment is straight a dedicated intersection-finder is invoked
3487 if( (*i1
)->front_
== (*i1
)->mid_
&&
3488 (*i2
)->rear_
== (*i2
)->mid_
)
3491 Concrete::Time res2
;
3492 if( p2
->lineSegmentIntersection( & res2
, & t
, *(*i1
)->mid_
, *(*i2
)->mid_
) )
3496 Helpers::intersection_info_resultFactory
.set( "other", Helpers::newValHandle( new Lang::PathSlider2D( p2
, res2
) ) );
3497 *dstInfo
= Helpers::intersection_info_resultFactory
.build( );
3504 Bezier::ControlPoints
< Concrete::Coords2D
> seg_a( *((*i1
)->mid_
), *((*i1
)->front_
), *((*i2
)->rear_
), *((*i2
)->mid_
) );
3505 Bezier::PolyCoeffs
< Concrete::Coords2D
> seg_a_coeffs( seg_a
);
3507 /* A segment may intersect with the other path at several places, but only the first shall be returned.
3508 * This makes it useful to track the earliest intersection time we've seen so far. Then segments that are
3509 * after this point doesn't have to be searched at all, and segments before this point need only be
3510 * searched up to this point.
3512 Concrete::Time t
= Concrete::HUGE_TIME
;
3513 Concrete::Time res2
= Concrete::HUGE_TIME
;
3515 /* Otherwise, I first scan the straight parts of the other path in order to find the easy intersections
3516 * as soon as possible.
3519 typedef typeof straightSegments ListType
;
3520 for( ListType::const_iterator i
= straightSegments
.begin( ); i
!= straightSegments
.end( ); ++i
)
3522 Concrete::Coords2D rInv
= ( *(i
->second
) - *(i
->first
) ) * ( 1. / Concrete::innerScalar( *(i
->second
) - *(i
->first
), *(i
->second
) - *(i
->first
) ) );
3523 Concrete::Coords2D
n( i
->first
->y_
- i
->second
->y_
, i
->second
->x_
- i
->first
->x_
);
3524 double offset
= Concrete::innerScalar( n
, *i
->first
);
3526 seg_a_coeffs
.hyperplaneIntersections( tmp
, n
, offset
);
3527 for( double * tmpi
= tmp
; *tmpi
!= HUGE_VAL
; ++tmpi
)
3529 double t2( Concrete::innerScalar( rInv
, seg_a_coeffs
.point( *tmpi
) - *(i
->first
) ) );
3530 if( 0 <= t2
&& t2
<= 1 )
3532 if( Concrete::Time( *tmpi
) < t
)
3534 t
= Concrete::Time( *tmpi
);
3535 res2
= i
->steps
+ Shapes::straightLineArcTime( t2
);
3542 /* The remaining segments are pushed onto a queue
3544 std::list
< SegmentSectionPair2D
* > work
;
3546 /* Keep track of the memory we use:
3548 PtrOwner_back_Access
< std::list
< const SegmentPolynomialPair2D
* > > baseSegsMemory
;
3551 typedef typeof curvedSegments ListType
;
3552 for( ListType::const_iterator seg_b
= curvedSegments
.begin( ); seg_b
!= curvedSegments
.end( ); ++seg_b
)
3554 SegmentSectionPair2D
* workItem
;
3555 SegmentPolynomialPair2D
* baseSegs
= new SegmentPolynomialPair2D( steps
, seg_a
, seg_b
->steps
, *seg_b
);
3556 baseSegsMemory
.push_back( baseSegs
);
3557 if( t
< Concrete::HUGE_TIME
)
3559 workItem
= new SegmentSectionPair2D( baseSegs
, Concrete::ZERO_TIME
, t
, Concrete::ZERO_TIME
, Concrete::UNIT_TIME
);
3563 workItem
= new SegmentSectionPair2D( baseSegs
, Concrete::ZERO_TIME
, Concrete::UNIT_TIME
, Concrete::ZERO_TIME
, Concrete::UNIT_TIME
);
3566 pushOverlappingDeleteOther( work
, workItem
);
3570 /* Then we do the work...
3572 while( ! work
.empty( ) )
3574 SegmentSectionPair2D
* workItem
= work
.front( );
3576 workItem
->update_t_a1( t
);
3577 if( workItem
->isEmpty( ) )
3583 const Concrete::Time t_tol_1
= Computation::the_arcdelta
/ workItem
->maxSpeed_a( );
3584 const Concrete::Time t_tol_2
= Computation::the_arcdelta
/ workItem
->maxSpeed_b( );
3588 if( workItem
->splitTimes( & t1
, & t2
, 0.001 * t_tol_1
, 0.001 * t_tol_2
, DISTANCE_TOL
) // too low precision gives erraneous results!
3589 || workItem
->distanceAt( t1
, t2
) < DISTANCE_TOL
)
3594 res2
= workItem
->steps_b( ) + t2
;
3596 // We have an intersection; only two children to consider
3597 /* When splitTimes returned true, DO WE HAVE TO convergence by ourselves? */
3598 pushOverlappingDeleteOther( work
, workItem
->cutAfterBefore( t1
, t2
, CUT_LOSS
* t_tol_1
, CUT_LOSS
* t_tol_2
) );
3599 pushOverlappingDeleteOther( work
, workItem
->cutAfterAfter( t1
, t2
, CUT_LOSS
* t_tol_1
, CUT_LOSS
* t_tol_2
) );
3603 // No intersection; four children to consider
3604 pushOverlappingDeleteOther( work
, workItem
->cutBeforeBefore( t1
, t2
, CUT_LOSS
* t_tol_1
, CUT_LOSS
* t_tol_2
) );
3605 pushOverlappingDeleteOther( work
, workItem
->cutBeforeAfter( t1
, t2
, CUT_LOSS
* t_tol_1
, CUT_LOSS
* t_tol_2
) );
3606 pushOverlappingDeleteOther( work
, workItem
->cutAfterBefore( t1
, t2
, CUT_LOSS
* t_tol_1
, CUT_LOSS
* t_tol_2
) );
3607 pushOverlappingDeleteOther( work
, workItem
->cutAfterAfter( t1
, t2
, CUT_LOSS
* t_tol_1
, CUT_LOSS
* t_tol_2
) );
3612 if( t
< Concrete::HUGE_TIME
) // HUGE_VAL means that no intersection was found along the current segment.
3616 Helpers::intersection_info_resultFactory
.set( "other", Helpers::newValHandle( new Lang::PathSlider2D( p2
, res2
) ) );
3617 *dstInfo
= Helpers::intersection_info_resultFactory
.build( );
3623 throw Exceptions::OutOfRange( "Failed to find intersection. Probably because there was no intersection." );
3626 Concrete::SplineTime
3627 Lang::ElementaryPath2D::continuousApproximator( const RefCountPtr
< const Lang::ElementaryPath2D
> & p2
, RefCountPtr
< const Lang::Structure
> * dstInfo
) const
3629 using namespace Computation
;
3631 /* Note that this algorithm will try to detect intsersections.
3632 * When an intersection is found, it should be possible to switch to the algorithm that is specialized for intersection computations.
3633 * However, as I am not sure that the tolerances are set correctly, this is one reason why calling that algorithm may be dangerous.
3634 * Additionally, calling the intersection algorithm does not generalize to the 3D case, and it could be difficult to take advantage of all
3635 * the work that has already been done in this algorithm.
3636 * What we still should to is to switch to an overlap-detection algorithm instead of the distance algorithm when an intersection has been
3637 * encountered, but this optimization is not implemented at the moment.
3640 const Concrete::Length DISTANCE_TOL
= 0.001 * Computation::the_arcdelta
;
3641 const double CUT_LOSS
= 0.01;
3645 throw Exceptions::OutOfRange( "The empty path does not approximate anying." );
3649 throw Exceptions::OutOfRange( "The empty path cannot be the approximation target." );
3652 Concrete::Time resSteps
= Concrete::HUGE_TIME
;
3653 Concrete::Time resFrac
= 0;
3654 Concrete::Time res2
= 0;
3656 /* In this algorithm, we always work with the distance between the paths squared.
3658 Concrete::LengthSquared
bestDist( HUGE_VAL
);
3659 const Concrete::LengthSquared
INTERSECT_DIST( 1e-10 );
3660 const Concrete::LengthSquared
ZERO_DIST( 0 );
3662 /* First, the second path is divided up into its straight and (potentially) curved segments.
3663 * During the scan, we also compute a first crude estimate of the distance between the paths by
3664 * computing the distance (squared) between all path points.
3666 std::vector
< CurvedSegType
> curvedSegments
;
3667 curvedSegments
.reserve( p2
->size( ) );
3668 std::vector
< StraightSegType
> straightSegments
;
3669 straightSegments
.reserve( p2
->size( ) );
3671 Concrete::Time steps2
= Concrete::ZERO_TIME
;
3672 const_iterator i1
= p2
->begin( );
3673 const_iterator i2
= i1
;
3675 for( ; i1
!= p2
->end( ); ++i1
, ++i2
, steps2
+= Concrete::UNIT_TIME
)
3677 Concrete::Time steps
= Concrete::ZERO_TIME
;
3678 for( const_iterator j
= begin( ); j
!= end( ); ++j
, steps
+= Concrete::UNIT_TIME
)
3680 Concrete::LengthSquared d
= ( *((*i1
)->mid_
) - *((*j
)->mid_
) ).normSquared( );
3681 if( d
<= INTERSECT_DIST
)
3683 /* We interpret this as an intersection. Save only if old optimum was not an intersection,
3684 * or this point is earlier than old optimum.
3686 * At this point, the fraction is still 0.
3688 if( bestDist
> ZERO_DIST
||
3693 bestDist
= ZERO_DIST
;
3696 else if( d
< bestDist
)
3698 resSteps
= steps
; /* At this point, the fraction is still 0. */
3703 if( i2
== p2
->end( ) )
3705 if( p2
->isClosed( ) )
3714 if( (*i1
)->front_
== (*i1
)->mid_
&&
3715 (*i2
)->rear_
== (*i2
)->mid_
)
3717 straightSegments
.push_back( StraightSegType( steps2
, (*i1
)->mid_
, (*i2
)->mid_
) );
3721 curvedSegments
.push_back( CurvedSegType( steps2
, Bezier::ControlPoints
< Concrete::Coords2D
>( *((*i1
)->mid_
), *((*i1
)->front_
), *((*i2
)->rear_
), *((*i2
)->mid_
) ) ) );
3726 /* Next, the crude estimate is refined by comparing all straight line segments.
3729 Concrete::Time steps
= Concrete::ZERO_TIME
;
3730 const_iterator i1
= begin( );
3731 const_iterator i2
= i1
;
3733 for( ; i1
!= end( ); ++i1
, ++i2
, steps
+= Concrete::UNIT_TIME
)
3747 if( (*i1
)->front_
== (*i1
)->mid_
&&
3748 (*i2
)->rear_
== (*i2
)->mid_
)
3750 /* Straight line segment. This is the only case we care about at this point.
3752 Concrete::Coords2D l0
= *((*i1
)->mid_
);
3753 Concrete::Coords2D l1
= *((*i2
)->mid_
);
3754 Concrete::Coords2D r
= l1
- l0
;
3755 Concrete::Coords2D rInv
= r
* ( 1. / Concrete::innerScalar( r
, r
) );
3756 /* Distance between straight line segments is solved as a polytope distance problem. */
3758 const size_t DIM
= 2;
3759 double ga
[ N
* DIM
];
3760 double gb
[ N
* DIM
];
3765 *dstg
= Concrete::Length::offtype( l0
.x_
);
3767 *dstg
= Concrete::Length::offtype( l0
.y_
);
3769 *dstg
= Concrete::Length::offtype( l1
.x_
);
3771 *dstg
= Concrete::Length::offtype( l1
.y_
);
3773 typedef typeof straightSegments ListType
;
3774 for( ListType::const_iterator i
= straightSegments
.begin( ); i
!= straightSegments
.end( ); ++i
)
3778 *dstg
= Concrete::Length::offtype( i
->first
->x_
);
3780 *dstg
= Concrete::Length::offtype( i
->first
->y_
);
3782 *dstg
= Concrete::Length::offtype( i
->second
->x_
);
3784 *dstg
= Concrete::Length::offtype( i
->second
->y_
);
3786 double double_distSquaredLow
;
3787 double double_distSquaredHigh
;
3788 QPSolverStatus status
;
3789 Computation::polytope_distance_generator_form( DIM
,
3792 & double_distSquaredLow
, & double_distSquaredHigh
,
3793 Concrete::LengthSquared::offtype( bestDist
), -1,
3795 0.5 * Concrete::Length::offtype( Computation::theDistanceTol
), /* Lagrange multiplier tolerance. */
3799 if( status
.code
!= Computation::QP_OK
)
3801 const char * binaryFilename
= "qp-fail-data.binary";
3802 Computation::polytope_distance_generator_form_write_binary_data( binaryFilename
,
3806 std::ostringstream msg
;
3807 msg
<< "QP solver status: " << status
.code_str( ) << ", with reason: " << status
.sub_str( )
3808 << ", binary data was written to \"" << binaryFilename
<< "\"." ;
3809 throw Exceptions::InternalError( strrefdup( msg
) );
3811 Concrete::LengthSquared
distSquaredLow( double_distSquaredLow
);
3812 Concrete::LengthSquared
distSquaredHigh( double_distSquaredHigh
);
3813 if( distSquaredHigh
<= INTERSECT_DIST
)
3815 /* We interpret this as an intersection. Save only if old optimum was not an intersection,
3816 * or this point is earlier than old optimum.
3818 Concrete::Time t
= Shapes::straightLineArcTime( Concrete::innerScalar( rInv
, Concrete::Coords2D( ya
[0], ya
[1] ) - l0
) );
3819 if( bestDist
> ZERO_DIST
||
3820 steps
+ t
< resSteps
+ resFrac
)
3824 Concrete::Coords2D l0b
= *(i
->first
);
3825 Concrete::Coords2D l1b
= *(i
->second
);
3826 Concrete::Coords2D rb
= l1b
- l0b
;
3827 Concrete::Coords2D rInv_b
= rb
* ( 1. / Concrete::innerScalar( rb
, rb
) );
3828 res2
= i
->steps
+ Shapes::straightLineArcTime( Concrete::innerScalar( rInv_b
, Concrete::Coords2D( yb
[0], yb
[1] ) - l0b
) );
3829 bestDist
= ZERO_DIST
;
3832 else if( distSquaredLow
< bestDist
)
3834 /* The optimum has been improved. Save new optimum. */
3836 resFrac
= Shapes::straightLineArcTime( Concrete::innerScalar( rInv
, Concrete::Coords2D( ya
[0], ya
[1] ) - l0
) );
3837 Concrete::Coords2D l0b
= *(i
->first
);
3838 Concrete::Coords2D l1b
= *(i
->second
);
3839 Concrete::Coords2D rb
= l1b
- l0b
;
3840 Concrete::Coords2D rInv_b
= rb
* ( 1. / Concrete::innerScalar( rb
, rb
) );
3841 res2
= i
->steps
+ Shapes::straightLineArcTime( Concrete::innerScalar( rInv_b
, Concrete::Coords2D( yb
[0], yb
[1] ) - l0b
) );
3842 bestDist
= distSquaredLow
;
3849 /* Next, we scan *this path, and for each segment we pair it up with all segments on p2.
3850 * In the case that two straight segments are paired, their distance has already been computed, but any other
3851 * combination of segments is pushed to a queue of work to be done.
3854 /* The remaining segments are pushed onto a queue
3856 std::list
< SegmentSectionPair2D
* > work
;
3858 /* Keep track of the memory we use:
3860 PtrOwner_back_Access
< std::list
< const SegmentPolynomialPair2D
* > > baseSegsMemory
;
3862 Concrete::Time steps
= Concrete::ZERO_TIME
;
3863 const_iterator i1
= begin( );
3864 const_iterator i2
= i1
;
3866 for( ; i1
!= end( ); ++i1
, ++i2
, steps
+= Concrete::UNIT_TIME
)
3880 if( (*i1
)->front_
== (*i1
)->mid_
&&
3881 (*i2
)->rear_
== (*i2
)->mid_
)
3883 /* Straight line segment. The distance to straight line segments on the other path has already been computed.
3885 typedef typeof curvedSegments ListType
;
3886 for( ListType::const_iterator seg_b
= curvedSegments
.begin( ); seg_b
!= curvedSegments
.end( ); ++seg_b
)
3888 SegmentPolynomialPair2D
* baseSegs
= new SegmentPolynomialPair2D( steps
, *((*i1
)->mid_
), *((*i2
)->mid_
), seg_b
->steps
, *seg_b
);
3889 baseSegsMemory
.push_back( baseSegs
);
3890 pushCloseDeleteOther( & work
, new SegmentSectionPair2D( baseSegs
, Concrete::ZERO_TIME
, Concrete::UNIT_TIME
, Concrete::ZERO_TIME
, Concrete::UNIT_TIME
), bestDist
);
3895 /* General segment. (If it happens to be straight, we don't care.)
3897 Bezier::ControlPoints
< Concrete::Coords2D
> seg_a( *((*i1
)->mid_
), *((*i1
)->front_
), *((*i2
)->rear_
), *((*i2
)->mid_
) );
3900 typedef typeof straightSegments ListType
;
3901 for( ListType::const_iterator i
= straightSegments
.begin( ); i
!= straightSegments
.end( ); ++i
)
3903 SegmentPolynomialPair2D
* baseSegs
= new SegmentPolynomialPair2D( steps
, seg_a
, i
->steps
, *(i
->first
), *(i
->second
) );
3904 baseSegsMemory
.push_back( baseSegs
);
3905 pushCloseDeleteOther( & work
, new SegmentSectionPair2D( baseSegs
, Concrete::ZERO_TIME
, Concrete::UNIT_TIME
, Concrete::ZERO_TIME
, Concrete::UNIT_TIME
), bestDist
);
3910 typedef typeof curvedSegments ListType
;
3911 for( ListType::const_iterator seg_b
= curvedSegments
.begin( ); seg_b
!= curvedSegments
.end( ); ++seg_b
)
3913 SegmentPolynomialPair2D
* baseSegs
= new SegmentPolynomialPair2D( steps
, seg_a
, seg_b
->steps
, *seg_b
);
3914 baseSegsMemory
.push_back( baseSegs
);
3915 pushCloseDeleteOther( & work
, new SegmentSectionPair2D( baseSegs
, Concrete::ZERO_TIME
, Concrete::UNIT_TIME
, Concrete::ZERO_TIME
, Concrete::UNIT_TIME
), bestDist
);
3921 /* Now we reach the main part of the algorithm...
3923 while( ! work
.empty( ) )
3925 SegmentSectionPair2D
* workItem
= work
.front( );
3927 if( workItem
->precomputedHullDistanceSquared( ) > ( ( bestDist
== 0 ) ? INTERSECT_DIST
: bestDist
) )
3929 /* A better solution has been found since this piece of work was created.
3934 if( bestDist
<= INTERSECT_DIST
)
3936 /* If we have detected an intersection, we can skip any piece of work that cannot yield an earlier optimum. */
3937 if( workItem
->steps_a( ) > resSteps
)
3942 if( workItem
->steps_a( ) == resSteps
)
3944 workItem
->update_t_a1( resFrac
);
3947 if( workItem
->isEmpty( ) )
3953 const Concrete::Time t_tol_1
= Computation::the_arcdelta
/ workItem
->maxSpeed_a( );
3954 const Concrete::Time t_tol_2
= Computation::the_arcdelta
/ workItem
->maxSpeed_b( );
3958 bool intersect
= workItem
->splitTimes( & t1
, & t2
, 0.001 * t_tol_1
, 0.001 * t_tol_2
, DISTANCE_TOL
); // too low precision gives erraneous results!
3959 Concrete::LengthSquared d
= workItem
->squaredDistanceAt( t1
, t2
);
3960 intersect
= intersect
|| d
< INTERSECT_DIST
;
3963 if( bestDist
> ZERO_DIST
3964 || workItem
->globalTime_a( t1
) < resSteps
+ resFrac
)
3966 bestDist
= ZERO_DIST
;
3967 resSteps
= workItem
->steps_a( );
3969 res2
= workItem
->steps_b( ) + t2
;
3972 else if( d
< bestDist
)
3975 resSteps
= workItem
->steps_a( );
3977 res2
= workItem
->steps_b( ) + t2
;
3981 // We have an intersection; only two children to consider
3982 pushCloseDeleteOther( & work
, workItem
->cutAfterBefore( t1
, t2
, CUT_LOSS
* t_tol_1
, CUT_LOSS
* t_tol_2
), bestDist
);
3983 pushCloseDeleteOther( & work
, workItem
->cutAfterAfter( t1
, t2
, CUT_LOSS
* t_tol_1
, CUT_LOSS
* t_tol_2
), bestDist
);
3987 // No intersection; four children to consider
3988 pushCloseDeleteOther( & work
, workItem
->cutBeforeBefore( t1
, t2
, CUT_LOSS
* t_tol_1
, CUT_LOSS
* t_tol_2
), bestDist
);
3989 pushCloseDeleteOther( & work
, workItem
->cutBeforeAfter( t1
, t2
, CUT_LOSS
* t_tol_1
, CUT_LOSS
* t_tol_2
), bestDist
);
3990 pushCloseDeleteOther( & work
, workItem
->cutAfterBefore( t1
, t2
, CUT_LOSS
* t_tol_1
, CUT_LOSS
* t_tol_2
), bestDist
);
3991 pushCloseDeleteOther( & work
, workItem
->cutAfterAfter( t1
, t2
, CUT_LOSS
* t_tol_1
, CUT_LOSS
* t_tol_2
), bestDist
);
3996 if( resSteps
== Concrete::HUGE_TIME
)
3998 throw Exceptions::InternalError( "Path to path approximation: No result was produced." );
4003 Helpers::approximator_info_resultFactory
.set( "dist", Helpers::newValHandle( new Lang::Length( sqrtPhysical( bestDist
) ) ) );
4004 Helpers::approximator_info_resultFactory
.set( "other", Helpers::newValHandle( new Lang::PathSlider2D( p2
, res2
) ) );
4005 *dstInfo
= Helpers::approximator_info_resultFactory
.build( );
4007 return Concrete::SplineTime( resSteps
+ resFrac
);
4011 Lang::ElementaryPath2D::pushOverlappingDeleteOther( std::list
< Computation::SegmentSectionPair2D
* > & work
, Computation::SegmentSectionPair2D
* item
)
4013 if( item
->convexHullOverlap( ) )
4015 work
.push_back( item
);
4024 Lang::ElementaryPath2D::pushCloseDeleteOther( std::list
< Computation::SegmentSectionPair2D
* > * work
, Computation::SegmentSectionPair2D
* item
, Concrete::LengthSquared closeDist
)
4026 if( item
->isEmpty( )
4027 || item
->convexHullSeparatedBy( closeDist
) )
4033 work
->push_back( item
);
4038 Computation::SegmentPolynomialPair2D::kInit( )
4040 kxaD0_0
= polyCoeffs_a
.z0_
.x_
;
4041 kxaD0_1
= polyCoeffs_a
.z1_
.x_
.offtype
< 0, 1 >( );
4042 kxaD0_2
= polyCoeffs_a
.z2_
.x_
.offtype
< 0, 2 >( );
4043 kxaD0_3
= polyCoeffs_a
.z3_
.x_
.offtype
< 0, 3 >( );
4046 kxaD1_1
= 2 * kxaD0_2
;
4047 kxaD1_2
= 3 * kxaD0_3
;
4050 kxaD2_1
= 2 * kxaD1_2
;
4052 kyaD0_0
= polyCoeffs_a
.z0_
.y_
;
4053 kyaD0_1
= polyCoeffs_a
.z1_
.y_
.offtype
< 0, 1 >( );
4054 kyaD0_2
= polyCoeffs_a
.z2_
.y_
.offtype
< 0, 2 >( );
4055 kyaD0_3
= polyCoeffs_a
.z3_
.y_
.offtype
< 0, 3 >( );
4058 kyaD1_1
= 2 * kyaD0_2
;
4059 kyaD1_2
= 3 * kyaD0_3
;
4062 kyaD2_1
= 2 * kyaD1_2
;
4064 kxbD0_0
= polyCoeffs_b
.z0_
.x_
;
4065 kxbD0_1
= polyCoeffs_b
.z1_
.x_
.offtype
< 0, 1 >( );
4066 kxbD0_2
= polyCoeffs_b
.z2_
.x_
.offtype
< 0, 2 >( );
4067 kxbD0_3
= polyCoeffs_b
.z3_
.x_
.offtype
< 0, 3 >( );
4070 kxbD1_1
= 2 * kxbD0_2
;
4071 kxbD1_2
= 3 * kxbD0_3
;
4074 kxbD2_1
= 2 * kxbD1_2
;
4076 kybD0_0
= polyCoeffs_b
.z0_
.y_
;
4077 kybD0_1
= polyCoeffs_b
.z1_
.y_
.offtype
< 0, 1 >( );
4078 kybD0_2
= polyCoeffs_b
.z2_
.y_
.offtype
< 0, 2 >( );
4079 kybD0_3
= polyCoeffs_b
.z3_
.y_
.offtype
< 0, 3 >( );
4082 kybD1_1
= 2 * kybD0_2
;
4083 kybD1_2
= 3 * kybD0_3
;
4086 kybD2_1
= 2 * kybD1_2
;
4089 Computation::SegmentPolynomialPair2D::SegmentPolynomialPair2D( Concrete::Time steps_a
, const Bezier::ControlPoints
< Concrete::Coords2D
> & seg_a
, Concrete::Time steps_b
, const Bezier::ControlPoints
< Concrete::Coords2D
> & seg_b
)
4090 : polyCoeffs_a( seg_a
), straightLineSeg_a_( false ), polyCoeffs_b( seg_b
), straightLineSeg_b_( false ),
4091 steps_a_( steps_a
), steps_b_( steps_b
)
4095 maxSpeed_a_
= max( max( hypotPhysical( seg_a
.p1_
.x_
- seg_a
.p0_
.x_
, seg_a
.p1_
.y_
- seg_a
.p0_
.y_
),
4096 hypotPhysical( seg_a
.p2_
.x_
- seg_a
.p1_
.x_
, seg_a
.p2_
.y_
- seg_a
.p1_
.y_
) ),
4097 hypotPhysical( seg_a
.p3_
.x_
- seg_a
.p2_
.x_
, seg_a
.p3_
.y_
- seg_a
.p2_
.y_
) ).offtype
< 0, 1 >( );
4098 maxSpeed_b_
= max( max( hypotPhysical( seg_b
.p1_
.x_
- seg_b
.p0_
.x_
, seg_b
.p1_
.y_
- seg_b
.p0_
.y_
),
4099 hypotPhysical( seg_b
.p2_
.x_
- seg_b
.p1_
.x_
, seg_b
.p2_
.y_
- seg_b
.p1_
.y_
) ),
4100 hypotPhysical( seg_b
.p3_
.x_
- seg_b
.p2_
.x_
, seg_b
.p3_
.y_
- seg_b
.p2_
.y_
) ).offtype
< 0, 1 >( );
4103 Computation::SegmentPolynomialPair2D::SegmentPolynomialPair2D( Concrete::Time steps_a
, const Concrete::Coords2D
& seg_a_p0
, const Concrete::Coords2D
& seg_a_p3
, Concrete::Time steps_b
, const Bezier::ControlPoints
< Concrete::Coords2D
> & seg_b
)
4104 : polyCoeffs_a( Bezier::ControlPoints
< Concrete::Coords2D
>( seg_a_p0
, seg_a_p0
, seg_a_p3
, seg_a_p3
) ), straightLineSeg_a_( true ), polyCoeffs_b( seg_b
), straightLineSeg_b_( false ),
4105 steps_a_( steps_a
), steps_b_( steps_b
)
4109 maxSpeed_a_
= hypotPhysical( seg_a_p3
.x_
- seg_a_p0
.x_
, seg_a_p3
.y_
- seg_a_p0
.y_
).offtype
< 0, 1 >( );
4110 maxSpeed_b_
= max( max( hypotPhysical( seg_b
.p1_
.x_
- seg_b
.p0_
.x_
, seg_b
.p1_
.y_
- seg_b
.p0_
.y_
),
4111 hypotPhysical( seg_b
.p2_
.x_
- seg_b
.p1_
.x_
, seg_b
.p2_
.y_
- seg_b
.p1_
.y_
) ),
4112 hypotPhysical( seg_b
.p3_
.x_
- seg_b
.p2_
.x_
, seg_b
.p3_
.y_
- seg_b
.p2_
.y_
) ).offtype
< 0, 1 >( );
4115 Computation::SegmentPolynomialPair2D::SegmentPolynomialPair2D( Concrete::Time steps_a
, const Bezier::ControlPoints
< Concrete::Coords2D
> & seg_a
, Concrete::Time steps_b
, const Concrete::Coords2D
& seg_b_p0
, const Concrete::Coords2D
& seg_b_p3
)
4116 : polyCoeffs_a( seg_a
), straightLineSeg_a_( false ), polyCoeffs_b( Bezier::ControlPoints
< Concrete::Coords2D
>( seg_b_p0
, seg_b_p0
, seg_b_p3
, seg_b_p3
) ), straightLineSeg_b_( true ),
4117 steps_a_( steps_a
), steps_b_( steps_b
)
4121 maxSpeed_a_
= max( max( hypotPhysical( seg_a
.p1_
.x_
- seg_a
.p0_
.x_
, seg_a
.p1_
.y_
- seg_a
.p0_
.y_
),
4122 hypotPhysical( seg_a
.p2_
.x_
- seg_a
.p1_
.x_
, seg_a
.p2_
.y_
- seg_a
.p1_
.y_
) ),
4123 hypotPhysical( seg_a
.p3_
.x_
- seg_a
.p2_
.x_
, seg_a
.p3_
.y_
- seg_a
.p2_
.y_
) ).offtype
< 0, 1 >( );
4124 maxSpeed_b_
= hypotPhysical( seg_b_p3
.x_
- seg_b_p0
.x_
, seg_b_p3
.y_
- seg_b_p0
.y_
).offtype
< 0, 1 >( );
4128 Computation::SegmentPolynomialPair2D::splitTimes( Concrete::Time
* dst_a
, Concrete::Time
* dst_b
, const Concrete::Time t_alow
, const Concrete::Time t_ahigh
, const Concrete::Time t_atol
, const Concrete::Time t_blow
, const Concrete::Time t_bhigh
, const Concrete::Time t_btol
, Concrete::Length distance_tol
) const
4132 Concrete::LengthSquared
last_f( HUGE_VAL
);
4134 const double GRID
= 7;
4135 const Concrete::Time STEP_A
= ( t_ahigh
- t_alow
) / GRID
;
4136 const Concrete::Time STEP_B
= ( t_bhigh
- t_blow
) / GRID
;
4137 for( Concrete::Time tta
= t_alow
+ 0.5 * STEP_A
; tta
< t_ahigh
; tta
+= STEP_A
)
4139 for( Concrete::Time ttb
= t_blow
+ 0.5 * STEP_B
; ttb
< t_bhigh
; ttb
+= STEP_B
)
4141 Concrete::LengthSquared tmp
= squaredDistanceAt( tta
, ttb
);
4151 Concrete::Time last_ta
= - Concrete::UNIT_TIME
;
4152 Concrete::Time last_tb
= - Concrete::UNIT_TIME
;
4154 while( ta
< last_ta
- t_atol
|| ta
> last_ta
+ t_atol
||
4155 tb
< last_tb
- t_btol
|| tb
> last_tb
+ t_btol
)
4159 /* The derivatives below are actually computed for the objective divided by two
4160 * but since we will divide one by the other, it doesn't matter.
4162 Physical
< 2, -1 > objectiveD1a( 0 );
4163 Physical
< 2, -1 > objectiveD1b( 0 );
4164 Physical
< 2, -2 > objectiveD2aa( 0 );
4165 Physical
< 2, -2 > objectiveD2ab( 0 );
4166 Physical
< 2, -2 > objectiveD2bb( 0 );
4170 Physical
< 1, 0 > faD0
= kxaD0_0
+ ta
* ( kxaD0_1
+ ta
* ( kxaD0_2
+ ta
* kxaD0_3
) );
4171 Physical
< 1, 0 > fbD0
= kxbD0_0
+ tb
* ( kxbD0_1
+ tb
* ( kxbD0_2
+ tb
* kxbD0_3
) );
4172 Physical
< 1, -1 > faD1
= kxaD1_0
+ ta
* ( kxaD1_1
+ ta
* kxaD1_2
);
4173 Physical
< 1, -1 > fbD1
= kxbD1_0
+ tb
* ( kxbD1_1
+ tb
* kxbD1_2
);
4174 Physical
< 1, -2 > faD2
= kxaD2_0
+ ta
* kxaD2_1
;
4175 Physical
< 1, -2 > fbD2
= kxbD2_0
+ tb
* kxbD2_1
;
4177 Physical
< 1, 0 > deltaD0
= faD0
- fbD0
;
4179 objectiveD1a
+= deltaD0
* faD1
;
4180 objectiveD1b
+= - deltaD0
* fbD1
;
4181 objectiveD2aa
+= faD1
* faD1
+ deltaD0
* faD2
;
4182 objectiveD2bb
+= fbD1
* fbD1
- deltaD0
* fbD2
;
4183 objectiveD2ab
+= - faD1
* fbD1
;
4188 Physical
< 1, 0 > faD0
= kyaD0_0
+ ta
* ( kyaD0_1
+ ta
* ( kyaD0_2
+ ta
* kyaD0_3
) );
4189 Physical
< 1, 0 > fbD0
= kybD0_0
+ tb
* ( kybD0_1
+ tb
* ( kybD0_2
+ tb
* kybD0_3
) );
4190 Physical
< 1, -1 > faD1
= kyaD1_0
+ ta
* ( kyaD1_1
+ ta
* kyaD1_2
);
4191 Physical
< 1, -1 > fbD1
= kybD1_0
+ tb
* ( kybD1_1
+ tb
* kybD1_2
);
4192 Physical
< 1, -2 > faD2
= kyaD2_0
+ ta
* kyaD2_1
;
4193 Physical
< 1, -2 > fbD2
= kybD2_0
+ tb
* kybD2_1
;
4195 Physical
< 1, 0 > deltaD0
= faD0
- fbD0
;
4197 objectiveD1a
+= deltaD0
* faD1
;
4198 objectiveD1b
+= - deltaD0
* fbD1
;
4199 objectiveD2aa
+= faD1
* faD1
+ deltaD0
* faD2
;
4200 objectiveD2bb
+= fbD1
* fbD1
- deltaD0
* fbD2
;
4201 objectiveD2ab
+= - faD1
* fbD1
;
4204 Concrete::Time step_a
;
4205 Concrete::Time step_b
;
4206 if( objectiveD2aa
<= Physical
< 2, -2 >( 0 ) || objectiveD2bb
<= Physical
< 2, -2 >( 0 ) || objectiveD2ab
* objectiveD2ab
>= objectiveD2aa
* objectiveD2bb
)
4208 /* The minimization problem is not convex, locally.
4209 * Knowing that we have examined a grid of candidate points, we stop the minimization here.
4214 // double D1norm = hypotPhysical( objecitveD1a, objectiveD1b );
4215 // double boxDiameter = max( t_ahigh - t_alow, t_bhigh - t_blow );
4216 // step_a = - objectiveD1a * ( boxDiameter / D1norm );
4217 // step_b = - objectiveD1b * ( boxDiameter / D1norm );
4221 Physical
< -4, 4 > invDet
= 1. / ( objectiveD2aa
* objectiveD2bb
- objectiveD2ab
* objectiveD2ab
);
4222 // here's the division where the missing factors of 2 cancel.
4223 step_a
= - invDet
* ( objectiveD2bb
* objectiveD1a
- objectiveD2ab
* objectiveD1b
);
4224 step_b
= - invDet
* ( - objectiveD2ab
* objectiveD1a
+ objectiveD2aa
* objectiveD1b
);
4227 bool onBound
= false;
4231 if( ta
+ alpha
* step_a
< t_alow
)
4233 alpha
= ( t_alow
- ta
) / step_a
;
4236 else if( ta
+ alpha
* step_a
> t_ahigh
)
4238 alpha
= ( t_ahigh
- ta
) / step_a
;
4242 if( tb
+ alpha
* step_b
< t_blow
)
4244 alpha
= ( t_blow
- tb
) / step_b
;
4247 else if( tb
+ alpha
* step_b
> t_bhigh
)
4249 alpha
= ( t_bhigh
- tb
) / step_b
;
4257 Concrete::LengthSquared f
= squaredDistanceAt( ta
+ step_a
, tb
+ step_b
);
4258 while( f
> last_f
&& ( step_a
.abs( ) > t_atol
|| step_b
.abs( ) > t_btol
) )
4260 step_a
= step_a
* 0.5;
4261 step_b
= step_b
* 0.5;
4262 f
= squaredDistanceAt( ta
+ step_a
, tb
+ step_b
);
4277 return distanceAt( ta
, tb
) < distance_tol
;
4280 Concrete::LengthSquared
4281 Computation::SegmentPolynomialPair2D::squaredDistanceAt( Concrete::Time ta
, Concrete::Time tb
) const
4283 Concrete::Length fxaD0
= kxaD0_0
+ ta
* ( kxaD0_1
+ ta
* ( kxaD0_2
+ ta
* kxaD0_3
) );
4284 Concrete::Length fyaD0
= kyaD0_0
+ ta
* ( kyaD0_1
+ ta
* ( kyaD0_2
+ ta
* kyaD0_3
) );
4285 Concrete::Length fxbD0
= kxbD0_0
+ tb
* ( kxbD0_1
+ tb
* ( kxbD0_2
+ tb
* kxbD0_3
) );
4286 Concrete::Length fybD0
= kybD0_0
+ tb
* ( kybD0_1
+ tb
* ( kybD0_2
+ tb
* kybD0_3
) );
4287 Concrete::Length deltax
= fxaD0
- fxbD0
;
4288 Concrete::Length deltay
= fyaD0
- fybD0
;
4289 return deltax
* deltax
+ deltay
* deltay
;
4293 Computation::SegmentPolynomialPair2D::distanceAt( Concrete::Time ta
, Concrete::Time tb
) const
4295 Concrete::Length fxaD0
= kxaD0_0
+ ta
* ( kxaD0_1
+ ta
* ( kxaD0_2
+ ta
* kxaD0_3
) );
4296 Concrete::Length fyaD0
= kyaD0_0
+ ta
* ( kyaD0_1
+ ta
* ( kyaD0_2
+ ta
* kyaD0_3
) );
4297 Concrete::Length fxbD0
= kxbD0_0
+ tb
* ( kxbD0_1
+ tb
* ( kxbD0_2
+ tb
* kxbD0_3
) );
4298 Concrete::Length fybD0
= kybD0_0
+ tb
* ( kybD0_1
+ tb
* ( kybD0_2
+ tb
* kybD0_3
) );
4299 Concrete::Length deltax
= fxaD0
- fxbD0
;
4300 Concrete::Length deltay
= fyaD0
- fybD0
;
4301 return hypotPhysical( deltax
, deltay
);
4304 Bezier::ControlPoints
< Concrete::Coords2D
>
4305 Computation::SegmentPolynomialPair2D::getControls_a( const Concrete::Time t_low
, const Concrete::Time t_high
) const
4307 return Bezier::ControlPoints
< Concrete::Coords2D
>( polyCoeffs_a
.subSection( t_low
.offtype
< 0, 1 >( ), t_high
.offtype
< 0, 1 >( ) ) );
4310 Bezier::ControlPoints
< Concrete::Coords2D
>
4311 Computation::SegmentPolynomialPair2D::getControls_b( const Concrete::Time t_low
, const Concrete::Time t_high
) const
4313 return Bezier::ControlPoints
< Concrete::Coords2D
>( polyCoeffs_b
.subSection( t_low
.offtype
< 0, 1 >( ), t_high
.offtype
< 0, 1 >( ) ) );
4316 Computation::SegmentSectionPair2D::SegmentSectionPair2D( const SegmentPolynomialPair2D
* _baseSegs
, Concrete::Time _t_a0
, Concrete::Time _t_a1
, Concrete::Time _t_b0
, Concrete::Time _t_b1
)
4317 : controls_a( _baseSegs
->getControls_a( _t_a0
, _t_a1
) ), controls_b( _baseSegs
->getControls_b( _t_b0
, _t_b1
) ),
4318 baseSegs( _baseSegs
), t_a0( _t_a0
), t_a1( _t_a1
), t_b0( _t_b0
), t_b1( _t_b1
)
4321 Computation::SegmentSectionPair2D
*
4322 Computation::SegmentSectionPair2D::cutAfterAfter( Concrete::Time ta
, Concrete::Time tb
, Concrete::Time tol_a
, Concrete::Time tol_b
) const
4324 return new SegmentSectionPair2D( baseSegs
, t_a0
, ta
- tol_a
, t_b0
, tb
- tol_b
);
4327 Computation::SegmentSectionPair2D
*
4328 Computation::SegmentSectionPair2D::cutAfterBefore( Concrete::Time ta
, Concrete::Time tb
, Concrete::Time tol_a
, Concrete::Time tol_b
) const
4330 return new SegmentSectionPair2D( baseSegs
, t_a0
, ta
- tol_a
, tb
+ tol_b
, t_b1
);
4333 Computation::SegmentSectionPair2D
*
4334 Computation::SegmentSectionPair2D::cutBeforeAfter( Concrete::Time ta
, Concrete::Time tb
, Concrete::Time tol_a
, Concrete::Time tol_b
) const
4336 return new SegmentSectionPair2D( baseSegs
, ta
+ tol_a
, t_a1
, t_b0
, tb
- tol_b
);
4339 Computation::SegmentSectionPair2D
*
4340 Computation::SegmentSectionPair2D::cutBeforeBefore( Concrete::Time ta
, Concrete::Time tb
, Concrete::Time tol_a
, Concrete::Time tol_b
) const
4342 return new SegmentSectionPair2D( baseSegs
, ta
+ tol_a
, t_a1
, tb
+ tol_b
, t_b1
);
4346 Computation::SegmentSectionPair2D::splitTimes( Concrete::Time
* dst_a
, Concrete::Time
* dst_b
, const Concrete::Time t_tol_a
, const Concrete::Time t_tol_b
, Concrete::Length distance_tol
) const
4348 Concrete::Time da
= 0.01 * ( t_a1
- t_a0
);
4349 Concrete::Time db
= 0.01 * ( t_b1
- t_b0
);
4350 if( baseSegs
->splitTimes( dst_a
, dst_b
,
4351 t_a0
, t_a1
, t_tol_a
,
4352 t_b0
, t_b1
, t_tol_b
,
4355 /* An intersection was found to withing tolerances. Do not adjust for robust convergence! */
4359 /* For robust convergence, make sure we don't return split times that are too close to the corners of these sections. */
4360 if( *dst_a
< t_a0
+ da
&& *dst_b
< t_b0
+ db
)
4365 else if( *dst_a
< t_a0
+ da
&& *dst_b
> t_b1
- db
)
4370 else if( *dst_a
> t_a1
- da
&& *dst_b
< t_b0
+ db
)
4375 else if( *dst_a
> t_a1
- da
&& *dst_b
> t_b1
- db
)
4384 Computation::SegmentSectionPair2D::globalTime_a( Concrete::Time t
) const
4386 return steps_a( ) + t
;
4390 Computation::SegmentSectionPair2D::distanceAt( Concrete::Time ta
, Concrete::Time tb
) const
4392 return baseSegs
->distanceAt( ta
, tb
);
4395 Concrete::LengthSquared
4396 Computation::SegmentSectionPair2D::squaredDistanceAt( Concrete::Time ta
, Concrete::Time tb
) const
4398 return baseSegs
->squaredDistanceAt( ta
, tb
);
4402 Computation::SegmentSectionPair2D::convexHullOverlap( ) const
4404 /* The implementation is based around the idea that if there is a separating line
4405 * (which there is exactly when the polygons don't overlap), there has to be
4406 * a direction of such a line that is also the direction from one point to another
4407 * within a polygon. This gives some symmetry, which is implemented by
4408 * calling a function that searches such directions in only one of the polygons.
4409 * A quick check using enclosing circles is performed first to detect the simples cases.
4411 std::vector
< const Concrete::Coords2D
* > pointSet_a( 0 );
4412 pointSet_a
.reserve( 4 );
4413 pointSet_a
.push_back( & controls_a
.p0_
);
4414 pointSet_a
.push_back( & controls_a
.p1_
);
4415 pointSet_a
.push_back( & controls_a
.p2_
);
4416 pointSet_a
.push_back( & controls_a
.p3_
);
4417 std::vector
< const Concrete::Coords2D
* > pointSet_b( 0 );
4418 pointSet_b
.reserve( 4 );
4419 pointSet_b
.push_back( & controls_b
.p0_
);
4420 pointSet_b
.push_back( & controls_b
.p1_
);
4421 pointSet_b
.push_back( & controls_b
.p2_
);
4422 pointSet_b
.push_back( & controls_b
.p3_
);
4424 /* Do the circle-circle check
4427 Concrete::Coords2D
ca( 0, 0 );
4428 for( std::vector
< const Concrete::Coords2D
* >::const_iterator i
= pointSet_a
.begin( );
4429 i
!= pointSet_a
.end( );
4436 Concrete::Coords2D
cb( 0, 0 );
4437 for( std::vector
< const Concrete::Coords2D
* >::const_iterator i
= pointSet_b
.begin( );
4438 i
!= pointSet_b
.end( );
4445 Concrete::Length ra
= 0;
4446 for( std::vector
< const Concrete::Coords2D
* >::const_iterator i
= pointSet_a
.begin( );
4447 i
!= pointSet_a
.end( );
4450 ra
= max( ra
, hypotPhysical( ca
.x_
- (*i
)->x_
, ca
.y_
- (*i
)->y_
) );
4453 Concrete::Length rb
= 0;
4454 for( std::vector
< const Concrete::Coords2D
* >::const_iterator i
= pointSet_b
.begin( );
4455 i
!= pointSet_b
.end( );
4458 rb
= max( rb
, hypotPhysical( cb
.x_
- (*i
)->x_
, cb
.y_
- (*i
)->y_
) );
4461 if( ( ra
+ rb
) * ( ra
+ rb
) < ( ca
.x_
- cb
.x_
) * ( ca
.x_
- cb
.x_
) + ( ca
.y_
- cb
.y_
) * ( ca
.y_
- cb
.y_
) )
4467 /* Now to the search for a separating line.
4469 if( ! convexHullOneWayOverlap( pointSet_a
, pointSet_b
) )
4473 if( ! convexHullOneWayOverlap( pointSet_b
, pointSet_a
) )
4481 Computation::SegmentSectionPair2D::convexHullOneWayOverlap( const std::vector
< const Concrete::Coords2D
* > & pointSet1
, const std::vector
< const Concrete::Coords2D
* > & pointSet2
)
4483 Concrete::LengthSquared coincide_tol
= 0.0001 * Computation::the_arcdelta
* Computation::the_arcdelta
;
4484 for( std::vector
< const Concrete::Coords2D
* >::const_iterator i0
= pointSet1
.begin( ); i0
!= pointSet1
.end( ); ++i0
)
4486 std::vector
< const Concrete::Coords2D
* >::const_iterator i1
= i0
;
4488 for( ; i1
!= pointSet1
.end( ); ++i1
)
4490 const Concrete::Coords2D
& p0( **i0
);
4491 const Concrete::Coords2D
& p1( **i1
);
4493 /* The two points bound the convex hull iff the other points are on the same side of the segment.
4494 * If they are on the same side and p is on the other side, then the distance to p is of interest.
4496 const Concrete::Length tx
= p1
.x_
- p0
.x_
;
4497 const Concrete::Length ty
= p1
.y_
- p0
.y_
;
4498 if( tx
* tx
+ ty
* ty
< coincide_tol
)
4502 bool counterClockwise
; // true when ( p0, p1 ) are ordered counter-clockwise around the interior.
4504 std::vector
< const Concrete::Coords2D
* >::const_iterator i2
= pointSet1
.begin( );
4505 for( ; ; ++i2
) // we don't have to check against pointSet1.end( )
4507 if( i2
== i0
|| i2
== i1
)
4511 const Concrete::Length dx
= (*i2
)->x_
- p0
.x_
;
4512 const Concrete::Length dy
= (*i2
)->y_
- p0
.y_
;
4513 counterClockwise
= ( ty
* dx
- tx
* dy
< 0 );
4517 for( ; ; ++i2
) // we don't have to check against pointSet1.end( )
4519 if( i2
== i0
|| i2
== i1
)
4523 const Concrete::Length dx
= (*i2
)->x_
- p0
.x_
;
4524 const Concrete::Length dy
= (*i2
)->y_
- p0
.y_
;
4525 if( counterClockwise
== ( ty
* dx
- tx
* dy
< 0 ) )
4531 continue; // the points were on different sides.
4534 bool allOutside
= true;
4535 for( std::vector
< const Concrete::Coords2D
* >::const_iterator p
= pointSet2
.begin( ); p
!= pointSet2
.end( ); ++p
)
4537 const Concrete::Length dx
= (*p
)->x_
- p0
.x_
;
4538 const Concrete::Length dy
= (*p
)->y_
- p0
.y_
;
4539 if( ( ty
* dx
- tx
* dy
> 0 ) != counterClockwise
)
4553 /* Note that this is only one part of the test, only if both symmetric tests return true do the
4560 Computation::SegmentSectionPair2D::convexHullSeparatedBy( Concrete::LengthSquared sep
) const
4562 hull_dist_
= Concrete::LengthSquared( 0 );
4563 /* Even though there is a chance that the eight doubles that describe the generators are
4564 * stored densly, starting at & controls_a, the copying we do here takes no time compared
4565 * to the distance computation itself.
4567 const size_t N
= 4; /* Maximum number of generators per polytope. */
4568 const size_t DIM
= 2; /* Dimension of each generator. */
4569 double ga
[ N
* DIM
]; /* Allocate data on stack. */
4570 double gb
[ N
* DIM
];
4571 size_t na
; /* Number of generators used. */
4573 /* Initialize generators for each polytope. */
4574 if( baseSegs
->straightLineSeg_a( ) )
4577 controls_a
.getEndpoints( & ga
[0] );
4582 controls_a
.get( & ga
[0] );
4584 if( baseSegs
->straightLineSeg_b( ) )
4587 controls_b
.getEndpoints( & gb
[0] );
4592 controls_b
.get( & gb
[0] );
4595 double double_distSquaredLow
;
4596 double double_distSquaredHigh
;
4597 QPSolverStatus status
;
4598 Computation::polytope_distance_generator_form( DIM
,
4601 & double_distSquaredLow
, & double_distSquaredHigh
,
4602 Concrete::LengthSquared::offtype( sep
), -1,
4604 0.5 * Concrete::Length::offtype( Computation::theDistanceTol
), /* Lagrange multiplier tolerance. */
4606 0, 0, /* We don't care about where the optimum is */
4608 if( status
.code
!= Computation::QP_OK
)
4610 const char * binaryFilename
= "qp-fail-data.binary";
4611 Computation::polytope_distance_generator_form_write_binary_data( binaryFilename
,
4615 std::ostringstream msg
;
4616 msg
<< "QP solver status: " << status
.code_str( ) << ", with reason: " << status
.sub_str( )
4617 << ", binary data was written to \"" << binaryFilename
<< "\"." ;
4618 throw Exceptions::InternalError( strrefdup( msg
) );
4620 Concrete::LengthSquared
distSquaredLow( double_distSquaredLow
);
4621 Concrete::LengthSquared
distSquaredHigh( double_distSquaredHigh
);
4622 if( distSquaredLow
>= sep
)
4626 hull_dist_
= distSquaredHigh
;
4631 Computation::SegmentSectionPair2D::update_t_a1( Concrete::Time t
)
4633 t_a1
= min( t_a1
, t
);
4637 Computation::SegmentSectionPair2D::isEmpty( ) const
4639 return t_a1
< t_a0
|| t_b1
< t_b0
;
4643 Computation::SegmentSectionPair2D::writeTimes( std::ostream
& os
) const
4645 os
<< Concrete::Time::offtype( t_a0
) << "--" << Concrete::Time::offtype( t_a1
) << ", "
4646 << Concrete::Time::offtype( t_b0
) << "--" << Concrete::Time::offtype( t_b1
) << std::endl
;
4651 Computation::SegmentSectionPair2D::maxSpeed_a( ) const
4653 return baseSegs
->maxSpeed_a( );
4657 Computation::SegmentSectionPair2D::maxSpeed_b( ) const
4659 return baseSegs
->maxSpeed_b( );
4660 // Concrete::Length lengthBound =
4661 // hypotPhysical( controls_b.p1_.x_ - controls_b.p0_.x_, controls_b.p1_.y_ - controls_b.p0_.y_ ) +
4662 // hypotPhysical( controls_b.p2_.x_ - controls_b.p1_.x_, controls_b.p2_.y_ - controls_b.p1_.y_ ) +
4663 // hypotPhysical( controls_b.p3_.x_ - controls_b.p2_.x_, controls_b.p3_.y_ - controls_b.p2_.y_ );
4665 // return arclen_tol * ( t_b1 - t_b0 ) / *lengthBound;
4668 RefCountPtr
< const Lang::ElementaryPath2D
>
4669 Lang::ElementaryPath2D::subpath( const Concrete::SplineTime splt1
, const Concrete::SplineTime splt2
) const
4673 throw Exceptions::OutOfRange( "The empty path has no subpaths." );
4675 Lang::ElementaryPath2D
* res
= new Lang::ElementaryPath2D
;
4677 if( splt2
.t( ) < splt1
.t( ) )
4679 throw Exceptions::OutOfRange( "The path-times must come in ascending order." );
4681 if( splt2
.t( ) != HUGE_VAL
&& splt2
.t( ) > splt1
.t( ) + size( ) )
4683 throw Exceptions::OutOfRange( "It is forbidden (although not logically necessary) to cycle more than one revolution." );
4686 Concrete::Time gt1
= timeCheck( splt1
.t( ) ); /* "g" as in global. t1 refers to a time on an elementary segment */
4687 Concrete::Time gt2
= timeCheck( splt2
.t( ) );
4688 if( gt2
<= gt1
&& splt2
.t( ) > splt1
.t( ) )
4693 /* First, we treat the case where both cuts are on the same elementary segment.
4694 * The reason is twofold. First, it is more efficient, as it is almost only half
4695 * the amount of computation this way. Secondly, cutting sequentially on the same
4696 * segment requires a bit of extra manipulation of the time arguments.
4698 if( fmod( floor( gt1
.offtype
< 0, 1 >( ) ) + 1, duration( ) + 1 ) == ceil( gt2
.offtype
< 0, 1 >( ) ) )
4701 const Concrete::PathPoint2D
* p1
;
4702 const Concrete::PathPoint2D
* p2
;
4703 findSegment( gt1
, &t1
, &p1
, &p2
); /* note that the stored pointers must not be deleted */
4704 Concrete::Time t2
= gt2
- ceil( gt2
.offtype
< 0, 1 >( ) ) + 1;
4705 Concrete::Bezier x0
= p1
->mid_
->x_
.offtype
< 0, 3 >( );
4706 Concrete::Bezier y0
= p1
->mid_
->y_
.offtype
< 0, 3 >( );
4707 Concrete::Bezier x1
= p1
->front_
->x_
.offtype
< 0, 3 >( );
4708 Concrete::Bezier y1
= p1
->front_
->y_
.offtype
< 0, 3 >( );
4709 Concrete::Bezier x2
= p2
->rear_
->x_
.offtype
< 0, 3 >( );
4710 Concrete::Bezier y2
= p2
->rear_
->y_
.offtype
< 0, 3 >( );
4711 Concrete::Bezier x3
= p2
->mid_
->x_
.offtype
< 0, 3 >( );
4712 Concrete::Bezier y3
= p2
->mid_
->y_
.offtype
< 0, 3 >( );
4714 Concrete::Time tc
= t1
- 1; /* Note the sign here! */
4715 Concrete::Time td
= t1
- t2
;
4717 /* Compute new x polynomial using variable change t = t1 - td * tNew, where tNew is a time parameter in in the range [ 0, 1 ].
4719 Concrete::Length k0x
= - tc
* tc
* tc
* x0
+ t1
* ( 3 * tc
* tc
* x1
+ t1
* ( 3 * Concrete::UNIT_TIME
* x2
- 3 * t1
* x2
+ t1
* x3
) );
4720 Concrete::Length k1x
= 3 * td
* ( tc
* tc
* x0
+ ( -Concrete::UNIT_TIME
*Concrete::UNIT_TIME
+ 4 * Concrete::UNIT_TIME
* t1
- 3 * t1
* t1
) * x1
+ t1
* ( -2 * Concrete::UNIT_TIME
* x2
+ 3 * t1
* x2
- t1
* x3
) );
4721 Concrete::Length k2x
= -3 * td
* td
* ( tc
* x0
+ 2 * Concrete::UNIT_TIME
* x1
- 3 * t1
* x1
- Concrete::UNIT_TIME
* x2
+ 3 * t1
* x2
- t1
* x3
);
4722 Concrete::Length k3x
= td
* td
* td
* ( x0
- 3 * x1
+ 3 * x2
- x3
);
4723 /* Compute new coefficients in the standard polynomial base */
4724 Concrete::Length x0new
= k0x
;
4725 Concrete::Length x1new
= k0x
+ k1x
/ 3;
4726 Concrete::Length x2new
= k0x
+ ( 2 * k1x
+ k2x
) / 3;
4727 Concrete::Length x3new
= k0x
+ k1x
+ k2x
+ k3x
;
4729 /* Same thing for y */
4731 Concrete::Length k0y
= - tc
* tc
* tc
* y0
+ t1
* ( 3 * tc
* tc
* y1
+ t1
* ( 3 * Concrete::UNIT_TIME
* y2
- 3 * t1
* y2
+ t1
* y3
) );
4732 Concrete::Length k1y
= 3 * td
* ( tc
* tc
* y0
+ ( -Concrete::UNIT_TIME
*Concrete::UNIT_TIME
+ 4 * Concrete::UNIT_TIME
* t1
- 3 * t1
* t1
) * y1
+ t1
* ( -2 * Concrete::UNIT_TIME
* y2
+ 3 * t1
* y2
- t1
* y3
) );
4733 Concrete::Length k2y
= -3 * td
* td
* ( tc
* y0
+ 2 * Concrete::UNIT_TIME
* y1
- 3 * t1
* y1
- Concrete::UNIT_TIME
* y2
+ 3 * t1
* y2
- t1
* y3
);
4734 Concrete::Length k3y
= td
* td
* td
* ( y0
- 3 * y1
+ 3 * y2
- y3
);
4736 Concrete::Length y0new
= k0y
;
4737 Concrete::Length y1new
= k0y
+ k1y
/ 3;
4738 Concrete::Length y2new
= k0y
+ ( 2 * k1y
+ k2y
) / 3;
4739 Concrete::Length y3new
= k0y
+ k1y
+ k2y
+ k3y
;
4741 Concrete::PathPoint2D
* ptmp
= new Concrete::PathPoint2D( x0new
, y0new
);
4742 ptmp
->front_
= new Concrete::Coords2D( x1new
, y1new
);
4743 res
->push_back( ptmp
);
4744 ptmp
= new Concrete::PathPoint2D( x3new
, y3new
);
4745 ptmp
->rear_
= new Concrete::Coords2D( x2new
, y2new
);
4746 res
->push_back( ptmp
);
4748 return RefCountPtr
< const Lang::ElementaryPath2D
>( res
);
4752 /* Now, we treat the other case, where the cuts are on different segments. This results in a three phase
4754 * Since "cutting away after" requires much simpler formulas than straight-forward "cutting away before",
4755 * the latter is treated as the former, but with time reversed.
4758 Concrete::Time t
= Concrete::ZERO_TIME
;
4759 const_iterator i1
= begin( );
4760 for( ; t
+ 1 <= gt1
; t
+= Concrete::UNIT_TIME
, ++i1
)
4762 const_iterator i2
= i1
;
4769 Concrete::Time t1
= gt1
- t
;
4770 Concrete::PathPoint2D
* p1
;
4771 Concrete::PathPoint2D
* p2
;
4774 p1
= new Concrete::PathPoint2D( **i1
);
4775 p2
= new Concrete::PathPoint2D( **i2
);
4780 Concrete::Bezier x3
= (*i1
)->mid_
->x_
.offtype
< 0, 3 >( );
4781 Concrete::Bezier y3
= (*i1
)->mid_
->y_
.offtype
< 0, 3 >( );
4782 Concrete::Bezier x2
= (*i1
)->front_
->x_
.offtype
< 0, 3 >( );
4783 Concrete::Bezier y2
= (*i1
)->front_
->y_
.offtype
< 0, 3 >( );
4784 Concrete::Bezier x1
= (*i2
)->rear_
->x_
.offtype
< 0, 3 >( );
4785 Concrete::Bezier y1
= (*i2
)->rear_
->y_
.offtype
< 0, 3 >( );
4786 Concrete::Bezier x0
= (*i2
)->mid_
->x_
.offtype
< 0, 3 >( );
4787 Concrete::Bezier y0
= (*i2
)->mid_
->y_
.offtype
< 0, 3 >( );
4788 t1
= Concrete::UNIT_TIME
- t1
;
4790 /* Compute new x polynomial using variable change t = t1 * tNew, where tNew is a time parameter in in the range [ 0, 1 ].
4792 Concrete::Bezier k0x
= x0
;
4793 Concrete::Bezier k1x
= 3 * t1
* ( x1
- x0
).offtype
< 0, 1 >( );
4794 Concrete::Bezier k2x
= 3 * t1
* t1
* ( x0
- 2 * x1
+ x2
).offtype
< 0, 2 >( );
4795 Concrete::Bezier k3x
= t1
* t1
* t1
* ( -x0
+ 3 * x1
- 3 * x2
+ x3
).offtype
< 0, 3 >( );
4796 /* Compute new coefficients in the standard polynomial base */
4797 Concrete::Bezier x0new
= k0x
;
4798 Concrete::Bezier x1new
= k0x
+ k1x
/ 3;
4799 Concrete::Bezier x2new
= k0x
+ ( 2 * k1x
+ k2x
) / 3;
4800 Concrete::Bezier x3new
= k0x
+ k1x
+ k2x
+ k3x
;
4802 Concrete::Bezier k0y
= y0
;
4803 Concrete::Bezier k1y
= 3 * t1
* ( y1
- y0
).offtype
< 0, 1 >( );
4804 Concrete::Bezier k2y
= 3 * t1
* t1
* ( y0
- 2 * y1
+ y2
).offtype
< 0, 2 >( );
4805 Concrete::Bezier k3y
= t1
* t1
* t1
* ( -y0
+ 3 * y1
- 3 * y2
+ y3
).offtype
< 0, 3 >( );
4807 Concrete::Bezier y0new
= k0y
;
4808 Concrete::Bezier y1new
= k0y
+ k1y
/ 3;
4809 Concrete::Bezier y2new
= k0y
+ ( 2 * k1y
+ k2y
) / 3;
4810 Concrete::Bezier y3new
= k0y
+ k1y
+ k2y
+ k3y
;
4812 /* Now, reverse time again! */
4814 p1
= new Concrete::PathPoint2D( x3new
.offtype
< 0, -3 >( ), y3new
.offtype
< 0, -3 >( ) );
4815 p1
->front_
= new Concrete::Coords2D( x2new
.offtype
< 0, -3 >( ), y2new
.offtype
< 0, -3 >( ) );
4816 p2
= new Concrete::PathPoint2D( x0new
.offtype
< 0, -3 >( ), y0new
.offtype
< 0, -3 >( ) ); /* This point must be the same as before. */
4817 p2
->rear_
= new Concrete::Coords2D( x1new
.offtype
< 0, -3 >( ), y1new
.offtype
< 0, -3 >( ) );
4818 p2
->front_
= new Concrete::Coords2D( *(*i2
)->front_
);
4821 /* At this point we know that the rest of this segment shall be included, since the cuts are not on the
4825 for( ; t
+ 1 < gt2
; t
+= Concrete::UNIT_TIME
)
4827 res
->push_back( p1
);
4835 p2
= new Concrete::PathPoint2D( **i2
);
4838 /* Remember to delete p1 and p2 if unused! */
4840 Concrete::Time t2
= gt2
- t
;
4843 res
->push_back( p1
);
4844 res
->push_back( p2
);
4848 Concrete::Length xn1
= p1
->rear_
->x_
; /* "n" for negative index. We save these values now, so that we can delete p1 and p2 soon. */
4849 Concrete::Length yn1
= p1
->rear_
->y_
;
4851 Concrete::Bezier x0
= p1
->mid_
->x_
.offtype
< 0, 3 >( );
4852 Concrete::Bezier y0
= p1
->mid_
->y_
.offtype
< 0, 3 >( );
4853 Concrete::Bezier x1
= p1
->front_
->x_
.offtype
< 0, 3 >( );
4854 Concrete::Bezier y1
= p1
->front_
->y_
.offtype
< 0, 3 >( );
4855 Concrete::Bezier x2
= p2
->rear_
->x_
.offtype
< 0, 3 >( );
4856 Concrete::Bezier y2
= p2
->rear_
->y_
.offtype
< 0, 3 >( );
4857 Concrete::Bezier x3
= p2
->mid_
->x_
.offtype
< 0, 3 >( );
4858 Concrete::Bezier y3
= p2
->mid_
->y_
.offtype
< 0, 3 >( );
4863 /* Compute new x polynomial using variable change t = t2 * tNew, where tNew is a time parameter in in the range [ 0, 1 ].
4865 Concrete::Bezier k0x
= x0
;
4866 Concrete::Bezier k1x
= 3 * t2
* ( x1
- x0
).offtype
< 0, 1 >( );
4867 Concrete::Bezier k2x
= 3 * t2
* t2
* ( x0
- 2 * x1
+ x2
).offtype
< 0, 2 >( );
4868 Concrete::Bezier k3x
= t2
* t2
* t2
* ( -x0
+ 3 * x1
- 3 * x2
+ x3
).offtype
< 0, 3 >( );
4869 /* Compute new coefficients in the standard polynomial base */
4870 Concrete::Bezier x0new
= k0x
;
4871 Concrete::Bezier x1new
= k0x
+ k1x
/ 3;
4872 Concrete::Bezier x2new
= k0x
+ ( 2 * k1x
+ k2x
) / 3;
4873 Concrete::Bezier x3new
= k0x
+ k1x
+ k2x
+ k3x
;
4875 Concrete::Bezier k0y
= y0
;
4876 Concrete::Bezier k1y
= 3 * t2
* ( y1
- y0
).offtype
< 0, 1 >( );
4877 Concrete::Bezier k2y
= 3 * t2
* t2
* ( y0
- 2 * y1
+ y2
).offtype
< 0, 2 >( );
4878 Concrete::Bezier k3y
= t2
* t2
* t2
* ( -y0
+ 3 * y1
- 3 * y2
+ y3
).offtype
< 0, 3 >( );
4880 Concrete::Bezier y0new
= k0y
;
4881 Concrete::Bezier y1new
= k0y
+ k1y
/ 3;
4882 Concrete::Bezier y2new
= k0y
+ ( 2 * k1y
+ k2y
) / 3;
4883 Concrete::Bezier y3new
= k0y
+ k1y
+ k2y
+ k3y
;
4885 p1
= new Concrete::PathPoint2D( x0new
.offtype
< 0, -3 >( ), y0new
.offtype
< 0, -3 >( ) );
4886 p1
->front_
= new Concrete::Coords2D( x1new
.offtype
< 0, -3 >( ), y1new
.offtype
< 0, -3 >( ) );
4887 p1
->rear_
= new Concrete::Coords2D( xn1
, yn1
);
4888 p2
= new Concrete::PathPoint2D( x3new
.offtype
< 0, -3 >( ), y3new
.offtype
< 0, -3 >( ) );
4889 p2
->rear_
= new Concrete::Coords2D( x2new
.offtype
< 0, -3 >( ), y2new
.offtype
< 0, -3 >( ) );
4891 res
->push_back( p1
);
4892 res
->push_back( p2
);
4896 return RefCountPtr
< const Lang::ElementaryPath2D
>( res
);
4899 RefCountPtr
< const Lang::ElementaryPath2D
>
4900 Lang::ElementaryPath2D::reverse( ) const
4902 /* Note that reversing a closed path is not quite as straight-forward as one might first think!
4905 Lang::ElementaryPath2D
* res
= new Lang::ElementaryPath2D
;
4911 const_iterator i
= begin( );
4914 return RefCountPtr
< const Lang::ElementaryPath2D
>( res
);
4919 // The first path point remains first!
4922 for( ; i
!= end( ); ++i
)
4924 Concrete::PathPoint2D
* newPoint
= new Concrete::PathPoint2D( new Concrete::Coords2D( *((*i
)->mid_
) ) );
4925 if( (*i
)->rear_
!= (*i
)->mid_
)
4927 newPoint
->front_
= new Concrete::Coords2D( *((*i
)->rear_
) );
4929 if( (*i
)->front_
!= (*i
)->mid_
)
4931 newPoint
->rear_
= new Concrete::Coords2D( *((*i
)->front_
) );
4933 res
->push_front( newPoint
);
4938 Concrete::PathPoint2D
* newPoint
= new Concrete::PathPoint2D( new Concrete::Coords2D( *((*i
)->mid_
) ) );
4939 if( (*i
)->rear_
!= (*i
)->mid_
)
4941 newPoint
->front_
= new Concrete::Coords2D( *((*i
)->rear_
) );
4943 if( (*i
)->front_
!= (*i
)->mid_
)
4945 newPoint
->rear_
= new Concrete::Coords2D( *((*i
)->front_
) );
4947 res
->push_front( newPoint
);
4950 return RefCountPtr
< const Lang::ElementaryPath2D
>( res
);
4954 Lang::ElementaryPath2D::show( std::ostream
& os
) const
4956 os
<< "Elementary subpath with " << size( ) << " path points" ;
4960 Lang::ElementaryPath2D::gcMark( Kernel::GCMarkedSet
& marked
)
4963 RefCountPtr
< const Lang::Path3D
>
4964 Lang::ElementaryPath2D::typed_to3D( const RefCountPtr
< const Lang::Path2D
> & self
) const
4966 typedef const Lang::ElementaryPath2D ArgType
;
4967 RefCountPtr
< ArgType
> selfTyped
= self
.down_cast
< ArgType
>( );
4968 if( selfTyped
== NullPtr
< ArgType
>( ) )
4970 throw Exceptions::InternalError( "ElementaryPath2D::typed_to3D: self was of unexpected type." );
4973 return RefCountPtr
< const Lang::Path3D
>( new Lang::Path2Din3D( selfTyped
) );
4977 RefCountPtr
< const Lang::Geometric3D
>
4978 Lang::ElementaryPath2D::to3D( const RefCountPtr
< const Lang::Geometric2D
> & self
) const
4980 typedef const Lang::ElementaryPath2D ArgType
;
4981 RefCountPtr
< ArgType
> selfTyped
= self
.down_cast
< ArgType
>( );
4982 if( selfTyped
== NullPtr
< ArgType
>( ) )
4984 throw Exceptions::InternalError( "ElementaryPath2D::to3D: self was of unexpected type." );
4987 return RefCountPtr
< const Lang::Geometric3D
>( new Lang::Path2Din3D( selfTyped
) );