Spinning Topp Logo BlackTopp Studios
inc
shape.cpp
1 // © Copyright 2010 - 2016 BlackTopp Studios Inc.
2 /* This file is part of The Mezzanine Engine.
3 
4  The Mezzanine Engine is free software: you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation, either version 3 of the License, or
7  (at your option) any later version.
8 
9  The Mezzanine Engine is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with The Mezzanine Engine. If not, see <http://www.gnu.org/licenses/>.
16 */
17 /* The original authors have included a copy of the license specified above in the
18  'Docs' folder. See 'gpl.txt'
19 */
20 /* We welcome the use of the Mezzanine engine to anyone, including companies who wish to
21  Build professional software and charge for their product.
22 
23  However there are some practical restrictions, so if your project involves
24  any of the following you should contact us and we will try to work something
25  out:
26  - DRM or Copy Protection of any kind(except Copyrights)
27  - Software Patents You Do Not Wish to Freely License
28  - Any Kind of Linking to Non-GPL licensed Works
29  - Are Currently In Violation of Another Copyright Holder's GPL License
30  - If You want to change our code and not add a few hundred MB of stuff to
31  your distribution
32 
33  These and other limitations could cause serious legal problems if you ignore
34  them, so it is best to simply contact us or the Free Software Foundation, if
35  you have any questions.
36 
37  Joseph Toppi - toppij@gmail.com
38  John Blackwood - makoenergy02@gmail.com
39 */
40 /*
41  -----------------------------------------------------------------------------
42  This source file is part of ogre-procedural
43 
44  For the latest info, see http://code.google.com/p/ogre-procedural/
45 
46  Copyright (c) 2010-2013 Michael Broutin
47 
48  Permission is hereby granted, free of charge, to any person obtaining a copy
49  of this software and associated documentation files (the "Software"), to deal
50  in the Software without restriction, including without limitation the rights
51  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
52  copies of the Software, and to permit persons to whom the Software is
53  furnished to do so, subject to the following conditions:
54 
55  The above copyright notice and this permission notice shall be included in
56  all copies or substantial portions of the Software.
57 
58  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
59  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
60  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
61  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
62  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
63  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
64  THE SOFTWARE.
65  -----------------------------------------------------------------------------
66  */
67 #ifndef _graphicsproceduralshape_cpp
68 #define _graphicsproceduralshape_cpp
69 
70 #include "Graphics/Procedural/shape.h"
71 #include "Graphics/Procedural/multishape.h"
72 #include "Graphics/Procedural/path.h"
73 
74 #include "Graphics/meshmanager.h"
75 
76 #include "MathTools/mathtools.h"
77 #include "exception.h"
78 
79 #include "Ogre.h"
80 
81 namespace
82 {
83  /// @brief Sorter method used to sort directions generated during a boolean operation.
84  /// @param One The first pair to compare.
85  /// @param Two The second pair to compare.
86  /// @return Returns true if the first pair should be ordered before the second pair, false otherwise.
87  Mezzanine::Boole _SortAngles(std::pair<Mezzanine::Real,Mezzanine::UInt8> One, std::pair<Mezzanine::Real,Mezzanine::UInt8> Two)
88  {
89  return ( One.first < Two.first );
90  }
91 }
92 
93 namespace Mezzanine
94 {
95  namespace Graphics
96  {
97  namespace Procedural
98  {
99  ///////////////////////////////////////////////////////////////////////////////
100  /// @brief Convenience class storing data on the point in a 2D shape where multiple segments intersect.
101  /// @details
102  ///////////////////////////////////////
104  {
105  /// @brief An Array storing indexes to points that are the first vertices of segments that intersect for each of the two shapes being processed.
107  /// @brief An Array storing whether the intersection is on or very near a vertex belonging to either shape being processed.
109  /// @brief The position of the intersection.
111 
112  /// @brief Class constructor.
113  /// @param Index0 The index of the first point.
114  /// @param Index1 The index of the second point.
115  /// @param Intersect The position of the intersection.
116  IntersectionInShape(const Whole Index0, const Whole Index1, Vector2 Intersect) :
117  Position(Intersect)
118  {
119  this->Index[0] = Index0;
120  this->Index[1] = Index1;
121  this->OnVertex[0] = false;
122  this->OnVertex[1] = false;
123  }
124  };//IntersectionInShape
125 
126  ///////////////////////////////////////////////////////////////////////////////
127  // Shape Methods
128 
130  OutSide(Procedural::SS_Right),
131  Closed(false)
132  { }
133 
135  { }
136 
138  {
139  if( !this->Closed || this->Points.size() < 2 ) {
140  MEZZ_EXCEPTION(ExceptionBase::INVALID_STATE_EXCEPTION,"2D shapes must be closed and must contain at least 2 points.");
141  }
142  if( !Other.Closed || Other.Points.size() < 2 ) {
143  MEZZ_EXCEPTION(ExceptionBase::PARAMETERS_EXCEPTION,"2D shapes must be closed and must contain at least 2 points.");
144  }
145 
146  // Compute the intersection between the 2 shapes
147  IntersectionContainer Intersections;
148  this->_FindAllIntersections(Other, Intersections);
149 
150  // Build the resulting shape
151  if( Intersections.empty() ) {
152  if( this->IsPointInside( Other.GetPoint(0) ) ) {
153  // Shape B is completely inside shape A
154  if( OpType == Procedural::BO_Union ) {
155  return *this;
156  }else if( OpType == Procedural::BO_Intersection ) {
157  return Other;
158  }else if( OpType == Procedural::BO_Difference ) {
159  MultiShape ms;
160  ms.AddShape(*this);
161  ms.AddShape(Other);
162  ms.GetShape(1).SwitchSide();
163  return ms;
164  }
165  }else if( Other.IsPointInside( this->GetPoint(0) ) ) {
166  // Shape A is completely inside shape B
167  if( OpType == Procedural::BO_Union ) {
168  return Other;
169  }else if( OpType == Procedural::BO_Intersection ) {
170  return *this;
171  }else if( OpType == Procedural::BO_Difference ) {
172  MultiShape ms;
173  ms.AddShape(*this);
174  ms.AddShape(Other);
175  ms.GetShape(0).SwitchSide();
176  return ms;
177  }
178  }else{
179  if( OpType == Procedural::BO_Union ) {
180  MultiShape ms;
181  ms.AddShape( *this );
182  ms.AddShape( Other );
183  return ms;
184  }else if( OpType == Procedural::BO_Intersection ) {
185  return Shape();//empty result
186  }else if( OpType == Procedural::BO_Difference ) {
187  return Shape();//empty result
188  }
189  }
190  }
191  MultiShape outputMultiShape;
192 
193  const Shape* InputShapes[2];
194  InputShapes[0] = this;
195  InputShapes[1] = &Other;
196 
197  while( !Intersections.empty() )
198  {
199  Shape outputShape;
200  UInt8 shapeSelector = 0; // 0 : first shape, 1 : second shape
201 
202  Vector2 currentPosition = Intersections.begin()->Position;
203  IntersectionInShape firstIntersection = *Intersections.begin();
204  Whole currentSegment = firstIntersection.Index[ shapeSelector ];
205  Intersections.erase( Intersections.begin() );
206  outputShape.AddPoint( currentPosition );
207 
208  Char8 IsIncreasing = 0;// +1 if increasing, -1 if decreasing, 0 if undefined
209 
210  if( !this->_FindWhereToGo(InputShapes, OpType, firstIntersection, shapeSelector, IsIncreasing, currentSegment) ) {
211  // That intersection is located on a place where the resulting shape won't go => discard
212  continue;
213  }
214 
215  while( true )
216  {
217  // find the closest intersection on the same segment, in the correct direction
218  IntersectionIterator found_next_intersection = Intersections.end();
219  Real distanceToNextIntersection = std::numeric_limits<Real>::max();
220 
221  Whole nextPoint = currentSegment + ( IsIncreasing == 1 ? 1 : 0 );
222  Boole nextPointIsOnIntersection = false;
223 
224  for( IntersectionIterator it = Intersections.begin() ; it != Intersections.end() ; ++it )
225  {
226  if( currentSegment == it->Index[shapeSelector] ) {
227  if( ( ( it->Position - currentPosition ).DotProduct( it->Position - InputShapes[shapeSelector]->GetPoint(nextPoint) ) < 0 ) ||
228  ( it->OnVertex[shapeSelector] && nextPoint == it->Index[shapeSelector] ) )
229  {
230  // found an intersection between the current one and the next segment point
231  Real d = ( it->Position - currentPosition ).Length();
232  if( d < distanceToNextIntersection ) {
233  // check if we have the nearest intersection
234  found_next_intersection = it;
235  distanceToNextIntersection = d;
236  }
237  }
238  }
239  if( nextPoint == it->Index[shapeSelector] && it->OnVertex[shapeSelector] )
240  nextPointIsOnIntersection = true;
241  }
242 
243  // stop condition
244  if( currentSegment == firstIntersection.Index[shapeSelector] ) {
245  // we found ourselves on the same segment as the first intersection and no other
246  if( ( firstIntersection.Position - currentPosition ).DotProduct( firstIntersection.Position - InputShapes[shapeSelector]->GetPoint(nextPoint) ) < 0 ) {
247  Real d = ( firstIntersection.Position - currentPosition ).Length();
248  if( d > 0. && d < distanceToNextIntersection ) {
249  outputShape.Close();
250  break;
251  }
252  }
253  }
254 
255  // We actually found the next intersection => change direction and add current intersection to the list
256  if( found_next_intersection != Intersections.end() ) {
257  IntersectionInShape currentIntersection = *found_next_intersection;
258  Intersections.erase(found_next_intersection);
259  outputShape.AddPoint(currentIntersection.Position);
260  Boole result = this->_FindWhereToGo(InputShapes, OpType, currentIntersection, shapeSelector, IsIncreasing, currentSegment);
261  if( !result ) {
262  OGRE_EXCEPT(Ogre::Exception::ERR_INTERNAL_ERROR, "We should not be here!", "Procedural::Shape::_booleanOperation(const Procedural::Shape&, Procedural::BooleanOperationType)");
263  }
264  }else{
265  // no intersection found for the moment => just continue on the current segment
266  if( !nextPointIsOnIntersection ) {
267  if( IsIncreasing == 1 ) {
268  currentPosition = InputShapes[shapeSelector]->GetPoint( currentSegment + 1 );
269  }else{
270  currentPosition = InputShapes[shapeSelector]->GetPoint( currentSegment );
271  }
272 
273  outputShape.AddPoint(currentPosition);
274  }
275  currentSegment = MathTools::WrappedModulo( currentSegment + IsIncreasing,InputShapes[shapeSelector]->GetSegCount() );
276  }
277  }
278 
279  outputMultiShape.AddShape(outputShape);
280  }
281  return outputMultiShape;
282  }
283 
284  Boole Shape::_IsLookingForOutside(const Procedural::BooleanOperation OpType, const Char8 ShapeSelector) const
285  {
286  switch( OpType )
287  {
288  case Procedural::BO_Union: return true;
289  case Procedural::BO_Intersection: return false;
290  case Procedural::BO_Difference: return ( ShapeSelector == 0 );
291  default: return true;
292  }
293  }
294 
295  Char8 Shape::_IsIncreasing(const Real Dot, const Procedural::BooleanOperation OpType, const Char8 ShapeSelector) const
296  {
297  if( Dot < 0 && OpType == Procedural::BO_Union )
298  return -1;
299  if( Dot > 0 && OpType == Procedural::BO_Intersection )
300  return -1;
301  if( OpType == Procedural::BO_Difference ) {
302  if( ( Dot < 0 && ShapeSelector == 0) || ( Dot > 0 && ShapeSelector == 1 ) )
303  return -1;
304  }
305  return 1;
306  }
307 
308  Boole Shape::_FindWhereToGo(const Shape* InputShapes[], const Procedural::BooleanOperation OpType, const IntersectionInShape& Intersection, UInt8& ShapeSelector, Char8& IsIncreasing, Whole& CurrentSegment) const
309  {
310  if( Intersection.OnVertex[0] || Intersection.OnVertex[1] ) {
311  // determine 4 directions with normal info
312  // if 2 normals "face each other" then you have the couple of outside directions
313  Vector2 Directions[4];
314  Char8 Sides[4];
315  UInt8 IncomingDirection;
316 
317  // fill-in the incoming arrays
318  if( IsIncreasing == 0 ) {
319  IncomingDirection = 255;
320  }else{
321  IncomingDirection = ShapeSelector + ( IsIncreasing == 1 ? 2 : 0 );
322  }
323  for( UInt8 i = 0 ; i < 2 ; i++ )
324  {
325  if( Intersection.OnVertex[i] ) {
326  Directions[ i ] = InputShapes[i]->GetDirectionBefore( Intersection.Index[i] );
327  Directions[ 2 + i ] = -InputShapes[i]->GetDirectionAfter( Intersection.Index[i] );
328  }else{
329  Directions[ 2 + i ] = -InputShapes[i]->GetDirectionAfter( Intersection.Index[i] );
330  Directions[ i ] = -Directions[ 2 + i ];
331  }
332  }
333  for( UInt8 i = 0 ; i < 4 ; i++ )
334  {
335  Sides[i] = ( i / 2 == 0 ? -1 : 1 ) * ( InputShapes[ i % 2 ]->OutSide == Procedural::SS_Right ? -1 : 1 );
336  }
337 
338  Boole IsOutside[4];
339  std::pair<Real,UInt8> SortedDirections[4];
340 
341  // sort by angle
342  for( Integer i = 0 ; i < 4 ; i++ )
343  {
344  if( i == 0 ) {
345  SortedDirections[i].first = 0;
346  }else{
347  SortedDirections[i].first = Sides[0] * Directions[0].AngleTo( Directions[i] );
348  }
349  SortedDirections[i].second = i;
350  }
351 
352  std::sort(SortedDirections, SortedDirections + 4, _SortAngles);
353 
354  //find which segments are outside
355  if( Sides[0] != Sides[ SortedDirections[1].second ] ) {
356  IsOutside[0] = IsOutside[ SortedDirections[1].second ] = true;
357  IsOutside[ SortedDirections[2].second ] = IsOutside[ SortedDirections[3].second ] = false;
358  }else{
359  IsOutside[ SortedDirections[1].second ] = IsOutside[ SortedDirections[2].second ] = true;
360  IsOutside[ SortedDirections[3].second ] = IsOutside[ SortedDirections[0].second ] = false;
361  }
362 
363  //find first eligible segment that is not the current segment
364  for( unsigned short i = 0 ; i < 4 ; i++ )
365  {
366  if( ( IsOutside[i] == this->_IsLookingForOutside( OpType, i % 2 ) ) && ( i != IncomingDirection ) ) {
367  ShapeSelector = i % 2;
368  IsIncreasing = i / 2 == 0 ? 1 : -1;
369  CurrentSegment = Intersection.Index[ ShapeSelector ];
370  return true;
371  }
372  }
373  // if we reach here, it means that no segment is eligible! (it should only happen with difference opereation
374  return false;
375  }else{
376  // determine which way to go
377  Integer NextShapeSelector = ( ShapeSelector + 1 ) % 2;
378 
379  Real Dot = InputShapes[NextShapeSelector]->GetDirectionAfter( Intersection.Index[NextShapeSelector] ).DotProduct( InputShapes[ShapeSelector]->GetNormalAfter(CurrentSegment) );
380  IsIncreasing = this->_IsIncreasing( Dot, OpType, NextShapeSelector );
381 
382  ShapeSelector = NextShapeSelector;
383 
384  CurrentSegment = Intersection.Index[ ShapeSelector ];
385  return true;
386  }
387  }
388 
389  void Shape::_FindAllIntersections(const Shape& Other, IntersectionContainer& Intersections) const
390  {
391  for( unsigned short SelfSeg = 0 ; SelfSeg < this->GetSegCount() ; ++SelfSeg )
392  {
393  LineSegment2D Seg1( this->GetPoint( SelfSeg ), this->GetPoint( SelfSeg + 1 ) );
394 
395  for( unsigned short OtherSeg = 0 ; OtherSeg < Other.GetSegCount() ; ++OtherSeg )
396  {
397  LineSegment2D Seg2( Other.GetPoint( OtherSeg ), Other.GetPoint( OtherSeg + 1 ) );
398 
400  if( Result.first ) {
401  IntersectionInShape Inter( SelfSeg, OtherSeg, Result.second );
402  // check if intersection is "borderline" : too near to a vertex
403  if( Seg1.PointA.SquaredDistance( Result.second ) < 1e-8 ) {
404  Inter.OnVertex[0] = true;
405  }
406  if( Seg1.PointB.SquaredDistance( Result.second ) < 1e-8 ) {
407  Inter.OnVertex[0] = true;
408  Inter.Index[0]++;
409  }
410  if( Seg2.PointA.SquaredDistance( Result.second ) < 1e-8 ) {
411  Inter.OnVertex[1] = true;
412  }
413  if( Seg2.PointB.SquaredDistance( Result.second ) < 1e-8 ) {
414  Inter.OnVertex[1] = true;
415  Inter.Index[1]++;
416  }
417 
418  Intersections.push_back( Inter );
419  }
420  }
421  }
422  }
423 
424  ///////////////////////////////////////////////////////////////////////////////
425  // Utility
426 
427  Mesh* Shape::GenerateMesh(const String& Name, const String& Group) const
428  {
429  Ogre::ManualObject* TempMan = new Ogre::ManualObject("TempMan");
430  TempMan->begin("BaseWhiteNoLighting",Ogre::RenderOperation::OT_LINE_STRIP);
431 
432  this->_AppendToManualObject(TempMan);
433 
434  TempMan->end();
435  Mesh* NewMesh = MeshManager::GetSingletonPtr()->_WrapInternalMesh( TempMan->convertToMesh(Name,Group) );
436  delete TempMan;
437  return NewMesh;
438  }
439 
441  {
442  this->Points.insert(this->Points.end(), Other.Points.begin(), Other.Points.end());
443  return *this;
444  }
445 
447  {
448  if( this->Points.empty() ) {
449  this->AppendShape(Other);
450  }else{
451  Vector2 refVector = *( this->Points.end() - 1 );
452  Point2DContainer pointList( Other.Points.begin(), Other.Points.end() );
453  for( Point2DIterator it = pointList.begin() ; it != pointList.end() ; ++it )
454  { *it += refVector; }
455  this->Points.insert( this->Points.end(), pointList.begin(), pointList.end() );
456  }
457  return *this;
458  }
459 
460  Shape Shape::ExtractSubShape(const Whole First, const Whole Last)
461  {
462  Shape Ret;
463  for( Whole i = First ; i <= Last ; ++i )
464  { Ret.AddPoint( this->Points[i] ); }
465  Ret.SetOutSide( this->OutSide );
466  if( this->Closed ) {
467  Ret.Close();
468  }
469  return Ret;
470  }
471 
473  { return ( this->Points.size() - 1 ) + ( this->Closed ? 1 : 0 ); }
474 
476  {
477  Real Length = 0;
478  for( Whole Index = 0 ; Index < this->Points.size() - 1 ; ++Index )
479  { Length += ( this->Points[ Index + 1 ] - this->Points[Index] ).Length(); }
480  if( this->Closed ) {
481  Length += ( this->Points.back() - *(this->Points.begin()) ).Length();
482  }
483  return Length;
484  }
485 
486  Boole Shape::IsPointInside(const Vector2& Point) const
487  {
488  // Draw a horizontal lines that goes through "point"
489  // Using the closest intersection, find whethe the point is actually inside
490  Integer ClosestSegmentIndex = -1;
491  Real ClosestSegmentDistance = std::numeric_limits<Real>::max();
492  Vector2 ClosestSegmentIntersection;
493 
494  for( Whole CurrSeg = 0 ; CurrSeg < this->GetSegCount() ; ++CurrSeg )
495  {
496  Vector2 A = this->GetPoint( CurrSeg );
497  Vector2 B = this->GetPoint( CurrSeg + 1 );
498  if( A.Y != B.Y && ( A.Y - Point.Y ) * ( B.Y - Point.Y ) <= 0.0 ) {
499  Vector2 Intersect( A.X + ( Point.Y - A.Y ) * ( B.X - A.X ) / ( B.Y - A.Y ), Point.Y );
500  Real Dist = MathTools::Abs( Point.X - Intersect.X );
501  if( Dist < ClosestSegmentDistance ) {
502  ClosestSegmentIndex = CurrSeg;
503  ClosestSegmentDistance = Dist;
504  ClosestSegmentIntersection = Intersect;
505  }
506  }
507  }
508  if( ClosestSegmentIndex != -1 ) {
509  if( this->GetNormalAfter( ClosestSegmentIndex ).X * ( Point.X - ClosestSegmentIntersection.X ) < 0 ) {
510  return true;
511  }else{
512  return false;
513  }
514  }
515  if( this->FindRealOutSide() == this->OutSide ) {
516  return false;
517  }else{
518  return true;
519  }
520  }
521 
523  {
524  Real sqRadius = 0.0;
525  for( Whole Index = 0 ; Index < this->Points.size() ; ++Index )
526  { sqRadius = std::max( sqRadius, this->Points[Index].SquaredLength() ); }
527  return MathTools::Sqrt(sqRadius);
528  }
529 
530  Vector2 Shape::GetPosition(const Whole Index, const Real Coord) const
531  {
532  if( !this->Closed || Index >= this->Points.size() )
533  MEZZ_EXCEPTION(ExceptionBase::PARAMETERS_EXCEPTION,"Specified point index is out of bounds.");
534  if( Coord < 0.0 || Coord > 1.0 )
535  MEZZ_EXCEPTION(ExceptionBase::PARAMETERS_EXCEPTION,"Coord must be between 0 and 1.");
536  Vector2 A = this->GetPoint( Index );
537  Vector2 B = this->GetPoint( Index + 1 );
538  return A + ( ( B - A ) * Coord );
539  }
540 
542  {
543  if( this->Points.size() < 2 )
544  MEZZ_EXCEPTION(ExceptionBase::PARAMETERS_EXCEPTION, "The shape must contain at least 2 points.");
545  Whole Index = 0;
546  while( true )
547  {
548  Real NextLen = ( this->GetPoint( Index + 1 ) - this->GetPoint( Index ) ).Length();
549  if( Coord > NextLen ) {
550  Coord -= NextLen;
551  }else{
552  return this->GetPosition(Index,Coord);
553  }
554  if( !this->Closed && Index >= this->Points.size() - 2 ) {
555  return this->Points.back();
556  }
557  Index++;
558  }
559  }
560 
562  {
563  return this->MirrorAroundPoint(Vector2(0,0),Flip);
564  }
565 
566  Shape& Shape::MirrorAroundPoint(const Real X, const Real Y, Boole Flip)
567  {
568  return this->MirrorAroundPoint(Vector2(X,Y),Flip);
569  }
570 
572  {
573  Integer NumPoints = (Integer)this->Points.size();
574  if( Flip ) {
575  for( Integer CurrPoint = NumPoints - 1 ; CurrPoint >= 0 ; CurrPoint-- )
576  {
577  Vector2 Pos = this->Points.at(CurrPoint) - Point;
578  this->Points.push_back( ( Pos * -1.0 ) + Point );
579  }
580  }else{
581  for( Integer CurrPoint = 0 ; CurrPoint < NumPoints ; CurrPoint++ )
582  {
583  Vector2 Pos = this->Points.at(CurrPoint) - Point;
584  this->Points.push_back( ( Pos * -1.0 ) + Point );
585  }
586  }
587  return *this;
588  }
589 
591  {
592  Integer NumPoints = (Integer)this->Points.size();
593  Vector2 Normal = Axis.Perpendicular().GetNormal();
594  if( Flip ) {
595  for( Integer CurrPoint = 0 ; CurrPoint < NumPoints ; CurrPoint++ )
596  {
597  Vector2 Pos = this->Points.at(CurrPoint);
598  Pos = Pos.Reflect(Normal);
599  if( Pos != this->Points.at(CurrPoint) ) {
600  this->Points.push_back(Pos);
601  }
602  }
603  }else{
604  for( Integer CurrPoint = NumPoints - 1 ; CurrPoint >= 0 ; CurrPoint-- )
605  {
606  Vector2 Pos = this->Points.at(CurrPoint);
607  Pos = Pos.Reflect(Normal);
608  if( Pos != this->Points.at(CurrPoint) ) {
609  this->Points.push_back(Pos);
610  }
611  }
612  }
613  return *this;
614  }
615 
616  Shape& Shape::Reflect(const Vector2& Normal)
617  {
618  for( Point2DIterator it = this->Points.begin() ; it != this->Points.end() ; ++it )
619  { *it = it->Reflect(Normal); }
620  return *this;
621  }
622 
624  {
625  this->Closed = true;
626  return *this;
627  }
628 
630  {
631  return this->Closed;
632  }
633 
635  {
636  std::reverse(this->Points.begin(), this->Points.end());
637  this->SwitchSide();
638  return *this;
639  }
640 
642  {
643  this->Points.clear();
644  return *this;
645  }
646 
647  ///////////////////////////////////////////////////////////////////////////////
648  // Point Management
649 
651  {
652  this->Points.push_back(Point);
653  return *this;
654  }
655 
656  Shape& Shape::AddPoint(const Real X, const Real Y)
657  {
658  this->Points.push_back(Vector2(X,Y));
659  return *this;
660  }
661 
663  {
664  if( this->Points.empty() ) {
665  this->Points.push_back( Point );
666  }else{
667  this->Points.push_back( Point + *( this->Points.end() - 1 ) );
668  }
669  return *this;
670  }
671 
672  Shape& Shape::AddPointRel(const Real X, const Real Y)
673  {
674  if( this->Points.empty() ) {
675  this->Points.push_back( Vector2(X,Y) );
676  }else{
677  this->Points.push_back( Vector2(X,Y) + *( this->Points.end() - 1 ) );
678  }
679  return *this;
680  }
681 
682  Shape& Shape::InsertPoint(const Whole Index, const Real X, const Real Y)
683  {
684  this->Points.insert( this->Points.begin() + Index, Vector2(X,Y) );
685  return *this;
686  }
687 
688  Shape& Shape::InsertPoint(const Whole Index, const Vector2& Point)
689  {
690  this->Points.insert( this->Points.begin() + Index, Point );
691  return *this;
692  }
693 
694  const Vector2& Shape::GetPoint(const Integer Index) const
695  {
696  return this->Points[ this->GetBoundedIndex(Index) ];
697  }
698 
700  {
701  return this->Points.size();
702  }
703 
705  {
706  if( this->Closed ) {
707  return MathTools::WrappedModulo( Index, this->Points.size() );
708  }
709  return MathTools::Clamp( Index, Integer(0), Integer(this->Points.size() - 1) );
710  }
711 
713  { return this->Points; }
714 
716  { return this->Points; }
717 
719  { return this->Points; }
720 
721  ///////////////////////////////////////////////////////////////////////////////
722  // Transform
723 
725  {
726  for( Point2DIterator it = this->Points.begin() ; it != this->Points.end() ; ++it )
727  *it += Trans;
728  return *this;
729  }
730 
731  Shape& Shape::Translate(const Real TransX, const Real TransY)
732  {
733  return this->Translate( Vector2(TransX,TransY) );
734  }
735 
736  Shape& Shape::Rotate(const Real Angle)
737  {
738  Real c = MathTools::Cos( Angle );
739  Real s = MathTools::Sin( Angle );
740  for( Point2DIterator it = this->Points.begin() ; it != this->Points.end() ; ++it )
741  {
742  Real x = it->X;
743  Real y = it->Y;
744  it->X = c * x - s * y;
745  it->Y = s * x + c * y;
746  }
747  return *this;
748  }
749 
750  Shape& Shape::Scale(const Real Scaling)
751  {
752  return this->Scale(Scaling,Scaling);
753  }
754 
755  Shape& Shape::Scale(const Real ScaleX, const Real ScaleY)
756  {
757  for( Point2DIterator it = this->Points.begin() ; it != this->Points.end() ; ++it )
758  {
759  it->X *= ScaleX;
760  it->Y *= ScaleY;
761  }
762  return *this;
763  }
764 
765  Shape& Shape::Scale(const Vector2& Scaling)
766  {
767  return this->Scale(Scaling.X,Scaling.Y);
768  }
769 
770  ///////////////////////////////////////////////////////////////////////////////
771  // Directions and Normals
772 
774  {
775  // If the path isn't closed, we get a different calculation at the end, because
776  // the tangent shall not be null
777  if( !this->Closed && Index == this->Points.size() - 1 && Index > 0) {
778  return ( this->Points[Index] - this->Points[ Index - 1 ] ).GetNormal();
779  }else{
780  return ( this->GetPoint( Index + 1 ) - this->GetPoint(Index) ).GetNormal();
781  }
782  }
783 
785  {
786  // If the path isn't closed, we get a different calculation at the end, because
787  // the tangent shall not be null
788  if( !this->Closed && Index == 1) {
789  return ( this->Points[1] - this->Points[0] ).GetNormal();
790  }else{
791  return ( this->GetPoint(Index) - this->GetPoint( Index - 1 ) ).GetNormal();
792  }
793  }
794 
796  {
797  return ( this->GetDirectionAfter(Index) + this->GetDirectionBefore(Index) ).GetNormal();
798  }
799 
801  {
802  if( this->OutSide == Procedural::SS_Right ) {
803  return -( this->GetDirectionAfter(Index).Perpendicular() );
804  }
805  return this->GetDirectionAfter(Index).Perpendicular();
806  }
807 
809  {
810  if( this->OutSide == Procedural::SS_Right ) {
811  return -( this->GetDirectionBefore(Index).Perpendicular() );
812  }
813  return this->GetDirectionBefore(Index).Perpendicular();
814  }
815 
816  Vector2 Shape::GetAvgNormal(const Whole Index) const
817  {
818  if( this->OutSide == Procedural::SS_Right ) {
819  return -( this->GetAvgDirection(Index).Perpendicular() );
820  }
821  return this->GetAvgDirection(Index).Perpendicular();
822  }
823 
824  ///////////////////////////////////////////////////////////////////////////////
825  // OutSide
826 
828  {
829  this->OutSide = Side;
830  return *this;
831  }
832 
834  {
835  return this->OutSide;
836  }
837 
839  {
841  return *this;
842  }
843 
845  {
846  Real X = this->Points[0].X;
847  Integer Index = 0;
848  for( unsigned short CurrPoint = 1 ; CurrPoint < this->Points.size() ; ++CurrPoint )
849  {
850  if( X < this->Points[CurrPoint].X ) {
851  X = this->Points[CurrPoint].X;
852  Index = CurrPoint;
853  }
854  }
855  Real Alpha1 = Vector2::Unit_Y().AngleTo( this->GetDirectionAfter(Index) );
856  Real Alpha2 = Vector2::Unit_Y().AngleTo( -( this->GetDirectionBefore(Index) ) );
857  if( Alpha1 < Alpha2 ) {
858  return Procedural::SS_Right;
859  }else{
860  return Procedural::SS_Left;
861  }
862  }
863 
865  {
866  return ( this->FindRealOutSide() == this->OutSide );
867  }
868 
869  ///////////////////////////////////////////////////////////////////////////////
870  // Conversion
871 
873  {
874  Path RetPath;
875  ConstPoint2DIterator BeginIt = this->Points.begin();
876  for( ConstPoint2DIterator PointIt = BeginIt ; PointIt != this->Points.end() ; ++PointIt )
877  { RetPath.AddPoint( Vector3( (*PointIt).X, 0, (*PointIt).Y ) ); }
878  if( this->Closed && this->Points.size() > 2 ) {
879  RetPath.AddPoint( Vector3( (*BeginIt).X, 0, (*BeginIt).Y ) );
880  }
881  return RetPath;
882  }
883 
885  {
886  if( !this->Closed ) {
887  Shape TempShape;
888  TempShape.SetOutSide( this->OutSide );
889  for( Whole Index = 0 ; Index < this->Points.size() ; Index++ )
890  { TempShape.AddPoint( this->Points[Index] + ( this->GetAvgNormal(Index) * Amount ) ); }
891  for( Integer Index = this->Points.size() - 1 ; Index >= 0 ; Index-- )
892  { TempShape.AddPoint( this->Points[Index] - ( this->GetAvgNormal(Index) * Amount ) ); }
893  TempShape.Close();
894  return MultiShape().AddShape(TempShape);
895  }else{
896  MultiShape RetShape;
897  Shape TempShape1;
898  for( Whole Index = 0 ; Index < this->Points.size() ; Index++ )
899  { TempShape1.AddPoint( this->Points[Index] + ( this->GetAvgNormal(Index) * Amount ) ); }
900  TempShape1.Close();
901  TempShape1.SetOutSide( this->OutSide );
902  RetShape.AddShape(TempShape1);
903  Shape TempShape2;
904  for( Whole Index = 0 ; Index < this->Points.size() ; Index++ )
905  { TempShape2.AddPoint( this->Points[Index] - ( this->GetAvgNormal(Index) * Amount ) ); }
906  TempShape2.Close();
908  RetShape.AddShape(TempShape2);
909  return RetShape;
910  }
911  }
912 
913  ///////////////////////////////////////////////////////////////////////////////
914  // Boolean Operations
915 
917  { return this->_BooleanOperation(Other,Procedural::BO_Union); }
918 
920  { return this->_BooleanOperation(Other,Procedural::BO_Intersection); }
921 
923  { return this->_BooleanOperation(Other,Procedural::BO_Difference); }
924 
925  ///////////////////////////////////////////////////////////////////////////////
926  // Internal Methods
927 
928  void Shape::_AppendToManualObject(Ogre::ManualObject* Object) const
929  {
930 
931  }
932  }//Procedural
933  }//Graphics
934 }//Mezzanine
935 
936 #endif
static Vector2 Unit_Y()
Gets a vector representing the Y unit of a Vector2.
Definition: vector2.cpp:87
MultiShape _BooleanOperation(const Shape &Other, const Procedural::BooleanOperation OpType) const
Performs a boolean operation between this shape and another shape.
Definition: shape.cpp:137
Point2DContainer::iterator Point2DIterator
Iterator type for a Point2DContainer.
Vector2 GetDirectionBefore(const Whole Index) const
Gets the direction from a point to the previous point in the sequence.
Definition: shape.cpp:784
std::vector< IntersectionInShape > IntersectionContainer
Basic container type for the storage of Intersections in this class.
Definition: shape.h:99
Vector2 Perpendicular() const
Generates a Vector2 that is perpendicular to this vector.
Definition: vector2.cpp:263
Boole _IsLookingForOutside(const Procedural::BooleanOperation OpType, const Char8 ShapeSelector) const
Gets whether or not the current boolean operation is looking for outside edges of the two shapes bein...
Definition: shape.cpp:284
Shape & AddPoint(const Vector2 &Point)
Adds a point to the shape.
Definition: shape.cpp:650
Procedural::ShapeSide FindRealOutSide() const
On a closed shape, find if the outside is located on the right or on the left.
Definition: shape.cpp:844
bool Boole
Generally acts a single bit, true or false.
Definition: datatypes.h:173
const Vector2 & GetPoint(const Integer Index) const
Gets a point by index which can be out of bounds and will wrap.
Definition: shape.cpp:694
Boole IsPointInside(const Vector2 &Point) const
Tells whether a point is inside a shape or not.
Definition: shape.cpp:486
Vector2 GetDirectionAfter(const Whole Index) const
Gets the direction of a point to the next point in the sequence.
Definition: shape.cpp:773
Point2DContainer & GetPointsReference()
Gets raw vector data of this shape as a non-const reference.
Definition: shape.cpp:715
IntersectionTestResult Intersects(const LineSegment2D &Other) const
Gets whether or not another line segment intersects with this one.
Definition: linesegment.cpp:70
Whole GetPointCount() const
Gets the number of points in this shape.
Definition: shape.cpp:699
Procedural::ShapeSide GetOutSide() const
Gets which side is the OutSide of this shape.
Definition: shape.cpp:833
MultiShape & AddShape(const Shape &ToAdd)
Adds a shape to this MultiShape.
Definition: multishape.cpp:298
Vector2 GetPosition(const Whole Index, const Real Coord) const
Gets the position on a segment. the provided index is out of bounds or the Coord is outside of the ra...
Definition: shape.cpp:530
Shape & Mirror(Boole Flip=false)
Create a symetric copy of the points in this shape at the origin point.
Definition: shape.cpp:561
#define MEZZ_EXCEPTION(num, desc)
An easy way to throw exceptions with rich information.
Definition: exception.h:3048
This class is used to check and modify the properties of a graphics mesh.
Definition: mesh.h:63
int Integer
A datatype used to represent any integer close to.
Definition: datatypes.h:154
Shape & Reflect(const Vector2 &Normal)
Reflect all points in this shape against a zero-origined line with a given normal.
Definition: shape.cpp:616
Char8 _IsIncreasing(const Real Dot, const Procedural::BooleanOperation OpType, const Char8 ShapeSelector) const
Gets whether or not the segment being operated on is to be incremented.
Definition: shape.cpp:295
Shape & Reverse()
Reverses direction/ordering of the segments in this shape.
Definition: shape.cpp:634
IntersectionContainer::iterator IntersectionIterator
Iterator type for Intersections stored in this class.
Definition: shape.h:101
Shape & Scale(const Real Scaling)
Applies the given scale to all the points in this shape.
Definition: shape.cpp:750
Shape & Translate(const Vector2 &Trans)
Applies the given translation to all the points in this shape.
Definition: shape.cpp:724
MultiShape Thicken(const Real Amount)
Applies a "thickness" to a shape, ie a bit like the extruder, but in 2D.
Definition: shape.cpp:884
Shape & AppendShapeRel(const Shape &Other)
Appends all the points from another shape to this shape with their positions relative to the position...
Definition: shape.cpp:446
IntersectionInShape(const Whole Index0, const Whole Index1, Vector2 Intersect)
Class constructor.
Definition: shape.cpp:116
Real AngleTo(const Vector2 &Other) const
Gets an oriented angle between this Vector2 and another Vector2.
Definition: vector2.cpp:288
uint8_t UInt8
An 8-bit unsigned integer.
Definition: datatypes.h:118
Boole Closed
Whether or not the first point and last point should be connected, closing the shape.
Definition: shape.h:113
Vector2 GetAvgDirection(const Whole Index) const
Gets the averaged direction from the specified point to both the next and previous points in the sequ...
Definition: shape.cpp:795
This implements the exception hiearchy for Mezzanine.
MultiShape BooleanDifference(const Shape &Other) const
Computes the difference between this shape and another one.
Definition: shape.cpp:922
Point2DContainer::const_iterator ConstPoint2DIterator
Const Iterator type for a Point2DContainer.
std::pair< Boole, Vector2 > IntersectionTestResult
This is a type used for the return of a intersection test.
Definition: linesegment.h:58
Real DotProduct(const Vector2 &Other) const
This is used to calculate the dotproduct of this and another vector.
Definition: vector2.cpp:248
Shape & AppendShape(const Shape &Other)
Appends all the points from another shape to this shape.
Definition: shape.cpp:440
Vector2 PointA
The first point defining the segment.
Definition: linesegment.h:64
float Real
A Datatype used to represent a real floating point number.
Definition: datatypes.h:141
char Char8
A datatype to represent one character.
Definition: datatypes.h:169
Vector2 Reflect(const Vector2 &Normal) const
Gets a reflection vector to the line with the given normal.
Definition: vector2.cpp:266
Procedural::ShapeSide OutSide
Sets which extreme side of this shape is to be considered the outside of the shape. Useful when a shape is not closed.
Definition: shape.h:110
ShapeSide
An enum used to express which side to work with in Shape operations.
std::vector< Vector2 > Point2DContainer
Basic container type for the storage of 2D points.
Path & AddPoint(const Vector3 &ToAdd)
Adds a point to this path.
Definition: path.cpp:275
static MeshManager * GetSingletonPtr()
Fetches a pointer to the singleton.
Definition: singleton.h:97
Real Y
Coordinate on the Y vector.
Definition: vector2.h:69
Completely merges the the two buffers.
Shape & MirrorAroundPoint(const Real X, const Real Y, Boole Flip=false)
Create a symetric copy of the points in this shape at an arbitrary point.
Definition: shape.cpp:566
Shape & Close()
Makes this a closed shape, connecting the last point to the first point.
Definition: shape.cpp:623
Shape ExtractSubShape(const Whole First, const Whole Last)
Extracts a part of the shape as a new shape.
Definition: shape.cpp:460
Real X
Coordinate on the X vector.
Definition: vector2.h:67
Path ConvertToPath() const
Converts the shape to a 3D path.
Definition: shape.cpp:872
Shape & Rotate(const Real Angle)
Applies the given rotation to all the points in this shape.
Definition: shape.cpp:736
This is used to represent a point on a 2 dimentional area, such as a screen.
Definition: vector2.h:63
MultiShape BooleanIntersect(const Shape &Other) const
Computes the intersection between this shape and another one.
Definition: shape.cpp:916
Point2DContainer GetPoints() const
Gets a copy of raw vector data of this shape.
Definition: shape.cpp:712
Real FindBoundingRadius() const
Computes the radius of a bounding circle centered on the origin of this shape.
Definition: shape.cpp:522
Shape & SetOutSide(const Procedural::ShapeSide Side)
Sets which side is the OutSide of this shape.
Definition: shape.cpp:827
Whole GetSegCount() const
Gets the number of segments in this shape.
Definition: shape.cpp:472
Convenience class storing data on the point in a 2D shape where multiple segments intersect...
Definition: shape.cpp:103
BooleanOperation
An enum used to describe which boolean operation to take when processing two triangle buffers...
Real SquaredDistance(const Vector2 &Other) const
Gets the squared distance between this and another vector.
Definition: vector2.cpp:254
Real GetTotalLength() const
Gets the total length of all segments in this shape.
Definition: shape.cpp:475
Gets only the parts of the two buffers that aren't overlapping.
Thrown when the available information should have worked but failed for unknown reasons.
Definition: exception.h:113
Whole GetBoundedIndex(const Integer Index) const
Converts a potentially unsafe Integer index into a safe Whole index.
Definition: shape.cpp:704
Boole OnVertex[2]
An Array storing whether the intersection is on or very near a vertex belonging to either shape being...
Definition: shape.cpp:108
Thrown when parameters are checked at runtime and found invalid.
Definition: exception.h:108
Gets only the parts of the two buffers that are overlapping.
MultiShape BooleanUnion(const Shape &Other) const
Computes the union between this shape and another one.
Definition: shape.cpp:919
Shape & Reset()
Clears all points from this shape.
Definition: shape.cpp:641
Vector2 Position
The position of the intersection.
Definition: shape.cpp:110
Vector2 GetNormalBefore(const Whole Index) const
Gets the normal of the segment before the specified point.
Definition: shape.cpp:808
This is used to represent a point in space, or a vector through space.
Definition: vector3.h:77
Shape & AddPointRel(const Vector2 &Point)
Adds a point to the shape, relative to the last position added.
Definition: shape.cpp:662
Boole IsClosed() const
Gets whether or not this shape is closed.
Definition: shape.cpp:629
The bulk of the engine components go in this namspace.
Definition: actor.cpp:56
Point2DContainer Points
Container storing all of the points that form this shape.
Definition: shape.h:107
Shape & MirrorAroundAxis(const Vector2 &Axis, Boole Flip=false)
Create a symetric copy of the points in this shape at an arbitrary axis.
Definition: shape.cpp:590
unsigned long Whole
Whole is an unsigned integer, it will be at least 32bits in size.
Definition: datatypes.h:151
Vector2 PointB
The second point defining the segment.
Definition: linesegment.h:66
Mesh * GenerateMesh(const String &Name, const String &Group) const
Outputs a mesh representing this shape.
Definition: shape.cpp:427
void _AppendToManualObject(Ogre::ManualObject *Object) const
Appends the shape vertices to a manual object being edited.
Definition: shape.cpp:928
Vector2 GetNormal() const
Gets the normal of this Vector2.
Definition: vector2.cpp:282
Vector2 GetAvgNormal(const Whole Index) const
Gets the averaged normal of the segments before and after the specified point.
Definition: shape.cpp:816
A geometry math class for expressing a line connecting 2 points in 2D space.
Definition: linesegment.h:52
A collection of interconnected 2D points used to express an arbitrary 2D shape.
Definition: shape.h:95
Vector2 GetNormalAfter(const Whole Index) const
Gets the normal of the segment after the specified point.
Definition: shape.cpp:800
Shape & GetShape(const Whole Index)
Gets a shape by index.
Definition: multishape.cpp:311
A grouping of individual 2D shapes used to express more elaborate shapes.
Definition: multishape.h:87
Whole Index[2]
An Array storing indexes to points that are the first vertices of segments that intersect for each of...
Definition: shape.cpp:106
Boole _FindWhereToGo(const Shape *InputShapes[], const Procedural::BooleanOperation OpType, const IntersectionInShape &Intersection, UInt8 &ShapeSelector, Char8 &IsIncreasing, Whole &CurrentSegment) const
Determines which segment to follow when an intersection is encountered.
Definition: shape.cpp:308
A collection of interconnected 3D points used to express path through 3D space.
Definition: path.h:93
std::string String
A datatype used to a series of characters.
Definition: datatypes.h:159
Shape & InsertPoint(const Whole Index, const Real X, const Real Y)
Inserts a point to the shape.
Definition: shape.cpp:682
Shape & SwitchSide()
Toggles the currently set "OutSide".
Definition: shape.cpp:838
void _FindAllIntersections(const Shape &Other, IntersectionContainer &Intersections) const
Finds all detectable intersections between this shape and another shape.
Definition: shape.cpp:389
Boole IsOutsideRealOutside() const
Gets whether the currently set OutSide is the real Outside.
Definition: shape.cpp:864