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; } } }
public override void Create() { /*foreach (Node n in Children) * { * Shapes.AddRange(n.Shapes.Select(a => a.Copy())); * } * return;*/ if (Is3d) { } else { Shapes.AddRange(Children[0].Shapes.Select(a => a.Copy())); Shapes[0].Convexes[0].Faces[0].Split(Shapes[0].Convexes[0].Edges[1], 0.3, Shapes[0].Convexes[0].Edges[3], 0.25); Shapes[0].Convexes[0].Faces[0].IntegrityCheck(); Shapes[0].Convexes[0].Faces[1].IntegrityCheck(); double closestdist, closestu, closestv; Point3D raystart = new Point3D(0, 0, 0); Vector3D raydir = new Vector3D(5, 1, 0); MathUtils.RayLineResult res = MathUtils.ClosestPointRayLine(out closestdist, out closestu, out closestv, raystart, raydir, Shapes[0].Convexes[0].Edges[1].Vertices[0].Pos, Shapes[0].Convexes[0].Edges[1].Vertices[1].Pos); Scene.AddDebugLine(raystart - raydir * 10, raystart + raydir * 10); Scene.AddDebugCross(raystart + raydir * closestu, 0.5); Scene.AddDebugCross(Shapes[0].Convexes[0].Edges[1].Vertices[0].Pos + Shapes[0].Convexes[0].Edges[1].Offset * closestv, 0.5); //this'll be the clever algorithm. it takes advantage of the fact that we always maintain: //- for any given node, none of it's child convexes overlap //therefore: //- no node should test it's own convexes against each other /* * * //this is the simplest algorithm - we assume every convex could potentially overlap every other convex * //and keep iterating until no more splits occur. it works, but involves a lot of unnecessary overlap tests * Convexes = new List<Convex>(); * foreach (Node n in Children) * Convexes.AddRange(n.Convexes.Select(a => a.Copy())); * * //draw all initial convexes if this is the current stage * if (!CSGScene.NextStage("Begin union")) * { * foreach (Convex c in Convexes) * c.DebugDraw(); * return; * } * * //now do the iterative splitting * //loop until no splits done * bool done_split = true; * while (done_split) * { * //spin over every convex * done_split = false; * for (int i = 0; i < Convexes.Count; i++) * { * //go over every other convex * for (int j = i + 1; j < Convexes.Count; j++) * { * //get the 2 convexes to compare * Convex acvx = Convexes[i]; * Convex bcvx = Convexes[j]; * * //do a clip test * List<Convex> otherconvexsplit = new List<Convex>(); * Convex overlap = null; * if (Convex.CalculateClippedConvexes2d(acvx, bcvx, otherconvexsplit, ref overlap)) * { * //got a split, so remove the convex that was split (cvx a), and re-add the sections * //that didn't overlap * Convexes.RemoveAt(i); * Convexes.AddRange(otherconvexsplit); * done_split = true; * * //if last stage, draw the convex we were splitting and then bail * if (!CSGScene.NextStage("Done a split")) * { * return; * } * break; * } * } * * //break out (so we iterate round again) if a split happened * if (done_split) * break; * } * }*/ } }