瀏覽代碼

refactoring+improvements to fingerDetection

Alexander Hendrich 11 年之前
父節點
當前提交
9650e0cf4d

+ 2 - 4
bbiwarg/Images/EdgeImage.cs

@@ -57,7 +57,7 @@ namespace bbiwarg.Images
             RoughImage.FillConvexPoly(polygon, new Gray(0));
         }
 
-        public Vector2D findNextEdge(Vector2D start, Vector2D direction, int maxSearchSize = 0, bool roughEdge = true)
+        public Vector2D findNextRoughEdge(Vector2D start, Vector2D direction, int maxSearchSize = 0)
         {
             Vector2D maxGrow = (Parameters.ImageMaxPixel - start) / direction;
             Vector2D maxDecline = start / direction.getAbsolute();
@@ -88,10 +88,8 @@ namespace bbiwarg.Images
             {
                 end += direction;
 
-                if ((roughEdge && isRoughEdgeAt(end)) || (!roughEdge && isEdgeAt(end)))
-                {
+                if (isRoughEdgeAt(end))
                     return end;
-                }
             }
 
             return null;

+ 1 - 1
bbiwarg/InputHandler.cs

@@ -264,7 +264,7 @@ namespace bbiwarg
             {
                 for (int i = 0; i < f.SliceTrail.NumSlices; i++)
                     OutputImages[1].drawLineSegment(f.SliceTrail[i].LineSegment, Parameters.FingerSliceColor);
-                OutputImages[1].drawContour(f.Contour, Parameters.FingerContourColor);
+                OutputImages[1].drawContour(f.getContour(Parameters.FingerContourMargin), Parameters.FingerContourColor);
                 OutputImages[1].drawLineSegment(f.LineSegment, Parameters.FingerTrackedColor);
                 OutputImages[1].drawText(f.MidPoint.IntX, f.MidPoint.IntY, f.TrackID.ToString(), Parameters.FingerIDColor);
             }

+ 4 - 4
bbiwarg/Parameters.cs

@@ -19,11 +19,11 @@ namespace bbiwarg
     class Parameters
     {
         // input
-        public static readonly InputType InputSource = InputType.Camera;
+        public static readonly InputType InputSource = InputType.Movie;
         public static readonly String InputMoviePath = "..\\..\\videos\\touch\\4.skv";
 
         // Logger
-        public static readonly LogSubject LogLevel = LogSubject.None;
+        public static readonly LogSubject LogLevel = LogSubject.Timer;
         public static readonly int ConsoleWidth = 90;
         public static readonly int ConsoleHeight = 30;
 
@@ -63,7 +63,7 @@ namespace bbiwarg
 
         // finger detection
         public static readonly int FingerStepSize = 1;
-        public static readonly int FingerMaxGapCounter = 5;
+        public static readonly int FingerMaxGapCounter = 3;
         public static readonly int FingerMaxSliceDifferencePerStep = 5;
         public static readonly int FingerMinNumSlices = 25 / FingerStepSize;
         public static readonly int FingerMaxWidth = 30;
@@ -71,7 +71,7 @@ namespace bbiwarg
         public static readonly int FingerRemoveNumSlicesForCorrection = 10 / FingerStepSize;
         public static readonly int FingerNumSlicesForRelativeDirection = FingerRemoveNumSlicesForCorrection;
         public static readonly int FingerOutMargin = 8;
-        public static readonly int FingerCrippleMinDifference = 20;
+        public static readonly int FingerMaxCrippleDifference = 20;
         public static readonly int FingerContourMargin = 4;
 
         // finger tracking

+ 4 - 1
bbiwarg/Recognition/FingerRecognition/Finger.cs

@@ -22,7 +22,6 @@ namespace bbiwarg.Recognition.FingerRecognition
         public Vector2D Direction { get { return SliceTrail.FittedDirection; } }
         public LineSegment2D LineSegment { get { return SliceTrail.LineSegment; } }
         public FingerSliceTrail SliceTrail { get; private set; }
-        public Contour<Point> Contour { get { return SliceTrail.Contour; } }
         public Hand Hand { get; private set; }
         public TouchEvent TouchEvent { get; private set; }
 
@@ -45,5 +44,9 @@ namespace bbiwarg.Recognition.FingerRecognition
         {
             TouchEvent = touchEvent;
         }
+
+        public Contour<Point> getContour(float margin) {
+            return SliceTrail.getContour(margin);
+        }
     }
 }

