コード例 #1
0
        /// <summary>
        /// This is a high-level function to cuts fixtures inside the given world, using the start and end points.
        /// Note: We don't support cutting when the start or end is inside a shape.
        /// </summary>
        /// <param name="world">The world.</param>
        /// <param name="start">The startpoint.</param>
        /// <param name="end">The endpoint.</param>
        /// <param name="thickness">The thickness of the cut</param>
        public static Vector2 Cut(World world, Vector2 start, Vector2 end, float thickness, Category collisionCategories = Category.None)
        {

            // The left side of the cut will remain part of the existing body;
            // the right side will be made into a new body

            List<Fixture> fixtures = new List<Fixture>();
            List<Vector2> entryPoints = new List<Vector2>();
            List<Vector2> exitPoints = new List<Vector2>();



            List<RayCastResult> results = new List<RayCastResult>();
            //float blockingFraction = float.MaxValue;
            Vector2 stoppingPoint = end;

            //We don't support cutting when the start or end is inside a shape.
            //if (world.TestPoint(start) != null || world.TestPoint(end) != null)
            //    return;

            //Get the entry points
            world.RayCast((f, p, n, fr) =>
                              {
                                  RayCastResult r = new RayCastResult();
                                  r.f = f;
                                  r.p = p;
                                  r.fr = fr;
                                  results.Add(r);

                                  return 1;
                                 
                              }, start, end);


            results = results.OrderBy(p => p.fr).ToList();

            foreach (RayCastResult r in results)
            {
                if ((r.f.CollisionCategories & collisionCategories) != Category.None)
                {
                    stoppingPoint = r.p;
                    break;
                }
                if (!r.f.TestPoint(ref end))
                {
                    if (world.FixtureCut != null)
                        world.FixtureCut(r.f);
                    fixtures.Add(r.f);
                    entryPoints.Add(r.p);
                }
            }


            //Reverse the ray to get the exitpoints
            world.RayCast((f, p, n, fr) =>
                              {
                                  if (fixtures.Contains(f))
                                  {
                                      exitPoints.Add(p);
                                  }
                                  return 1;
                              }, end, start);

            Debug.Assert(entryPoints.Count == exitPoints.Count && entryPoints.Count == fixtures.Count);

            //Fixture containsEnd = world.TestPoint(end);
            //if (containsEnd != null)
            //{
            //    entryPoints.RemoveAt(0);
            //    fixtures.Remove(containsEnd);
            //}
            //Fixture containsStart = world.TestPoint(start);
            //if (containsStart != null)
            //{
            //    exitPoints.RemoveAt(exitPoints.Count - 1);
            //    fixtures.Remove(containsStart);
            //}

            //We only have a single point. We need at least 2
            if (entryPoints.Count + exitPoints.Count < 2)
                return stoppingPoint;

            var query =
                (from fix in fixtures
                select fix.Body).Distinct();

            foreach (Body b in query) 
            {

                if (b == null || b.BodyType == BodyType.Static)
                    continue;

                ContactEdge edge = b.ContactList;
                while (edge != null)
                {
                    Contact c = edge.Contact;
                    edge = edge.Next;
                    world.ContactManager.Destroy(c);
                }

                List<Body> leftBodies = new List<Body>();
                List<Body> rightBodies = new List<Body>();
                //Body rightBody = new Body(world);

                List<Joint> leftJoints = new List<Joint>();
                List<Joint> rightJoints = new List<Joint>();

                foreach (Joint j in b.JointList)
                {
                    if (isLeft(start, end, j.WorldAnchorA))
                        leftJoints.Add(j);
                    else
                        rightJoints.Add(j);
                }

                //List<Fixture> leftList = new List<Fixture>();
                //List<Fixture> rightList = new List<Fixture>();
                Fixture[] bodyFixtures = new Fixture[b.FixtureList.Count];
                b.FixtureList.CopyTo(bodyFixtures);
                b.FixtureList.Clear();
                //leftBodies.Add(b);

                // For each fixture that was sliced through...
                foreach (Fixture fix in (from f in bodyFixtures where fixtures.Contains(f) select f))
                {
                    
                    
                    int i = fixtures.IndexOf(fix);

                    // split this in half and put the halves in the over/under lists
                    Vertices first;
                    Vertices second;
                    SplitShape(fix, entryPoints[i], exitPoints[i], thickness, out first, out second);
                    if (!SanityCheck(first) || !SanityCheck(second))
                    {
                        continue;
                    }
                    PolygonShape leftShape = new PolygonShape(first, fix.Shape.Density);
                    PolygonShape rightShape = new PolygonShape(second, fix.Shape.Density);

                    if (!b.FixtureList.Any())
                    {
                        if (leftShape.MassData.Area > rightShape.MassData.Area)
                        {
                            b.CreateFixture(leftShape, fix.UserData);
                            leftBodies.Add(b);
                            GlomFixture(world, b, rightBodies, rightShape, fix.UserData, rightJoints);
                        }
                        else
                        {
                            b.CreateFixture(rightShape, fix.UserData);
                            rightBodies.Add(b);
                            GlomFixture(world, b, leftBodies, leftShape, fix.UserData, leftJoints);
                        }
                    }
                    else
                    {
                        GlomFixture(world, b, leftBodies, leftShape, fix.UserData, leftJoints);
                        GlomFixture(world, b, rightBodies, rightShape, fix.UserData, rightJoints);
                    }

                                      
                }

                // for each fixture that was NOT sliced through...
                foreach (Fixture fix in (from f in bodyFixtures where !fixtures.Contains(f) select f)) {

                    if (isLeft(start, end, fix))
                    {
                        GlomFixture(world, b, leftBodies, fix.Shape, fix.UserData, leftJoints);
                    }
                    else
                    {
                        GlomFixture(world, b, rightBodies, fix.Shape, fix.UserData, rightJoints);
                        //rightBody.CreateFixture(fix.Shape.Clone(), fix.UserData);
                    }

                    
                }

                foreach (Body bod in leftBodies.Concat(rightBodies))
                {
                    bod.ResetMassData();
                    bod.BodyType = BodyType.Dynamic;
                    bod.Rotation = b.Rotation;
                    bod.LinearVelocity = b.LinearVelocity;
                    bod.AngularVelocity = b.AngularVelocity;
                    bod.Position = b.Position;
                }

                //b.JointList = null;
                //world.RemoveBody(b);

                foreach (Fixture f in bodyFixtures)
                {
                    b.DestroyFixture(f);
                }
                world.ProcessChanges();
            }

            return stoppingPoint;

        }