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 Henrik Tidefelt
21 #include "shapestypes.h"
22 #include "shapesexceptions.h"
25 #include "angleselect.h"
28 #include "statetypes.h"
29 #include "lighttypes.h"
30 #include "shadingtypes.h"
32 #include "trianglefunctions.h"
33 #include "constructorrepresentation.h"
39 using namespace Shapes
;
42 #define SPLICEDEBUG( code ) // code
44 Computation::ZBufLine::ZMap::ZMap( Concrete::Coords3D p0
, const Concrete::UnitFloatTriple
& d
,
45 Concrete::Length eyez
)
47 k_x_( d
.x_
), k_y_( d
.y_
), k_z_( d
.z_
),
51 // It is assumed that this funciton is only called with points that actually are on the line.
53 Computation::ZBufLine::ZMap::operator () ( const Concrete::Coords2D
& p
) const
55 // The two (overdetermined) equations are written
58 // where the three dimensional coordinates along the line are given by
61 const Concrete::Length ax
= eyez_
* k_x_
+ p
.x_
* k_z_
;
62 const Concrete::Length ay
= eyez_
* k_y_
+ p
.y_
* k_z_
;
63 const Physical
< 2, 0 > bx
= p
.x_
* ( eyez_
- p0_
.z_
) - eyez_
* p0_
.x_
;
64 const Physical
< 2, 0 > by
= p
.y_
* ( eyez_
- p0_
.z_
) - eyez_
* p0_
.y_
;
65 const Concrete::Length alpha
= ( ax
* bx
+ ay
* by
) / ( ax
* ax
+ ay
* ay
);
66 return p0_
.z_
+ alpha
* k_z_
;
70 Computation::ZBufLine::ZMap::intersection( const Computation::ZBufTriangle::ZMap
& plane
) const
72 // The triangle plane is given by a normal equation:
74 // Inserting our point gives
75 // < n, p0_ + k ( p1_ - p0_ ) > == b
76 // k < n, p1_ - p0_ > == b - < n, p0_ >
78 double a
= Concrete::inner( plane
.getNormal( ), d_
);
79 Concrete::Length b
= plane
.getM( ) - Concrete::inner( plane
.getNormal( ), p0_
);
83 throw "No intersection";
86 return p0_
+ ( b
/ a
) * d_
;
90 Computation::ZBufLine::ZBufLine( const Computation::StrokedLine3D
* painter
, const RefCountPtr
< const Computation::ZBufLine::ZMap
> & zMap
, const RefCountPtr
< const Bezier::PolyCoeffs
< Concrete::Coords2D
> > & bezierView
, const Concrete::Coords2D
& p0
, const Concrete::Coords2D
& p1
)
91 : painter_( painter
), bezierView_( bezierView
), p0_( p0
), p1_( p1
), d_( p1
- p0
), zMap_( zMap
)
95 Computation::ZBufLine::zAt( const Concrete::Coords2D
& p
) const
101 Computation::ZBufLine::intersectionTime( Concrete::Coords2D p0
, Concrete::Coords2D p1
) const
103 Concrete::Coords2D d
= p1
- p0
;
105 // The equation to solve is
106 // p0_ + t d_ == p0 + s d
108 // ( d_ -d ) ( t s ) == p0 - p0_
110 Concrete::Coords2D rhs
= p0
- p0_
;
112 // Borrowing an idea from ZBufTriangle, we don't test for singularity using the determinant since
113 // I have no good geometric interpretation of tolerance tests on its value.
115 double n_n1
= Concrete::innerScalar( d_
, d
);
116 double n_n_
= Concrete::innerScalar( d_
, d_
);
117 double n2n2
= Concrete::innerScalar( d
, d
);
119 if( n_n1
* n_n1
> ( 1 - 1e-8 ) * ( n_n_
* n2n2
) ) // This corresponds to an angle of approximately 0.01 degree.
121 return -1; // Any negative value means no intersection.
125 Physical
< -2, 0 > invDet
= 1. / ( d_
.x_
* d
.y_
- d_
.y_
* d
.x_
);
126 double t
= invDet
* ( d
.y_
* rhs
.x_
- d
.x_
* rhs
.y_
);
127 double s
= - invDet
* ( - d_
.y_
* rhs
.x_
+ d_
.x_
* rhs
.y_
);
129 if( 0 < s
&& s
< 1 &&
134 return -1; // Any negative value means no intersection.
138 Computation::ZBufLine::overlaps( const ZBufTriangle
& other
) const
140 // Compare Computation::ZBufLine::overlaps( const ZBufLine & other ) const.
141 // This test is also performed by testing a set of candidate separating planes.
142 // Here this set consists of this line itself together with the three sides of the triangle.
144 // We rather call it an overlap than missing small overlaps, because undetected overlaps may make short
145 // segments of a line shine through a surface at spurious points.
147 // Beginning with this line:
149 Concrete::UnitFloatPair normal
= p0_
.normalizedOrthogonalNoFail( p1_
);
151 Concrete::Length l0
= Concrete::inner( other
.points_
[ 0 ], normal
);
152 Concrete::Length m
= Concrete::inner( p0_
, normal
);
153 if( l0
< m
- Computation::theTrixelizeOverlapTol
)
155 if( Concrete::inner( other
.points_
[ 1 ], normal
) < m
- Computation::theTrixelizeOverlapTol
&&
156 Concrete::inner( other
.points_
[ 2 ], normal
) < m
- Computation::theTrixelizeOverlapTol
)
162 if( l0
> m
+ Computation::theTrixelizeOverlapTol
)
164 if( Concrete::inner( other
.points_
[ 1 ], normal
) > m
+ Computation::theTrixelizeOverlapTol
&&
165 Concrete::inner( other
.points_
[ 2 ], normal
) > m
+ Computation::theTrixelizeOverlapTol
)
172 // We now test the three sides of the triangle, remembering that only one side is the outside of a triangle side.
173 // ***Important***: Compare ZBufTriangle::oneWayOverlap.
174 // Note, though, that ZBufTriangle::oneWayOverlap prefers to answer false when in doubt.
176 const std::vector
< Concrete::Coords2D
> & poly1
= other
.points_
;
177 for( std::vector
< Concrete::Coords2D
>::const_iterator i0
= poly1
.begin( ); i0
!= poly1
.end( ); ++i0
)
179 std::vector
< Concrete::Coords2D
>::const_iterator i1
= i0
;
181 if( i1
== poly1
.end( ) )
185 std::vector
< Concrete::Coords2D
>::const_iterator i2
= i1
;
187 if( i2
== poly1
.end( ) )
192 const Concrete::Coords2D
& p0( *i0
);
193 const Concrete::Coords2D
& p1( *i1
);
195 const Concrete::Length txUnnormed
= p1
.x_
- p0
.x_
;
196 const Concrete::Length tyUnnormed
= p1
.y_
- p0
.y_
;
197 const Physical
< -1, 0 > invNorm
= 1. / hypotPhysical( txUnnormed
, tyUnnormed
);
198 // First we guess what's the inward direction
199 double inx( - invNorm
* tyUnnormed
);
200 double iny( invNorm
* txUnnormed
);
201 // Then we check if it needs to be reversed
203 const Concrete::Length dx
= i2
->x_
- p0
.x_
;
204 const Concrete::Length dy
= i2
->y_
- p0
.y_
;
205 if( dx
* inx
+ dy
* iny
< Concrete::ZERO_LENGTH
)
212 bool allOutside
= true;
214 const Concrete::Coords2D
& p
= p0_
;
215 const Concrete::Length dx
= p
.x_
- p0
.x_
;
216 const Concrete::Length dy
= p
.y_
- p0
.y_
;
217 if( inx
* dx
+ iny
* dy
> Computation::theTrixelizeOverlapTol
)
222 if( allOutside
) // read "still allOutside"
224 const Concrete::Coords2D
& p
= p1_
;
225 const Concrete::Length dx
= p
.x_
- p0
.x_
;
226 const Concrete::Length dy
= p
.y_
- p0
.y_
;
227 if( inx
* dx
+ iny
* dy
> Computation::theTrixelizeOverlapTol
)
244 Computation::ZBufLine::overlaps( const ZBufLine
& other
) const
246 // Since line segments are convex polyhedra, we can determine overlap by enumerating a set of separating
247 // planes which has to include one separating plane if such a plane exists. Here the only planes to consider
248 // are the lines themselves.
250 // Currently, the tolerance in this test is used to ensure that a true result really indicates an intersection.
252 Concrete::UnitFloatPair normal
= p0_
.normalizedOrthogonalNoFail( p1_
);
253 Concrete::Length m
= Concrete::inner( normal
, p0_
);
254 const ZBufLine
& tmpLine
= other
;
256 Concrete::Length l0
= Concrete::inner( tmpLine
.p0_
, normal
);
257 Concrete::Length l1
= Concrete::inner( tmpLine
.p1_
, normal
);
258 if( ( l0
< m
+ Computation::theTrixelizeOverlapTol
&&
259 l1
< m
+ Computation::theTrixelizeOverlapTol
) ||
260 ( l0
> m
- Computation::theTrixelizeOverlapTol
&&
261 l1
> m
- Computation::theTrixelizeOverlapTol
) )
267 Concrete::UnitFloatPair normal
= other
.p0_
.normalizedOrthogonalNoFail( other
.p1_
);
268 Concrete::Length m
= Concrete::inner( normal
, other
.p0_
);
269 const ZBufLine
& tmpLine
= *this;
271 Concrete::Length l0
= Concrete::inner( tmpLine
.p0_
, normal
);
272 Concrete::Length l1
= Concrete::inner( tmpLine
.p1_
, normal
);
273 if( ( l0
< m
+ Computation::theTrixelizeOverlapTol
&&
274 l1
< m
+ Computation::theTrixelizeOverlapTol
) ||
275 ( l0
> m
- Computation::theTrixelizeOverlapTol
&&
276 l1
> m
- Computation::theTrixelizeOverlapTol
) )
286 Computation::ZBufLine::splice( const ZBufLine
* line1
, const ZBufLine
* line2
, std::list
< const Computation::ZBufLine
* > * line1Container
, std::list
< const Computation::ZBufLine
* > * line2Container
)
288 // When this function is called, we know that the line segments intersect.
290 // First we compute where they intersect and determine which line is in front.
292 Concrete::Coords2D p01
= line1
->p0_
;
293 Concrete::Coords2D p02
= line2
->p0_
;
295 Concrete::Coords2D d1
= line1
->d_
;
296 Concrete::Coords2D d2
= line2
->d_
;
298 // What follows is similar to ZBufLine::intersectionTime.
300 Concrete::Coords2D rhs
= p02
- p01
;
302 // This time, perhaps more important than ever, the angle is computed to determine if the intersection is well-conditioned.
303 // Shall the angle be used to determine how much to cut away?
305 double n_n1
= Concrete::innerScalar( d1
, d2
);
306 double n_n_
= Concrete::innerScalar( d1
, d1
);
307 double n2n2
= Concrete::innerScalar( d2
, d2
);
309 if( n_n1
* n_n1
> ( 1 - 1e-8 ) * ( n_n_
* n2n2
) ) // This corresponds to an angle of approximately 0.01 degree.
311 // If the lines are very parallel I don't know what to do, so I just discard one of the lines!...
312 std::cerr
<< "Warning: discarding one of the parallel lines!" << std::endl
;
313 // line1Container->push_back( line1 );
314 line2Container
->push_back( line2
);
320 Physical
< -2, 0 > invDet
= 1. / ( d1
.x_
* d2
.y_
- d1
.y_
* d2
.x_
);
321 double t1
= invDet
* ( d2
.y_
* rhs
.x_
- d2
.x_
* rhs
.y_
);
322 // double t2 = - invDet * ( - d1.y_ * rhs.x_ + d1.x_ * rhs.y_ );
324 // If the lines seems not to overlap, let's assume that they at least almost overlap, and
325 // that the bottom one is to be cut anyway.
326 // Note that it will lead to infinite loops if we were to return the segments as they came in.
327 // if( t1 < 0 || 1 < t1 ||
328 // t2 < 0 || 1 < t2 )
330 // // This disagrees with the presumption that the lines do intersect, but never mind...
332 // line1Container->push_back( line1 );
333 // line2Container->push_back( line2 );
338 Concrete::Coords2D pInt
= p01
+ t1
* d1
;
341 const Computation::ZBufLine
* topLine
= line1
;
342 const Computation::ZBufLine
* botLine
= line2
;
343 std::list
< const Computation::ZBufLine
* > * topContainer
= line1Container
;
344 std::list
< const Computation::ZBufLine
* > * botContainer
= line2Container
;
346 if( line2
->zAt( pInt
) > line1
->zAt( pInt
) )
348 // ... and correct if needed.
351 topContainer
= line2Container
;
352 botContainer
= line1Container
;
355 // The line on top is kept as is.
356 topContainer
->push_back( topLine
);
358 Concrete::Length cutLength
=
359 0.5 * topLine
->painter_
->getMetaState( )->width_
+
360 0.5 * botLine
->painter_
->getMetaState( )->width_
;
362 Concrete::UnitFloatPair d
= botLine
->d_
.direction( );
363 Concrete::Length l0
= ( botLine
->p0_
- pInt
).norm( ) - cutLength
;
364 if( l0
> Computation::theTrixelizeOverlapTol
)
366 botContainer
->push_back( new Computation::ZBufLine( botLine
->painter_
, botLine
->zMap_
, botLine
->bezierView_
, botLine
->p0_
, pInt
- cutLength
* d
) );
369 Concrete::Length l1
= ( botLine
->p1_
- pInt
).norm( ) - cutLength
;
370 if( l1
> Computation::theTrixelizeOverlapTol
)
372 botContainer
->push_back( new Computation::ZBufLine( botLine
->painter_
, botLine
->zMap_
, botLine
->bezierView_
, pInt
+ cutLength
* d
, botLine
->p1_
) );
375 // Now we're finished with botLine.
380 Computation::ZBufLine::splice( const ZBufTriangle
& triangle
, std::list
< const Computation::ZBufLine
* > * myLines
) const
382 // std::cerr << "Triangle color: " ;
383 // dynamic_cast< const Computation::FilledPolygon3D * >( triangle.painter_ )->getColor( )->show( std::cerr );
384 // std::cerr << std::endl ;
386 // Generally, a line may cross a triangle edge at two points, and may penetrate the triangle at one point.
387 // If these points are sorted, we'll end up with at most four segments, and visibility can be tested for each.
388 // Finally, adjacent visible segments are joined and pushed in the destination container.
390 // The points on this segment are parameterized as
391 // p0_ + t ( p1_ - p0_ )
392 // where t obviously is in the range [ 0, 1 ].
394 double tIn
= HUGE_VAL
; // "time" when line enters triangle
395 double tOut
= -HUGE_VAL
; // "time" when line exits triangle
396 std::vector
< double > times
; // All interesting times.
398 times
.push_back( 0. );
399 times
.push_back( 1. );
401 for( std::vector
< Concrete::Coords2D
>::const_iterator i0
= triangle
.points_
.begin( ); i0
!= triangle
.points_
.end( ); ++i0
)
403 std::vector
< Concrete::Coords2D
>::const_iterator i1
= i0
;
405 if( i1
== triangle
.points_
.end( ) )
407 i1
= triangle
.points_
.begin( );
410 double tmp
= intersectionTime( *i0
, *i1
); // the return value is non-positive if there is no intersection
413 times
.push_back( tmp
);
414 tIn
= std::min( tIn
, tmp
);
415 tOut
= std::max( tOut
, tmp
);
421 /* Only one of the times can be the enter/exit time.
422 * Note that the test below really needs to be this robust.
424 if( tIn
> 0.5 ? triangle
.contains( p0_
+ 0.5 * tIn
* d_
)
425 : ! triangle
.contains( p0_
+ 0.5 * ( tIn
+ 1 ) * d_
) )
437 // This means that the line is enclosed in the triangle.
443 // We now compute the intersection of the triangle and the line.
444 // It is no harm to add this point whether or not it lies inside the triangle,
445 // since the processing below will join adjacent segments that are visible.
449 Concrete::Coords2D pInt
= zMap_
->intersection( *(triangle
.zMap_
) ).make2DAutomatic( zMap_
->eyez( ) );
451 double tmp
= Concrete::inner( d_
, pInt
- p0_
) / Concrete::inner( d_
, d_
);
452 if( 0 < tmp
&& tmp
< 1 )
454 times
.push_back( tmp
);
457 catch( const char * noIntersectionBall
)
463 std::sort( times
.begin( ), times
.end( ) );
466 // We now have the times in increasing order, and it is time to identify visible segments.
468 Concrete::Length halfWidth
= painter_
->getMetaState( )->width_
;
470 bool visible
= false;
471 Concrete::Coords2D
pStart( 0, 0 );
472 typedef typeof times TimesType
;
473 TimesType::const_iterator i
= times
.begin( );
474 TimesType::const_iterator iLast
= i
;
476 for( ; i
!= times
.end( ); iLast
= i
, ++i
)
478 double tMid
= 0.5 * ( *iLast
+ *i
);
479 bool newVisible
= true;
480 if( tIn
< tMid
&& tMid
< tOut
)
482 Concrete::Coords2D pMid
= p0_
+ tMid
* d_
;
483 newVisible
= zAt( pMid
) + halfWidth
> triangle
.zAt( pMid
); // the halfWidth is like a tiebreaker for a line.
489 pStart
= p0_
+ *iLast
* d_
;
496 Concrete::Coords2D pEnd
= p0_
+ *iLast
* d_
;
497 if( ( pEnd
- pStart
).norm( ) > Computation::theTrixelizeOverlapTol
)
499 myLines
->push_back( new Computation::ZBufLine( painter_
, zMap_
, bezierView_
, pStart
, pEnd
) );
503 visible
= newVisible
;
508 Concrete::Coords2D pEnd
= p0_
+ *iLast
* d_
;
509 if( ( pEnd
- pStart
).norm( ) > Computation::theTrixelizeOverlapTol
)
511 myLines
->push_back( new Computation::ZBufLine( painter_
, zMap_
, bezierView_
, pStart
, pEnd
) );
517 RefCountPtr
< const Lang::PaintedPath2D
>
518 Computation::ZBufLine::stroke2D( ) const
520 RefCountPtr
< Lang::ElementaryPath2D
> path
= RefCountPtr
< Lang::ElementaryPath2D
>( new Lang::ElementaryPath2D( ) );
522 if( bezierView_
!= NullPtr
< typeof *bezierView_
>( ) )
527 bezierView_
->hyperplaneIntersections( t
, d_
, Concrete::innerScalar( d_
, p0_
) );
528 if( t
[0] == HUGE_VAL
)
530 throw Exceptions::InternalError( "ZBufLine::stroke2D: Failed to locate intersection for t0." );
532 if( t
[1] != HUGE_VAL
)
534 throw Exceptions::MiscellaneousRequirement( "ZBufLine::stroke2D: Problem due to non-monotonicity of the spline." );
541 bezierView_
->hyperplaneIntersections( t
, d_
, Concrete::innerScalar( d_
, p1_
) );
542 if( t
[0] == HUGE_VAL
)
544 throw Exceptions::InternalError( "ZBufLine::stroke2D: Failed to locate intersection for t1." );
546 if( t
[1] != HUGE_VAL
)
548 throw Exceptions::MiscellaneousRequirement( "ZBufLine::stroke2D: Problem due to non-monotonicity of the spline." );
552 Bezier::ControlPoints
< Concrete::Coords2D
> controls( bezierView_
->subSection( t0
, t1
) );
553 path
->push_back( new Concrete::PathPoint2D( p0_
.x_
, p0_
.y_
) );
554 path
->back( )->front_
= new Concrete::Coords2D( controls
.p1_
);
555 path
->push_back( new Concrete::PathPoint2D( p1_
.x_
, p1_
.y_
) );
556 path
->back( )->rear_
= new Concrete::Coords2D( controls
.p2_
);
560 path
->push_back( new Concrete::PathPoint2D( p0_
.x_
, p0_
.y_
) );
561 path
->push_back( new Concrete::PathPoint2D( p1_
.x_
, p1_
.y_
) );
564 // If we don't cast path to const, the contructor call becomes ambiguous.
565 return RefCountPtr
< const Lang::PaintedPath2D
>( new Lang::PaintedPath2D( painter_
->getMetaState( ),
566 static_cast< RefCountPtr
< const Lang::ElementaryPath2D
> >( path
),