+ 29 - 22
bbiwarg/Recognition/FingerRecognition/FingerDetector.cs

@@ -37,9 +37,9 @@ namespace bbiwarg.Recognition.FingerRecognition
 
             Fingers = new List<Finger>();
 
-            for (int y = 1; y < maxY; y += 5)
+            for (int y = 1; y < maxY; y += 4)
             {
-                for (int x = 1; x < maxX; x++)
+                for (int x = 1; x < maxX; x += 2)
                 {
                     if (edgeImageAdapted.isEdgeAt(x, y))
                     {
@@ -96,9 +96,7 @@ namespace bbiwarg.Recognition.FingerRecognition
                     if (trail.NumSlices > Parameters.FingerMinNumSlices / 2)
                     {
                         trail.removeFirstSlices(Parameters.FingerRemoveNumSlicesForCorrection);
-                        trail.reverse();
                         trail = expandTrail(trail, true);
-                        trail.reverse();
                         return trail;
                     }
                 }
@@ -110,6 +108,9 @@ namespace bbiwarg.Recognition.FingerRecognition
 
         private FingerSliceTrail expandTrail(FingerSliceTrail trail, bool reversed = false)
         {
+            if (reversed)
+                trail.reverse();
+
             Vector2D currentDirection = trail.getEndDirection();
             Vector2D currentPosition = trail.EndSlice.Mid + Parameters.FingerStepSize * currentDirection;
 
@@ -119,10 +120,9 @@ namespace bbiwarg.Recognition.FingerRecognition
             FingerSlice lastSlice = trail.EndSlice;
             FingerSlice nextSlice;
 
-            while (currentPosition.isInBound() && gapCounter <= Math.Min(numSlices, Parameters.FingerMaxGapCounter))
+            while (currentPosition.isInBound() && gapCounter < Math.Min(numSlices, Parameters.FingerMaxGapCounter))
             {
-                Vector2D direction = (reversed) ? currentDirection.getInverse() : currentDirection;
-                nextSlice = findFingerSliceFromMid(currentPosition, direction);
+                nextSlice = findFingerSliceFromMid(currentPosition, currentDirection, reversed);
 
                 if (nextSlice != null && Math.Abs(nextSlice.Length - lastSlice.Length) <= Parameters.FingerMaxSliceDifferencePerStep)
                 {
@@ -141,20 +141,23 @@ namespace bbiwarg.Recognition.FingerRecognition
                 }
             }
 
+            if (reversed)
+                trail.reverse();
+
             return trail;
         }
 
-        private FingerSlice findFingerSliceFromMid(Vector2D position, Vector2D direction)
+        private FingerSlice findFingerSliceFromMid(Vector2D position, Vector2D direction, bool reversed = false)
         {
             if (edgeImageAdapted.isRoughEdgeAt(position)) return null;
 
-            Vector2D dirStart = direction.getOrthogonal(true);
-            Vector2D dirEnd = direction.getOrthogonal(false);
+            Vector2D dirStart = direction.getOrthogonal(reversed);
+            Vector2D dirEnd = direction.getOrthogonal(!reversed);
 
-            Vector2D start = edgeImageAdapted.findNextEdge(position, dirStart, Parameters.FingerMaxWidth);
+            Vector2D start = edgeImageAdapted.findNextRoughEdge(position, dirStart, Parameters.FingerMaxWidth);
             if (start == null) return null;
 
-            Vector2D end = edgeImageAdapted.findNextEdge(position, dirEnd, Parameters.FingerMaxWidth);
+            Vector2D end = edgeImageAdapted.findNextRoughEdge(position, dirEnd, Parameters.FingerMaxWidth);
             if (end == null) return null;
 
             return getFingerSlice(start, end);
@@ -162,7 +165,7 @@ namespace bbiwarg.Recognition.FingerRecognition
         private FingerSlice findFingerSliceFromStartEdge(Vector2D start, Vector2D direction)
         {
             Vector2D searchStart = start + Parameters.FingerContourMargin * direction;
-            Vector2D end = edgeImageAdapted.findNextEdge(searchStart, direction, Parameters.FingerMaxWidth);
+            Vector2D end = edgeImageAdapted.findNextRoughEdge(searchStart, direction, Parameters.FingerMaxWidth);
             if (end == null)
                 return null;
 
@@ -178,11 +181,11 @@ namespace bbiwarg.Recognition.FingerRecognition
             return null;
         }
 
-        private bool fingerSliceDepthTest(FingerSlice fingerSlice)
+        private bool fingerSliceDepthTest(FingerSlice slice)
         {
-            Int16 depthStart = depthImage.getDepthAt(fingerSlice.ContourStart);
-            Int16 depthMid = depthImage.getDepthAt(fingerSlice.Mid);
-            Int16 depthEnd = depthImage.getDepthAt(fingerSlice.ContourEnd);
+            Int16 depthStart = depthImage.getDepthAt(slice.Start.moveWithinBound(slice.Direction.getInverse(), Parameters.FingerContourMargin));
+            Int16 depthMid = depthImage.getDepthAt(slice.Mid);
+            Int16 depthEnd = depthImage.getDepthAt(slice.End.moveWithinBound(slice.Direction, Parameters.FingerContourMargin));
             return (depthStart > depthMid && depthMid < depthEnd);
         }
 
@@ -199,17 +202,21 @@ namespace bbiwarg.Recognition.FingerRecognition
                 Fingers.Add(finger);
 
             //remove edges around detected finger to improve performance
-            edgeImageAdapted.removeEdgesInsidePolygon(finger.Contour.ToArray());
+            edgeImageAdapted.removeEdgesInsidePolygon(finger.getContour(Parameters.FingerContourMargin).ToArray());
         }
 
         private bool isCrippleFinger(Finger finger)
         {
+            FingerSlice midSlice = finger.SliceTrail.MidSlice;
+            Vector2D out1 = midSlice.Start.moveWithinBound(midSlice.Direction.getInverse(), Parameters.FingerOutMargin);
+            Vector2D out2 = midSlice.End.moveWithinBound(midSlice.Direction, Parameters.FingerOutMargin);
+
             Int16 depthAtFinger = depthImage.getDepthAt(finger.MidPoint);
-            Int16 depthAtOut1 = depthImage.getDepthAt(finger.SliceTrail.MidSlice.OutStart);
-            Int16 depthAtOut2 = depthImage.getDepthAt(finger.SliceTrail.MidSlice.OutEnd);
+            Int16 depthAtOut1 = depthImage.getDepthAt(out1);
+            Int16 depthAtOut2 = depthImage.getDepthAt(out2);
             int minDepthDifference = Math.Min(Math.Abs(depthAtFinger - depthAtOut1), Math.Abs(depthAtFinger - depthAtOut2));
 
-            return (minDepthDifference < Parameters.FingerCrippleMinDifference);
+            return (minDepthDifference < Parameters.FingerMaxCrippleDifference);
         }
 
 
@@ -220,7 +227,7 @@ namespace bbiwarg.Recognition.FingerRecognition
 
             if (startLength > endLength)
                 trail.reverse();
-            
+
             return trail;
         }
     }

