public static Solid CreateFromIntersectingPlanes(IEnumerable<Plane> planes, IDGenerator generator) { var solid = new Solid(generator.GetNextObjectID()); var list = planes.ToList(); for (var i = 0; i < list.Count; i++) { // Split the polygon by all the other planes var poly = new Polygon(list[i]); for (var j = 0; j < list.Count; j++) { if (i != j) poly.Split(list[j]); } // The final polygon is the face var face = new Face(generator.GetNextFaceID()) { Plane = poly.Plane , Parent = solid }; face.Vertices.AddRange(poly.Vertices.Select(x => new Vertex(x, face))); face.UpdateBoundingBox(); face.AlignTextureToWorld(); solid.Faces.Add(face); } // Ensure all the faces point outwards var origin = solid.GetOrigin(); foreach (var face in solid.Faces) { if (face.Plane.OnPlane(origin) >= 0) face.Flip(); } solid.UpdateBoundingBox(); return solid; }
public void BenchmarkSolidConstruction() { var idg = new IDGenerator(); var box = new Box(Coordinate.One * -100, Coordinate.One * 100); var planes = new CylinderBrush().Create(idg, box, null, 2).OfType<Solid>().SelectMany(x => x.Faces).Select(x => x.Plane).ToList(); var stopwatch = new Stopwatch(); stopwatch.Start(); for (var b = 0; b < 1000; b++) { Solid.CreateFromIntersectingPlanes(planes, idg); } stopwatch.Stop(); Debug.WriteLine(stopwatch.Elapsed); stopwatch.Restart(); for (var b = 0; b < 1000; b++) { var polys = new List<Polygon>(); for (var i = 0; i < planes.Count; i++) { var poly = new Polygon(planes[i]); for (var j = 0; j < planes.Count; j++) { if (i != j) poly.Split(planes[j]); } polys.Add(poly); } var solid = new Solid(idg.GetNextObjectID()); foreach (var polygon in polys) { var face = new Face(idg.GetNextFaceID()) {Plane = polygon.Plane}; face.Vertices.AddRange(polygon.Vertices.Select(x => new Vertex(x, face))); face.UpdateBoundingBox(); face.AlignTextureToWorld(); solid.Faces.Add(face); } solid.UpdateBoundingBox(); } stopwatch.Stop(); Debug.WriteLine(stopwatch.Elapsed); }
public void Unclone(Polygon polygon) { Vertices = new List<Coordinate>(polygon.Vertices); Plane = polygon.Plane.Clone(); }
/// <summary> /// Splits this polygon by a clipping plane, returning the back and front planes. /// The original polygon is not modified. /// </summary> /// <param name="clip">The clipping plane</param> /// <param name="back">The back polygon</param> /// <param name="front">The front polygon</param> /// <returns>True if the split was successful</returns> public bool Split(Plane clip, out Polygon back, out Polygon front) { // If the polygon doesn't span the plane, return false. var classify = ClassifyAgainstPlane(clip); if (classify != PlaneClassification.Spanning) { back = front = null; if (classify == PlaneClassification.Back) back = this; else if (classify == PlaneClassification.Front) front = this; return false; } // Get the new front and back vertices var backVerts = new List<Coordinate>(); var frontVerts = new List<Coordinate>(); var prev = 0; for (var i = 0; i <= Vertices.Count; i++) { var end = Vertices[i % Vertices.Count]; var cls = clip.OnPlane(end); // Check plane crossing if (i > 0 && cls != 0 && prev != 0 && prev != cls) { // This line end point has crossed the plane // Add the line intersect to the var start = Vertices[i - 1]; var line = new Line(start, end); var isect = clip.GetIntersectionPoint(line, true); if (isect == null) throw new Exception("Expected intersection, got null."); frontVerts.Add(isect); backVerts.Add(isect); } // Add original points if (i < Vertices.Count) { // OnPlane points get put in both polygons, doesn't generate split if (cls >= 0) frontVerts.Add(end); if (cls <= 0) backVerts.Add(end); } prev = cls; } back = new Polygon(backVerts); front = new Polygon(frontVerts); return true; }
private IEnumerable<Solid> CreateSolids(DataStructures.MapObjects.Map map, List<Coordinate> points, IEnumerable<ObjFace> objFaces) { var faces = objFaces.Select(x => CreateFace(map, points, x)).ToList(); // See if the solid is valid var solid = new Solid(map.IDGenerator.GetNextObjectID()); solid.Colour = Colour.GetRandomBrushColour(); solid.Faces.AddRange(faces); faces.ForEach(x => x.Parent = solid); if (solid.IsValid()) { // Do an additional check to ensure that all edges are shared var edges = solid.Faces.SelectMany(x => x.GetEdges()).ToList(); if (edges.All(x => edges.Count(y => x.EquivalentTo(y)) == 2)) { // Valid! let's get out of here! yield return solid; yield break; } } // Not a valid solid, decompose into tetrahedrons/etc foreach (var face in faces) { var polygon = new Polygon(face.Vertices.Select(x => x.Location)); if (!polygon.IsValid() || !polygon.IsConvex()) { // tetrahedrons foreach (var triangle in face.GetTriangles()) { var tf = new Face(map.IDGenerator.GetNextFaceID()); tf.Plane = new Plane(triangle[0].Location, triangle[1].Location, triangle[2].Location); tf.Vertices.AddRange(triangle.Select(x => new Vertex(x.Location, tf))); tf.UpdateBoundingBox(); yield return SolidifyFace(map, tf); } } else { // cone/pyramid/whatever yield return SolidifyFace(map, face); } } }
private MapObject GetBrush(Polygon bounds, decimal depth, IDGenerator idGenerator) { return null; }
private void CreateBrush(Polygon poly, decimal depth) { var brush = GetBrush(poly, depth, Document.Map.IDGenerator); if (brush == null) return; IAction action = new Create(Document.Map.WorldSpawn.ID, brush); if (Select.SelectCreatedBrush) { brush.IsSelected = true; if (Select.DeselectOthersWhenSelectingCreation) { action = new ActionCollection(new ChangeSelection(new MapObject[0], Document.Selection.GetSelectedObjects()), action); } } Document.PerformAction("Create " + BrushManager.CurrentBrush.Name.ToLower(), action); }
public override void ToolSelected(bool preventHistory) { _state = SketchState.None; _currentFace = _cloneFace = null; _intersection = null; _base = null; _depth = 0; _volumePlane = null; }
public override void MouseDown(ViewportBase viewport, ViewportEvent e) { // switch (_state) { case SketchState.None: // nothin break; case SketchState.Ready: if (e.Button != MouseButtons.Left) break; _base = new Polygon(_currentFace.Plane, 1); _base.Transform(new UnitTranslate(_intersection - _base.Vertices[0])); _state = SketchState.DrawingBase; break; case SketchState.DrawingBase: if (e.Button == MouseButtons.Right) { // Cancel _state = SketchState.None; _base = null; } else if (e.Button == MouseButtons.Left) { ExpandBase(_intersection); _volumePlane = new Plane(_base.Vertices[1], _base.Vertices[2], _base.Vertices[2] + _base.Plane.Normal); _state = SketchState.DrawingVolume; } break; case SketchState.DrawingVolume: if (e.Button == MouseButtons.Right) { _state = SketchState.DrawingBase; _volumePlane = null; } else if (e.Button == MouseButtons.Left) { var diff = _intersection - _base.Vertices[2]; var sign = _base.Plane.OnPlane(_intersection) < 0 ? -1 : 1; _depth = diff.VectorMagnitude() * sign; CreateBrush(_base, _depth); _base = null; _volumePlane = null; _state = SketchState.None; } break; default: throw new ArgumentOutOfRangeException(); } }
/// <summary> /// Splits this polygon by a clipping plane, returning the back and front planes. /// The original polygon is not modified. /// </summary> /// <param name="clip">The clipping plane</param> /// <param name="back">The back polygon</param> /// <param name="front">The front polygon</param> /// <returns>True if the split was successful</returns> public bool Split(Plane clip, out Polygon back, out Polygon front) { Polygon cFront, cBack; return Split(clip, out back, out front, out cBack, out cFront); }
private void Split(object sender) { var face = GetSplitFace(); if (face == null) return; var solid = face.Parent; var sel = MainTool.Points.Where(x => x.IsSelected).ToList(); var p1 = sel[0]; var p2 = sel[1]; if (p1.IsMidPoint) AddAdjacentPoint(face, p1); if (p2.IsMidPoint) AddAdjacentPoint(face, p2); var polygon = new Polygon(face.Vertices.Select(x => x.Location)); var clip = new Plane(p1.Coordinate, p2.Coordinate, p1.Coordinate + face.Plane.Normal * 10); Polygon back, front; polygon.Split(clip, out back, out front); if (back == null || front == null) return; solid.Faces.Remove(face); face.Parent = null; CreateFace(back, solid, face); CreateFace(front, solid, face); solid.UpdateBoundingBox(); MainTool.SetDirty(true, true); }
private void CreateFace(Polygon polygon, Solid parent, Face original) { var verts = polygon.Vertices; var f = new Face(Document.Map.IDGenerator.GetNextFaceID()) { Parent = parent, Plane = new Plane(verts[0], verts[1], verts[2]), Colour = parent.Colour, Texture = original.Texture.Clone() }; f.Vertices.AddRange(verts.Select(x => new Vertex(x, f))); f.UpdateBoundingBox(); f.CalculateTextureCoordinates(true); parent.Faces.Add(f); }
private void Render3D(Viewport3D vp) { if (_state == ClipState.None || _clipPlanePoint1 == null || _clipPlanePoint2 == null || _clipPlanePoint3 == null || Document.Selection.IsEmpty()) return; // Nothing to draw at this point TextureHelper.Unbind(); // Draw points if (!_clipPlanePoint1.EquivalentTo(_clipPlanePoint2) && !_clipPlanePoint2.EquivalentTo(_clipPlanePoint3) && !_clipPlanePoint1.EquivalentTo(_clipPlanePoint3)) { var plane = new Plane(_clipPlanePoint1, _clipPlanePoint2, _clipPlanePoint3); // Draw clipped solids GL.Enable(EnableCap.LineSmooth); GL.Hint(HintTarget.LineSmoothHint, HintMode.Nicest); var faces = new List<Face>(); var idg = new IDGenerator(); foreach (var solid in Document.Selection.GetSelectedObjects().OfType<Solid>().ToList()) { Solid back, front; if (solid.Split(plane, out back, out front, idg)) { if (_side != ClipSide.Front) faces.AddRange(back.Faces); if (_side != ClipSide.Back) faces.AddRange(front.Faces); } } GL.LineWidth(2); GL.Color3(Color.White); Rendering.Immediate.MapObjectRenderer.DrawWireframe(faces, true, false); GL.LineWidth(1); GL.Hint(HintTarget.LineSmoothHint, HintMode.Fastest); GL.Disable(EnableCap.LineSmooth); // Draw the clipping plane var poly = new Polygon(plane); var bbox = Document.Selection.GetSelectionBoundingBox(); var point = bbox.Center; foreach (var boxPlane in bbox.GetBoxPlanes()) { var proj = boxPlane.Project(point); var dist = (point - proj).VectorMagnitude() * 0.1m; poly.Split(new Plane(boxPlane.Normal, proj + boxPlane.Normal * Math.Max(dist, 100))); } GL.Disable(EnableCap.CullFace); GL.Begin(PrimitiveType.Polygon); GL.Color4(Color.FromArgb(100, Color.Turquoise)); foreach (var c in poly.Vertices) GL.Vertex3(c.DX, c.DY, c.DZ); GL.End(); GL.Enable(EnableCap.CullFace); } }
public void TestPolygonSplitting() { var planes = new[] { new Plane(new Coordinate(-64, 64, 64), new Coordinate(64, 64, 64), new Coordinate(64, -64, 64)), new Plane(new Coordinate(-64, -64, -64), new Coordinate(64, -64, -64), new Coordinate(64, 64, -64)), new Plane(new Coordinate(-64, 64, 64), new Coordinate(-64, -64, 64), new Coordinate(-64, -64, -64)), new Plane(new Coordinate(64, 64, -64), new Coordinate(64, -64, -64), new Coordinate(64, -64, 64)), new Plane(new Coordinate(64, 64, 64), new Coordinate(-64, 64, 64), new Coordinate(-64, 64, -64)), new Plane(new Coordinate(64, -64, -64), new Coordinate(-64, -64, -64), new Coordinate(-64, -64, 64)) }.ToList(); var polys = new List<Polygon>(); for (var i = 0; i < planes.Count; i++) { var poly = new Polygon(planes[i]); for (var j = 0; j < planes.Count; j++) { if (i != j) poly.Split(planes[j]); } polys.Add(poly); } Assert.AreEqual(6, polys.Count); }