Updating the changelog in the VERSION file, and version_sync.
[shapes.git] / source / zbufline.cc
blob93d06abb9ff33b5aeaccf106c4dafd88b8ee1b0f
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
6 * any later version.
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
19 #include <cmath>
21 #include "shapestypes.h"
22 #include "shapesexceptions.h"
23 #include "astexpr.h"
24 #include "consts.h"
25 #include "angleselect.h"
26 #include "astvar.h"
27 #include "astclass.h"
28 #include "statetypes.h"
29 #include "lighttypes.h"
30 #include "shadingtypes.h"
31 #include "globals.h"
32 #include "trianglefunctions.h"
33 #include "constructorrepresentation.h"
35 #include <ctype.h>
36 #include <list>
37 #include <algorithm>
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 )
46 : p0_( p0 ), d_( d ),
47 k_x_( d.x_ ), k_y_( d.y_ ), k_z_( d.z_ ),
48 eyez_( eyez )
49 { }
51 // It is assumed that this funciton is only called with points that actually are on the line.
52 Concrete::Length
53 Computation::ZBufLine::ZMap::operator () ( const Concrete::Coords2D & p ) const
55 // The two (overdetermined) equations are written
56 // ax alpha = bx
57 // ay alpha = by
58 // where the three dimensional coordinates along the line are given by
59 // p0_ + alpha d_
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_;
69 Concrete::Coords3D
70 Computation::ZBufLine::ZMap::intersection( const Computation::ZBufTriangle::ZMap & plane ) const
72 // The triangle plane is given by a normal equation:
73 // < n, x > == b
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_ );
81 if( a == 0 )
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 )
92 { }
94 Concrete::Length
95 Computation::ZBufLine::zAt( const Concrete::Coords2D & p ) const
97 return (*zMap_)( p );
100 double
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
107 // That is,
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 &&
130 0 < t && t < 1 )
132 return t;
134 return -1; // Any negative value means no intersection.
137 bool
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 )
158 return false;
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 )
167 return false;
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;
180 ++i1;
181 if( i1 == poly1.end( ) )
183 i1 = poly1.begin( );
185 std::vector< Concrete::Coords2D >::const_iterator i2 = i1;
186 ++i2;
187 if( i2 == poly1.end( ) )
189 i2 = poly1.begin( );
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 )
207 inx = -inx;
208 iny = -iny;
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 )
219 allOutside = false;
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 )
229 allOutside = false;
232 if( allOutside )
234 return false;
240 return true;
243 bool
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 ) )
263 return false;
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 ) )
278 return false;
282 return true;
285 void
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 );
316 return;
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 )
329 // {
330 // // This disagrees with the presumption that the lines do intersect, but never mind...
332 // line1Container->push_back( line1 );
333 // line2Container->push_back( line2 );
335 // return;
336 // }
338 Concrete::Coords2D pInt = p01 + t1 * d1;
340 // First we guess...
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;
345 // then we test...
346 if( line2->zAt( pInt ) > line1->zAt( pInt ) )
348 // ... and correct if needed.
349 topLine = line2;
350 botLine = line1;
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.
376 delete botLine;
379 void
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.
397 times.reserve( 6 );
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;
404 ++i1;
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
411 if( tmp > 0 )
413 times.push_back( tmp );
414 tIn = std::min( tIn, tmp );
415 tOut = std::max( tOut, tmp );
419 if( tIn == tOut )
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_ ) )
427 tIn = -HUGE_VAL;
429 else
431 tOut = HUGE_VAL;
435 if( tIn > tOut )
437 // This means that the line is enclosed in the triangle.
438 tIn = -HUGE_VAL;
439 tOut = HUGE_VAL;
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 )
459 // Never mind.
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;
475 ++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.
485 if( newVisible )
487 if( ! visible )
489 pStart = p0_ + *iLast * d_;
492 else
494 if( visible )
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;
506 if( visible )
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_ >( ) )
524 double t0 = 0;
526 double t[4];
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." );
536 t0 = t[0];
538 double t1 = 1;
540 double t[4];
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." );
550 t1 = t[0];
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_ );
558 else
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 ),
567 "S" ) );