+ 2 - 19
bbiwarg/Recognition/FingerRecognition/FingerSlice.cs

@@ -9,17 +9,10 @@ namespace bbiwarg.Recognition.FingerRecognition
 {
     class FingerSlice
     {
-        private LineSegment2D lineSegment;
-        private bool lineSegmentUpToDate;
-
         public Vector2D Mid { get; private set; }
         public Vector2D Start { get; private set; }
         public Vector2D End { get; private set; }
-        public Vector2D ContourStart { get; private set; }
-        public Vector2D ContourEnd { get; private set; }
-        public Vector2D OutStart { get; private set; }
-        public Vector2D OutEnd { get; private set; }
-        public LineSegment2D LineSegment { get { if (!lineSegmentUpToDate) updateLineSegment(); return lineSegment; } }
+        public LineSegment2D LineSegment { get; private set; }
         public Vector2D Direction { get { return LineSegment.Direction; } }
         public float Length { get { return LineSegment.Length; } }
 
@@ -28,17 +21,7 @@ namespace bbiwarg.Recognition.FingerRecognition
             Start = start;
             End = end;
             Mid = (start + end) / 2;
-            updateLineSegment();
-            ContourStart = (Start + Parameters.FingerContourMargin * Direction.getInverse()).moveInBound(Direction);
-            ContourEnd = (End + Parameters.FingerContourMargin * Direction).moveInBound(Direction.getInverse());
-            OutStart = (Start + Parameters.FingerOutMargin * Direction.getInverse()).moveInBound(Direction);
-            OutEnd = (End + Parameters.FingerOutMargin * Direction).moveInBound(Direction.getInverse());
-        }
-
-        private void updateLineSegment()
-        {
-            lineSegment = new LineSegment2D(Start, End);
-            lineSegmentUpToDate = true;
+            LineSegment = new LineSegment2D(Start, End);
         }
 
     }

