public override void Create() { if (!Scene.NextStage("Create SplitByRayNode")) { Shapes.AddRange(Children.SelectMany(a => a.Shapes).Select(a => a.Copy())); // if not doing this stage, just copy over the none split convexes return; } else if (Scene.IsCurrentStage()) { Scene.AddDebugLine(RayStart - RayDir * 10, RayStart + RayDir * 10); } Shapes.AddRange(Children.SelectMany(a => a.Shapes).Select(a => a.Copy().Split2d(RayStart, RayDir, SplitMode))); //do full copy then split }
public void SplitByRay(Point3D raystart, Vector3D raydir, out Face inside_face, out Face outside_face) { //default to just returning this as inside and outside inside_face = this; outside_face = null; raydir.Normalize(); if (!Scene.NextStage("SplitByRay")) { return; } else if (Scene.IsCurrentStage()) { Scene.AddDebugLine(raystart - raydir * 10, raystart + raydir * 10); } //get the edges, feature info and params that describe how the ray intersects the face Edge edge0, edge1; double param0, param1; MathUtils.RayLineResult res0, res1; GetRaySplitParams(raystart, raydir, out edge0, out param0, out res0, out edge1, out param1, out res1); //debug draw the results if (Scene.IsCurrentStage()) { if (res0 == MathUtils.RayLineResult.INTERSECTING_LINE) { Scene.AddDebugLine(edge0.Vertices[0].Pos, edge0.Vertices[1].Pos); } else if (res0 == MathUtils.RayLineResult.INTERSECTING_POS0) { Scene.AddDebugCross(edge0.Vertices[0].Pos, 0.5); } else if (res0 == MathUtils.RayLineResult.PARALLEL_OVERLAPPING) { Scene.AddDebugCross(edge0.Vertices[0].Pos, 0.5); Scene.AddDebugCross(edge0.Vertices[1].Pos, 0.5); } if (res1 == MathUtils.RayLineResult.INTERSECTING_LINE) { Scene.AddDebugLine(edge1.Vertices[0].Pos, edge1.Vertices[1].Pos); } else if (res1 == MathUtils.RayLineResult.INTERSECTING_POS0) { Scene.AddDebugCross(edge1.Vertices[0].Pos, 0.5); } else if (res1 == MathUtils.RayLineResult.PARALLEL_OVERLAPPING) { Scene.AddDebugCross(edge1.Vertices[1].Pos, 0.5); Scene.AddDebugCross(edge1.Vertices[1].Pos, 0.5); } } //check if centre is initially inside ray Vector3D centre_offset = Centre - raystart; double centre_cp = MathUtils.CrossXY(centre_offset, raydir); //valid results for closed, convex, clockwise polygon are: // res0 == no intersection res1 == no intersection //when res0 is parallel: // res0 == parallel overlapping res1 == intersecting pos0 of next edge (this is treated as no intersection as there is no split required) // res0 == parallel overlapping res1 == parallel overlapping next edge IF next edge is colinear //when res0 is vertex cases // res0 == intersecting pos0 of first edge res1 == parallel overlapping last edge (note: in any other scenario res0 would detect the parallel line, and res1 will be the vertex) // res0 == intersecting pos0 of any edge res1 == no intersection (i.e. we just touched 1 vertex) // res0 == intersecting pos0 of any edge res1 == intersecting pos0 of any none-neighour edge // res0 == intersecting pos0 of any edge res1 == intersecting line of any edge other than previous neighbour //when res0 is line cases // res0 == intersecting line of any edge res1 == intersecting pos0 of any edge other than next neighbour // res0 == intersecting line of any edge res1 == intersecting line of any edge //check intersection results for each valid combination of res0 and res1 if (res0 == MathUtils.RayLineResult.PARALLEL_OVERLAPPING) { //first edge is both parallel and overlapping the ray, so just return this face as left or right if (edge1 != NextEdge(edge0)) { throw new System.ApplicationException("If res0 is parallel, expected res1 to be the next edge"); } if (res1 == MathUtils.RayLineResult.PARALLEL_OVERLAPPING) { if (Math.Abs(1 - Vector3D.DotProduct(edge0.Direction, edge1.Direction)) > MathUtils.EPSILON) { throw new System.ApplicationException("If res0 is parallel and res1 is parallel, res1 must be colinear"); } //pick side and return if (centre_cp <= 0) { inside_face = this; } } else if (res1 == MathUtils.RayLineResult.INTERSECTING_POS0) { //pick side and return if (centre_cp <= 0) { inside_face = this; } } else { throw new System.ApplicationException("If res0 is parallel, expected res1 must be overlapping (if colinear) or pos0"); } } else if (res0 == MathUtils.RayLineResult.INTERSECTING_POS0) { //first edge overlaps its starting vertex if (res1 == MathUtils.RayLineResult.UNKOWN) { //no intersection (we just clipped the first vertex of edge0) - still pick side and return? if (centre_cp <= 0) { inside_face = this; } } else if (res1 == MathUtils.RayLineResult.INTERSECTING_POS0) { if (edge1 == NextEdge(edge0) || edge1 == PrevEdge(edge0)) { throw new System.ApplicationException("If res0 is pos0 and res1 is pos0, edge1 must not be neighbour of edge0"); } //got intersection - need to do vertex-vertex split Face newface = Split(edge0, edge1); Vector3D new_centre_offset = Centre - raystart; double new_centre_cp = MathUtils.CrossXY(new_centre_offset, raydir); if (new_centre_cp <= 0) { inside_face = this; outside_face = newface; } else { outside_face = this; inside_face = newface; } } else if (res1 == MathUtils.RayLineResult.INTERSECTING_LINE) { if (edge1 == PrevEdge(edge0)) { throw new System.ApplicationException("If res0 is pos0 and res1 is line, edge1 must not be neighbour of edge0"); } //got intersection - need to do vertex-edge split Face newface = Split(edge0, edge1, param1); Vector3D new_centre_offset = Centre - raystart; double new_centre_cp = MathUtils.CrossXY(new_centre_offset, raydir); if (new_centre_cp <= 0) { inside_face = this; outside_face = newface; } else { outside_face = this; inside_face = newface; } } else if (res1 == MathUtils.RayLineResult.PARALLEL_OVERLAPPING) { if (edge1 != Edges.Last()) { throw new System.ApplicationException("If res1 is parallel, expected it to be the last edge"); } if (edge0 != Edges.First()) { throw new System.ApplicationException("If res1 is parallel, expected res0 to be the first edge"); } //pick side and return if (centre_cp <= 0) { inside_face = this; } } else { throw new System.ApplicationException("If res0 is pos0, res1 must be none, pos0 or line"); } } else if (res0 == MathUtils.RayLineResult.INTERSECTING_LINE) { //first edge is overlaps the line if (res1 == MathUtils.RayLineResult.INTERSECTING_POS0) { if (edge1 == NextEdge(edge0)) { throw new System.ApplicationException("If res0 is line, edge1 must not be next neighbour of edge0"); } //got intersection - need to do edge-vertex split Face newface = Split(edge0, param0, edge1); Vector3D new_centre_offset = Centre - raystart; double new_centre_cp = MathUtils.CrossXY(new_centre_offset, raydir); if (new_centre_cp <= 0) { inside_face = this; outside_face = newface; } else { outside_face = this; inside_face = newface; } } else if (res1 == MathUtils.RayLineResult.INTERSECTING_LINE) { //got intersection - need to do edge-edge split Face newface = Split(edge0, param0, edge1, param1); Vector3D new_centre_offset = Centre - raystart; double new_centre_cp = MathUtils.CrossXY(new_centre_offset, raydir); if (new_centre_cp <= 0) { inside_face = this; outside_face = newface; } else { outside_face = this; inside_face = newface; } } else { throw new System.ApplicationException("If res0 is line, res1 must be pos or line"); } } else { if (res1 != MathUtils.RayLineResult.UNKOWN) { throw new System.ApplicationException("If res0 is no intersection, res1 should also be no intersection"); } //no intersection at all - still pick side and return? if (centre_cp <= 0) { inside_face = this; } } }