+ 20 - 81
bbiwarg/Recognition/FingerRecognition/FingerSliceTrail.cs

@@ -12,14 +12,8 @@ namespace bbiwarg.Recognition.FingerRecognition
     class FingerSliceTrail
     {
         private LineSegment2D lineSegment;
-        private Contour<Point> contour;
-        private Contour<Point> innerContour;
-        private Contour<Point> outerContour;
         private Vector2D fittedDirection;
         private bool lineSegmentUpToDate;
-        private bool contourUpToDate;
-        private bool innerContourUpToDate;
-        private bool outerContourUpToDate;
         private bool fittedDirectionUpToDate;
 
         public List<FingerSlice> Slices { get; private set; }
@@ -29,9 +23,6 @@ namespace bbiwarg.Recognition.FingerRecognition
         public FingerSlice this[int index] { get { return Slices[index]; } }
         public int NumSlices { get { return Slices.Count; } }
         public LineSegment2D LineSegment { get { if (!lineSegmentUpToDate) updateLineSegment(); return lineSegment; } }
-        public Contour<Point> Contour { get { if (!contourUpToDate) updateContour(); return contour; } }
-        public Contour<Point> InnerContour { get { if (!innerContourUpToDate) updateInnerContour(); return innerContour; } }
-        public Contour<Point> OuterContour { get { if (!outerContourUpToDate) updateOuterContour(); return outerContour; } }
         public Vector2D FittedDirection { get { if (!fittedDirectionUpToDate) updateFittedDirection(); return fittedDirection; } }
 
         public FingerSliceTrail(FingerSlice slice)
@@ -39,7 +30,6 @@ namespace bbiwarg.Recognition.FingerRecognition
             Slices = new List<FingerSlice>();
             addSlice(slice);
             lineSegmentUpToDate = false;
-            contourUpToDate = false;
             fittedDirectionUpToDate = false;
         }
 
@@ -47,13 +37,12 @@ namespace bbiwarg.Recognition.FingerRecognition
         {
             Slices.Add(slice);
             lineSegmentUpToDate = false;
-            contourUpToDate = false;
             fittedDirectionUpToDate = false;
         }
 
         public Vector2D getStartDirection()
         {
-            int innerStartIndex = Math.Min(NumSlices, Parameters.FingerNumSlicesForRelativeDirection);
+            int innerStartIndex = Math.Min(NumSlices-1, Parameters.FingerNumSlicesForRelativeDirection);
             return (StartSlice.Mid - Slices[innerStartIndex].Mid).normalize();
         }
 
@@ -67,7 +56,6 @@ namespace bbiwarg.Recognition.FingerRecognition
         {
             Slices.RemoveRange(0, numSlices);
             lineSegmentUpToDate = false;
-            contourUpToDate = false;
             fittedDirectionUpToDate = false;
         }
 
@@ -75,101 +63,52 @@ namespace bbiwarg.Recognition.FingerRecognition
         {
             Slices.Reverse();
             lineSegmentUpToDate = false;
-            contourUpToDate = false;
             fittedDirectionUpToDate = false;
         }
 
-        private void updateLineSegment()
-        {
-            lineSegment = new LineSegment2D(EndSlice.Mid, StartSlice.Mid);
-
-            lineSegmentUpToDate = true;
-        }
-
-        private void updateFittedDirection()
-        {
-            List<PointF> midPoints = new List<PointF>();
-            foreach (FingerSlice slice in Slices)
-                midPoints.Add(slice.Mid);
-
-            PointF pointOnLine, direction;
-            PointCollection.Line2DFitting(midPoints.ToArray(), Emgu.CV.CvEnum.DIST_TYPE.CV_DIST_FAIR, out direction, out pointOnLine);
-            fittedDirection = new Vector2D(direction).normalize();
-
-            if (fittedDirection.isInOppositeDirection(LineSegment.Direction))
-                fittedDirection = fittedDirection.getInverse();
-
-            fittedDirectionUpToDate = true;
-        }
-
-        private void updateContour()
-        {
+        public Contour<Point> getContour(float margin) {
             List<Point> pointsA = new List<Point>();
             List<Point> pointsB = new List<Point>();
 
             foreach (FingerSlice slice in Slices)
             {
-                pointsA.Add(slice.ContourStart);
-                pointsB.Add(slice.ContourEnd);
+                Vector2D direction = slice.Direction;
+                pointsA.Add(slice.Start.moveWithinBound(direction.getInverse(), margin));
+                pointsB.Add(slice.End.moveWithinBound(direction, margin));
             }
 
             pointsA.Reverse();
             pointsA.AddRange(pointsB);
 
-            contour = new Contour<Point>(new MemStorage());
+            Contour<Point> contour = new Contour<Point>(new MemStorage());
             foreach (Point p in pointsA)
             {
                 contour.Push(p);
             }
-            contourUpToDate = true;
+            return contour;
         }
 
-        private void updateInnerContour()
+        private void updateLineSegment()
         {
-            List<Point> pointsA = new List<Point>();
-            List<Point> pointsB = new List<Point>();
-
-            foreach (FingerSlice slice in Slices)
-            {
-                pointsA.Add(slice.Start);
-                pointsB.Add(slice.End);
-            }
-
-            pointsA.Reverse();
-            pointsA.AddRange(pointsB);
-
-            innerContour = new Contour<Point>(new MemStorage());
-            foreach (Point p in pointsA)
-            {
-                innerContour.Push(p);
-            }
-            innerContourUpToDate = true;
+            lineSegment = new LineSegment2D(EndSlice.Mid, StartSlice.Mid);
+            lineSegmentUpToDate = true;
         }
 
-        private void updateOuterContour()
+        private void updateFittedDirection()
         {
-            List<Point> pointsA = new List<Point>();
-            List<Point> pointsB = new List<Point>();
-
+            List<PointF> midPoints = new List<PointF>();
             foreach (FingerSlice slice in Slices)
-            {
-                pointsA.Add(slice.OutStart);
-                pointsB.Add(slice.OutEnd);
-            }
-
-            pointsA.Reverse();
-            pointsA.AddRange(pointsB);
-
-            outerContour = new Contour<Point>(new MemStorage());
-            foreach (Point p in pointsA)
-            {
-                outerContour.Push(p);
-            }
-            outerContourUpToDate = true;
-        }
+                midPoints.Add(slice.Mid);
 
+            PointF pointOnLine, direction;
+            PointCollection.Line2DFitting(midPoints.ToArray(), Emgu.CV.CvEnum.DIST_TYPE.CV_DIST_FAIR, out direction, out pointOnLine);
+            fittedDirection = new Vector2D(direction).normalize();
 
+            if (fittedDirection.isInOppositeDirection(LineSegment.Direction))
+                fittedDirection = fittedDirection.getInverse();
 
+            fittedDirectionUpToDate = true;
+        }
 
     }
 }

+ 3 - 3
bbiwarg/Recognition/HandRecognition/Hand.cs

@@ -73,8 +73,8 @@ namespace bbiwarg.Recognition.HandRecognition
                 FingerSliceTrail trail = null;
                 foreach (FingerSlice slice in finger.SliceTrail.Slices)
                 {
-                    Vector2D out1 = slice.OutStart;
-                    Vector2D out2 = slice.OutEnd;
+                    Vector2D out1 = slice.Start.moveWithinBound(slice.Direction.getInverse(), Parameters.FingerOutMargin);
+                    Vector2D out2 = slice.End.moveWithinBound(slice.Direction, Parameters.FingerOutMargin);
 
                     if (isInside(out1) && isInside(out2))
                     {
@@ -86,7 +86,7 @@ namespace bbiwarg.Recognition.HandRecognition
                 }
 
                 if (trail != null)
-                    Mask.FillConvexPoly(trail.OuterContour.ToArray(), new Gray(1));
+                    Mask.FillConvexPoly(trail.getContour(Parameters.FingerOutMargin).ToArray(), new Gray(1));
             }
             Mask = Mask.Dilate(1);
         }

+ 4 - 3
bbiwarg/Recognition/HandRecognition/HandDetector.cs

@@ -47,7 +47,7 @@ namespace bbiwarg.Recognition.HandRecognition
             foreach (Finger finger in fingers)
             {
                 Int16 depthAtHand = depthImage.getDepthAt(finger.HandPoint);
-                Point[] contour = finger.Contour.ToArray();
+                Point[] contour = finger.getContour(Parameters.FingerContourMargin).ToArray();
                 modifiedHandDepthImage.DrawPolyline(contour, false, new Gray(depthAtHand), 1);
             }
         }
@@ -137,8 +137,9 @@ namespace bbiwarg.Recognition.HandRecognition
                     List<Hand> mergeHands = new List<Hand>();
                     foreach (Finger overlappingFinger in otherHandsFingers[hand])
                     {
-                        Vector2D midOut1 = overlappingFinger.SliceTrail.MidSlice.OutStart;
-                        Vector2D midOut2 = overlappingFinger.SliceTrail.MidSlice.OutEnd;
+                        FingerSlice midSlice = overlappingFinger.SliceTrail.MidSlice;
+                        Vector2D midOut1 = midSlice.Start.moveWithinBound(midSlice.Direction.getInverse(), Parameters.FingerOutMargin);
+                        Vector2D midOut2 = midSlice.End.moveWithinBound(midSlice.Direction, Parameters.FingerOutMargin);
                         Int16 depthAtMidOut1 = depthImage.getDepthAt(midOut1);
                         Int16 depthAtMidOut2 = depthImage.getDepthAt(midOut2);
                         bool midOut1InHand = hand.isInside(midOut1);

+ 2 - 3
bbiwarg/Recognition/TouchRecognition/TouchDetector.cs

@@ -38,9 +38,8 @@ namespace bbiwarg.Recognition.TouchRecognition
             {
                 Vector2D tipPoint = finger.TipPoint;
                 Vector2D direction = finger.Direction;
-                Vector2D directionInv = direction.getInverse();
-                Vector2D tipPointInside = (tipPoint + Parameters.TouchTipInsideFactor * directionInv).moveInBound(direction);
-                Vector2D tipPointOutside = (tipPoint + Parameters.TouchTipOutsideFactor * direction).moveInBound(directionInv);
+                Vector2D tipPointInside = tipPoint.moveWithinBound(direction.getInverse(), Parameters.TouchTipInsideFactor);
+                Vector2D tipPointOutside = tipPoint.moveWithinBound(direction, Parameters.TouchTipOutsideFactor);
 
                 foreach (Palm palm in palms)
                 {

+ 0 - 1
bbiwarg/Utility/Timer.cs

@@ -49,7 +49,6 @@ namespace bbiwarg.Utility
 
         public static void outputAll()
         {
-            Logger.clear(LogSubject.Timer);
             Logger.log("---TIMERS-START---", LogSubject.Timer);
             foreach (String name in stopwatches.Keys)
             {

+ 13 - 7
bbiwarg/Utility/Vector2D.cs

@@ -14,7 +14,8 @@ namespace bbiwarg.Utility
         public float Y { get; private set; }
         public int IntX { get { return (int)X; } }
         public int IntY { get { return (int)Y; } }
-        public float Length { get { return (float)Math.Sqrt(X * X + Y * Y); } }
+        public float Length { get { counter++;  return (float)Math.Sqrt(X * X + Y * Y); } }
+        public static int counter = 0;
 
         public Vector2D(float x, float y)
         {
@@ -96,12 +97,17 @@ namespace bbiwarg.Utility
 
         public Vector2D moveInBound(Vector2D topLeft, Vector2D bottomRight, Vector2D inBoundDirection)
         {
-            Vector2D pos = new Vector2D(X, Y);
-            while (!pos.isInBound(topLeft, bottomRight))
-            {
-                pos += inBoundDirection;
-            }
-            return pos;
+            Vector2D position = new Vector2D(X, Y);
+            while (!position.isInBound(topLeft, bottomRight))
+                position += inBoundDirection;
+            return position;
+        }
+
+        public Vector2D moveWithinBound(Vector2D direction, float factor) {
+            Vector2D newPosition = this + factor * direction;
+            if (!newPosition.isInBound())
+                newPosition = newPosition.moveInBound(direction.getInverse());
+            return newPosition;
         }
 
         public Vector2D normalize()