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 FaceLineIntersectionTest() { var plane = new Plane(new Coordinate(0, 0, 1), 100); var face = new Face(1) {Plane = plane}; var coords = new[] { new Coordinate(-100, -100, 100), new Coordinate(100, -100, 100), new Coordinate(100, 100, 100), new Coordinate(-100, 100, 100) }; face.Vertices.AddRange(coords.Select(x => new Vertex(x, face))); face.CalculateTextureCoordinates(true); var passLine = new Line(new Coordinate(0, 0, 0), new Coordinate(0, 0, 200)); var reversePassLine = passLine.Reverse(); var failSegment = new Line(new Coordinate(0, 0, 0), new Coordinate(0, 0, 50)); var failLine = new Line(new Coordinate(0, 0, 0), new Coordinate(1, 0, 0)); var outsideFaceLine = new Line(new Coordinate(200, 0, 0), new Coordinate(200, 0, 200)); var pass1 = face.GetIntersectionPoint(passLine); var fail1 = face.GetIntersectionPoint(reversePassLine); var fail2 = face.GetIntersectionPoint(failSegment); var fail3 = face.GetIntersectionPoint(failLine); var fail4 = face.GetIntersectionPoint(outsideFaceLine); Assert.IsNotNull(pass1); Assert.IsNull(fail1); Assert.IsNull(fail2); Assert.IsNull(fail3); Assert.IsNull(fail4); }
public IEnumerable<MapObject> Create(IDGenerator generator, Box box, ITexture texture, int roundDecimals) { var solid = new Solid(generator.GetNextObjectID()) { Colour = Colour.GetRandomBrushColour() }; // The higher Z plane will be triangle, with the lower X value getting the two corners var c1 = new Coordinate(box.Start.X, box.Start.Y, box.End.Z).Round(roundDecimals); var c2 = new Coordinate(box.End.X, box.Start.Y, box.End.Z).Round(roundDecimals); var c3 = new Coordinate(box.Center.X, box.End.Y, box.End.Z).Round(roundDecimals); var c4 = new Coordinate(box.Center.X, box.Center.Y, box.Start.Z).Round(roundDecimals); var faces = new[] { new[] { c3, c2, c1 }, new[] { c3, c1, c4 }, new[] { c2, c3, c4 }, new[] { c1, c2, c4 } }; foreach (var arr in faces) { var face = new Face(generator.GetNextFaceID()) { Parent = solid, Plane = new Plane(arr[0], arr[1], arr[2]), Colour = solid.Colour, Texture = { Texture = texture } }; face.Vertices.AddRange(arr.Select(x => new Vertex(x, face))); face.UpdateBoundingBox(); face.AlignTextureToFace(); solid.Faces.Add(face); } solid.UpdateBoundingBox(); yield return solid; }
public IEnumerable<MapObject> Create(IDGenerator generator, Box box, ITexture texture) { var solid = new Solid(generator.GetNextObjectID()) { Colour = Colour.GetRandomBrushColour() }; // The lower Z plane will be base, the x planes will be triangles var c1 = new Coordinate(box.Start.X, box.Start.Y, box.Start.Z); var c2 = new Coordinate(box.End.X, box.Start.Y, box.Start.Z); var c3 = new Coordinate(box.End.X, box.End.Y, box.Start.Z); var c4 = new Coordinate(box.Start.X, box.End.Y, box.Start.Z); var c5 = new Coordinate(box.Center.X, box.Start.Y, box.End.Z); var c6 = new Coordinate(box.Center.X, box.End.Y, box.End.Z); var faces = new[] { new[] { c1, c2, c3, c4 }, new[] { c2, c1, c5 }, new[] { c5, c6, c3, c2 }, new[] { c4, c3, c6 }, new[] { c6, c5, c1, c4 } }; foreach (var arr in faces) { var face = new Face(generator.GetNextFaceID()) { Parent = solid, Plane = new Plane(arr[0], arr[1], arr[2]), Colour = solid.Colour, Texture = { Texture = texture } }; face.Vertices.AddRange(arr.Select(x => new Vertex(x, face))); face.UpdateBoundingBox(); face.AlignTextureToFace(); solid.Faces.Add(face); } solid.UpdateBoundingBox(); yield return solid; }
public IEnumerable<MapObject> Create(IDGenerator generator, Box box, ITexture texture) { var numsides = (int) _numSides.GetValue(); if (numsides < 3) yield break; // This is all very similar to the cylinder brush. var width = box.Width; var length = box.Length; var major = width / 2; var minor = length / 2; var angle = 2 * DMath.PI / numsides; var points = new Coordinate[numsides]; for (var i = 0; i < numsides; i++) { var a = i * angle; var xval = box.Center.X + major * DMath.Cos(a); var yval = box.Center.Y + minor * DMath.Sin(a); var zval = box.Start.Z; points[i] = new Coordinate(xval, yval, zval).Round(0); } var faces = new List<Coordinate[]>(); var point = new Coordinate(box.Center.X, box.Center.Y, box.End.Z); for (var i = 0; i < numsides; i++) { var next = (i + 1) % numsides; faces.Add(new[] {points[i], point, points[next]}); } faces.Add(points.ToArray()); var solid = new Solid(generator.GetNextObjectID()) { Colour = Colour.GetRandomBrushColour() }; foreach (var arr in faces) { var face = new Face(generator.GetNextFaceID()) { Parent = solid, Plane = new Plane(arr[0], arr[1], arr[2]), Colour = solid.Colour, Texture = { Texture = texture } }; face.Vertices.AddRange(arr.Select(x => new Vertex(x, face))); face.UpdateBoundingBox(); face.AlignTextureToFace(); solid.Faces.Add(face); } solid.UpdateBoundingBox(); yield return solid; }
public IEnumerable<MapObject> Create(IDGenerator generator, Box box, ITexture texture, int roundDecimals) { var solid = new Solid(generator.GetNextObjectID()) { Colour = Colour.GetRandomBrushColour() }; foreach (var arr in box.GetBoxFaces()) { var face = new Face(generator.GetNextFaceID()) { Parent = solid, Plane = new Plane(arr[0], arr[1], arr[2]), Colour = solid.Colour, Texture = { Texture = texture } }; face.Vertices.AddRange(arr.Select(x => new Vertex(x.Round(roundDecimals), face))); face.UpdateBoundingBox(); face.AlignTextureToFace(); solid.Faces.Add(face); } solid.UpdateBoundingBox(); yield 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); }
private Face CreateFace(DataStructures.MapObjects.Map map, List<Coordinate> points, ObjFace objFace) { var verts = objFace.Vertices.Select(x => points[x]).ToList(); var f = new Face(map.IDGenerator.GetNextFaceID()); f.Plane = new Plane(verts[2], verts[1], verts[0]); f.Vertices.AddRange(verts.Select(x => new Vertex(x, f)).Reverse()); f.UpdateBoundingBox(); return f; }
private void UpdateCurrentFace(Viewport3D viewport, ViewportEvent e) { var ray = viewport.CastRayFromScreen(e.X, e.Y); // The face doesn't change when drawing, just update the intersection if (_state == SketchState.DrawingBase || _state == SketchState.DrawingVolume) { _intersection = (_state == SketchState.DrawingBase ? _currentFace.Plane : _volumePlane).GetIntersectionPoint(ray, true, true); return; } var isect = Document.Map.WorldSpawn.GetAllNodesIntersectingWith(ray) .OfType<Solid>() .SelectMany(x => x.Faces) .Select(x => new { Item = x, Intersection = x.GetIntersectionPoint(ray) }) .Where(x => x.Intersection != null) .OrderBy(x => (x.Intersection - ray.Start).VectorMagnitude()) .FirstOrDefault(); if (isect != null) { if (_currentFace != isect.Item) { _cloneFace = isect.Item.Clone(); _cloneFace.Transform(new UnitTranslate(isect.Item.Plane.Normal * 0.1m), TransformFlags.None); } _currentFace = isect.Item; _intersection = isect.Intersection; _state = SketchState.Ready; } else { _cloneFace = null; _currentFace = null; _intersection = null; _state = SketchState.None; } }
public override void Render(ViewportBase viewport) { // Render if (_drawing != null) { var faces = _drawing.GetBoxFaces().Select(x => { var f = new Face(0) { Plane = new Plane(x[0], x[1], x[2])}; f.Vertices.AddRange(x.Select(v => new Vertex(v + f.Plane.Normal * 0.1m, f))); return f; }); MapObjectRenderer.DrawFilled(faces, Color.FromArgb(64, Color.DodgerBlue), false, false); } else if (_cloneFace != null) { MapObjectRenderer.DrawFilled(new[] { _cloneFace }, Color.FromArgb(64, Color.Orange), false, false); } }
public virtual void Unclone(Face f) { Paste(f); ID = f.ID; }
public virtual Face Copy(IDGenerator generator) { var f = new Face(generator.GetNextFaceID()) { Plane = Plane.Clone(), Colour = Colour, IsSelected = IsSelected, IsHidden = IsHidden, Opacity = Opacity, Texture = Texture.Clone(), Parent = Parent, BoundingBox = BoundingBox.Clone() }; foreach (var v in Vertices.Select(x => x.Clone())) { v.Parent = f; f.Vertices.Add(v); } return f; }
public IEnumerable<Face> GetBoxFaces() { var faces = new List<Face>(); if (Children.Any()) return faces; var box = BoundingBox.GetBoxFaces(); var dummySolid = new Solid(-1) { IsCodeHidden = IsCodeHidden, IsRenderHidden2D = IsRenderHidden2D, IsSelected = IsSelected, IsRenderHidden3D = IsRenderHidden3D, IsVisgroupHidden = IsVisgroupHidden }; foreach (var ca in box) { var face = new Face(0) { Plane = new Plane(ca[0], ca[1], ca[2]), Colour = Colour, IsSelected = IsSelected, Parent = dummySolid }; face.Vertices.AddRange(ca.Select(x => new Vertex(x, face))); face.UpdateBoundingBox(); faces.Add(face); } return faces; }
private IEnumerable<Face> GetSides() { if (_state == SketchState.None || _state == SketchState.Ready || _base == null) yield break; var b = new Face(0) {Plane = _base.Plane}; b.Vertices.AddRange(_base.Vertices.Select(x => new Vertex(x, b))); b.UpdateBoundingBox(); yield return b; if (_state != SketchState.DrawingVolume) yield break; var t = new Face(0) { Plane = new Plane(_base.Plane.Normal, _base.Plane.PointOnPlane + _base.Plane.Normal * _depth) }; t.Vertices.AddRange(_base.Vertices.Select(x => new Vertex(x + _base.Plane.Normal * _depth, t))); t.UpdateBoundingBox(); yield return t; }
public void Deselect(Face face) { SelectedFaces.RemoveAll(x => x == face); face.IsSelected = false; }
public void Select(Face face) { face.IsSelected = true; SelectedFaces.Add(face); _changed = 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 Solid SolidifyFace(DataStructures.MapObjects.Map map, Face face) { var solid = new Solid(map.IDGenerator.GetNextObjectID()); solid.Colour = Colour.GetRandomBrushColour(); solid.Faces.Add(face); face.Parent = solid; var center = face.Vertices.Aggregate(Coordinate.Zero, (sum, v) => sum + v.Location) / face.Vertices.Count; var offset = center - face.Plane.Normal * 5; for (var i = 0; i < face.Vertices.Count; i++) { var v1 = face.Vertices[i]; var v2 = face.Vertices[(i + 1) % face.Vertices.Count]; var f = new Face(map.IDGenerator.GetNextFaceID()); f.Parent = solid; f.Plane = new Plane(v1.Location, offset, v2.Location); f.Parent = solid; f.Vertices.Add(new Vertex(offset, f)); f.Vertices.Add(new Vertex(v2.Location, f)); f.Vertices.Add(new Vertex(v1.Location, f)); f.UpdateBoundingBox(); solid.Faces.Add(f); } return solid; }
private static Solid CreateFakeDecalSolid(Face face) { var s = new Solid(0) { Colour = face.Colour, IsVisgroupHidden = face.IsHidden, IsSelected = face.IsSelected }; s.Faces.Add(face); var p = face.BoundingBox.Center - face.Plane.Normal * 10; // create a new point underneath the face var p1 = face.Vertices[0].Location; var p2 = face.Vertices[1].Location; var p3 = face.Vertices[2].Location; var p4 = face.Vertices[3].Location; var faces = new[] { new[] { p2, p1, p}, new[] { p3, p2, p}, new[] { p4, p3, p}, new[] { p1, p4, p} }; foreach (var ff in faces) { var f = new Face(-1) { Colour = face.Colour, IsSelected = face.IsSelected, IsHidden = face.IsHidden, Plane = new Plane(ff[0], ff[1], ff[2]) }; f.Vertices.AddRange(ff.Select(x => new Vertex(x, f))); f.UpdateBoundingBox(); s.Faces.Add(f); } s.UpdateBoundingBox(); return s; }
public void AlignTextureWithFace(Face face) { // Get reference values for the axes var refU = face.Texture.UAxis; var refV = face.Texture.VAxis; // Reference points in the texture plane to use for shifting later on var refX = face.Texture.UAxis * face.Texture.XShift * face.Texture.XScale; var refY = face.Texture.VAxis * face.Texture.YShift * face.Texture.YScale; // Two non-parallel planes intersect at an edge. We want the textures on this face // to line up with the textures on the provided face. To do this, we rotate the texture // normal on the provided face around the intersection edge to get the new texture axes. // Then we rotate the texture reference point around this edge as well to get the new shift values. // The scale values on both faces will always end up being the same value. // Find the intersection edge vector var intersectionEdge = face.Plane.Normal.Cross(Plane.Normal); // Create a plane using the intersection edge as the normal var intersectionPlane = new Plane(intersectionEdge, 0); // If the planes are parallel, the texture doesn't need any rotation - just different shift values. var intersect = Plane.Intersect(face.Plane, Plane, intersectionPlane); if (intersect != null) { var texNormal = face.Texture.GetNormal(); // Since the intersection plane is perpendicular to both face planes, we can find the angle // between the two planes (the original texture plane and the plane of this face) by projecting // the normals of the planes onto the perpendicular plane and taking the cross product. // Project the two normals onto the perpendicular plane var ptNormal = intersectionPlane.Project(texNormal).Normalise(); var ppNormal = intersectionPlane.Project(Plane.Normal).Normalise(); // Get the angle between the projected normals var dot = Math.Round(ptNormal.Dot(ppNormal), 4); var angle = DMath.Acos(dot); // A.B = cos(angle) // Rotate the texture axis by the angle around the intersection edge var transform = new UnitRotate(angle, new Line(Coordinate.Zero, intersectionEdge)); refU = transform.Transform(refU); refV = transform.Transform(refV); // Rotate the texture reference points as well, but around the intersection line, not the origin refX = transform.Transform(refX + intersect) - intersect; refY = transform.Transform(refY + intersect) - intersect; } // Convert the reference points back to get the final values Texture.Rotation = 0; Texture.UAxis = refU; Texture.VAxis = refV; Texture.XShift = refU.Dot(refX) / face.Texture.XScale; Texture.YShift = refV.Dot(refY) / face.Texture.YScale; Texture.XScale = face.Texture.XScale; Texture.YScale = face.Texture.YScale; CalculateTextureCoordinates(true); }
public void CalculateDecalGeometry() { _decalGeometry = new List<Face>(); if (Decal == null) return; // Texture not found var boxRadius = Coordinate.One * 4; // Decals apply to all faces that intersect within an 8x8x8 bounding box // centered at the origin of the decal var box = new Box(Origin - boxRadius, Origin + boxRadius); var root = GetRoot(Parent); // Get the faces that intersect with the decal's radius var faces = root.GetAllNodesIntersectingWith(box).OfType<Solid>() .SelectMany(x => x.Faces).Where(x => x.IntersectsWithBox(box)); var idg = new IDGenerator(); // Dummy generator foreach (var face in faces) { // Project the decal onto the face var center = face.Plane.Project(Origin); var texture = face.Texture.Clone(); texture.Name = Decal.Name; texture.Texture = Decal; texture.XShift = -Decal.Width / 2m; texture.YShift = -Decal.Height / 2m; var decalFace = new Face(idg.GetNextFaceID()) { Colour = Colour, IsSelected = IsSelected, IsHidden = IsCodeHidden, Plane = face.Plane, Texture = texture }; // Re-project the vertices in case the texture axes are not on the face plane var xShift = face.Texture.UAxis * face.Texture.XScale * Decal.Width / 2; var yShift = face.Texture.VAxis * face.Texture.YScale * Decal.Height / 2; var verts = new[] { new Vertex(face.Plane.Project(center + xShift - yShift), decalFace), // Bottom Right new Vertex(face.Plane.Project(center + xShift + yShift), decalFace), // Top Right new Vertex(face.Plane.Project(center - xShift + yShift), decalFace), // Top Left new Vertex(face.Plane.Project(center - xShift - yShift), decalFace) // Bottom Left }; // Because the texture axes don't have to align to the face, we might have a reversed face here // If so, reverse the points to get a valid face for the plane. // TODO: Is there a better way to do this? var vertPlane = new Plane(verts[0].Location, verts[1].Location, verts[2].Location); if (!face.Plane.Normal.EquivalentTo(vertPlane.Normal)) { Array.Reverse(verts); } decalFace.Vertices.AddRange(verts); decalFace.UpdateBoundingBox(); // Calculate the X and Y shift bases on the first vertex location (assuming U/V of first vertex is zero) - we dont want these to change var vtx = decalFace.Vertices[0]; decalFace.Texture.XShift = -(vtx.Location.Dot(decalFace.Texture.UAxis)) / decalFace.Texture.XScale; decalFace.Texture.YShift = -(vtx.Location.Dot(decalFace.Texture.VAxis)) / decalFace.Texture.YScale; decalFace.CalculateTextureCoordinates(); // Next, the decal geometry needs to be clipped to the face so it doesn't spill into the void // Create a fake solid out of the decal geometry and clip it against all the brush planes var fake = CreateFakeDecalSolid(decalFace); foreach (var f in face.Parent.Faces.Except(new[] { face })) { Solid back, front; fake.Split(f.Plane, out back, out front, idg); fake = back ?? fake; } // Extract out the original face decalFace = fake.Faces.First(x => x.Plane.EquivalentTo(face.Plane, 0.05m)); // Add a tiny bit to the normal axis to ensure the decal is rendered in front of the face var normalAdd = face.Plane.Normal * 0.2m; decalFace.Transform(new UnitTranslate(normalAdd), TransformFlags.TextureLock); _decalGeometry.Add(decalFace); } }
public virtual void Paste(Face f) { Plane = f.Plane.Clone(); Colour = f.Colour; IsSelected = f.IsSelected; IsHidden = f.IsHidden; Opacity = f.Opacity; Texture = f.Texture.Clone(); Parent = f.Parent; BoundingBox = f.BoundingBox.Clone(); Vertices.Clear(); foreach (var v in f.Vertices.Select(x => x.Clone())) { v.Parent = this; Vertices.Add(v); } }
private static Face ReadFace(BinaryReader br, IDGenerator generator) { var face = new Face(generator.GetNextFaceID()); var textureName = br.ReadFixedLengthString(Encoding.UTF8, 256); br.ReadBytes(4); // Unused face.Texture.Name = textureName; face.Texture.UAxis = br.ReadCoordinate(); face.Texture.XShift = br.ReadSingleAsDecimal(); face.Texture.VAxis = br.ReadCoordinate(); face.Texture.YShift = br.ReadSingleAsDecimal(); face.Texture.Rotation = br.ReadSingleAsDecimal(); face.Texture.XScale = br.ReadSingleAsDecimal(); face.Texture.YScale = br.ReadSingleAsDecimal(); br.ReadBytes(16); // Unused var numVerts = br.ReadInt32(); for (var i = 0; i < numVerts; i++) { face.Vertices.Add(new Vertex(br.ReadCoordinate(), face)); } face.Plane = br.ReadPlane(); face.UpdateBoundingBox(); return face; }
private static bool RenderTransparent(ITexture texture, Face face) { //if (face.Parent != null && face.Parent.IsTransparent) return true; if (texture == null || texture.Name == null) return false; return texture.Name.ToLower() == "aaatrigger"; }
private static void WriteFace(BinaryWriter bw, Face face) { bw.WriteFixedLengthString(Encoding.UTF8, 256, face.Texture.Name); bw.Write(new byte[4]); bw.WriteCoordinate(face.Texture.UAxis); bw.WriteDecimalAsSingle(face.Texture.XShift); bw.WriteCoordinate(face.Texture.VAxis); bw.WriteDecimalAsSingle(face.Texture.YShift); bw.WriteDecimalAsSingle(face.Texture.Rotation); bw.WriteDecimalAsSingle(face.Texture.XScale); bw.WriteDecimalAsSingle(face.Texture.YScale); bw.Write(new byte[16]); bw.Write(face.Vertices.Count); foreach (var vertex in face.Vertices) { bw.WriteCoordinate(vertex.Location); } bw.WritePlane(face.Vertices.Select(v => v.Location).ToArray()); }
public override void ToolSelected(bool preventHistory) { _state = SketchState.None; _currentFace = _cloneFace = null; _intersection = null; _drawing = null; _volumePlane = null; }
private Solid MakeSolid(IDGenerator generator, IEnumerable<Coordinate[]> faces, ITexture texture, Color col) { var solid = new Solid(generator.GetNextObjectID()) { Colour = col }; foreach (var arr in faces) { var face = new Face(generator.GetNextFaceID()) { Parent = solid, Plane = new Plane(arr[0], arr[1], arr[2]), Colour = solid.Colour, Texture = { Texture = texture } }; face.Vertices.AddRange(arr.Select(x => new Vertex(x, face))); face.UpdateBoundingBox(); face.AlignTextureToWorld(); solid.Faces.Add(face); } solid.UpdateBoundingBox(); return solid; }
public Vertex(Coordinate location, Face parent) { Location = location; Parent = parent; TextureV = TextureU = 0; }
public IEnumerable<MapObject> Create(IDGenerator generator, Box box, ITexture texture, int roundDecimals) { var numsides = (int) _numSides.GetValue(); if (numsides < 3) yield break; // Cylinders can be elliptical so use both major and minor rather than just the radius // NOTE: when a low number (< 10ish) of faces are selected this will cause the cylinder to not touch all the edges of the box. var width = box.Width; var length = box.Length; var height = box.Height; var major = width / 2; var minor = length / 2; var angle = 2 * DMath.PI / numsides; // Calculate the X and Y points for the ellipse var points = new Coordinate[numsides]; for (var i = 0; i < numsides; i++) { var a = i * angle; var xval = box.Center.X + major * DMath.Cos(a); var yval = box.Center.Y + minor * DMath.Sin(a); var zval = box.Start.Z; points[i] = new Coordinate(xval, yval, zval).Round(roundDecimals); } var faces = new List<Coordinate[]>(); // Add the vertical faces var z = new Coordinate(0, 0, height).Round(roundDecimals); for (var i = 0; i < numsides; i++) { var next = (i + 1) % numsides; faces.Add(new[] {points[i], points[i] + z, points[next] + z, points[next]}); } // Add the elliptical top and bottom faces faces.Add(points.ToArray()); faces.Add(points.Select(x => x + z).Reverse().ToArray()); // Nothing new here, move along var solid = new Solid(generator.GetNextObjectID()) { Colour = Colour.GetRandomBrushColour() }; foreach (var arr in faces) { var face = new Face(generator.GetNextFaceID()) { Parent = solid, Plane = new Plane(arr[0], arr[1], arr[2]), Colour = solid.Colour, Texture = { Texture = texture } }; face.Vertices.AddRange(arr.Select(x => new Vertex(x, face))); face.UpdateBoundingBox(); face.AlignTextureToFace(); solid.Faces.Add(face); } solid.UpdateBoundingBox(); yield return solid; }
public IEnumerable<MapObject> Create(IDGenerator generator, Box box, ITexture texture, int roundDecimals) { var width = box.Width; var length = Math.Max(1, Math.Abs((int) box.Length)); var height = box.Height; var flatten = (float) _flattenFactor.Value; var text = _text.GetValue(); var family = _fontChooser.GetFontFamily(); var style = Enum.GetValues(typeof (FontStyle)).OfType<FontStyle>().FirstOrDefault(fs => family.IsStyleAvailable(fs)); if (!family.IsStyleAvailable(style)) family = FontFamily.GenericSansSerif; var set = new PolygonSet(); var sizes = new List<RectangleF>(); using (var bmp = new Bitmap(1,1)) { using (var g = System.Drawing.Graphics.FromImage(bmp)) { using (var font = new Font(family, length, style, GraphicsUnit.Pixel)) { for (var i = 0; i < text.Length; i += 32) { using (var sf = new StringFormat(StringFormat.GenericTypographic)) { var rem = Math.Min(text.Length, i + 32) - i; var range = Enumerable.Range(0, rem).Select(x => new CharacterRange(x, 1)).ToArray(); sf.SetMeasurableCharacterRanges(range); var reg = g.MeasureCharacterRanges(text.Substring(i, rem), font, new RectangleF(0, 0, float.MaxValue, float.MaxValue), sf); sizes.AddRange(reg.Select(x => x.GetBounds(g))); } } } } } var xOffset = box.Start.DX; var yOffset = box.End.DY; for (var ci = 0; ci < text.Length; ci++) { var c = text[ci]; var size = sizes[ci]; var gp = new GraphicsPath(); gp.AddString(c.ToString(CultureInfo.InvariantCulture), family, (int)style, length, new PointF(0, 0), StringFormat.GenericTypographic); gp.Flatten(new System.Drawing.Drawing2D.Matrix(), flatten); var polygons = new List<Polygon>(); var poly = new List<PolygonPoint>(); for (var i = 0; i < gp.PointCount; i++) { var type = gp.PathTypes[i]; var point = gp.PathPoints[i]; poly.Add(new PolygonPoint(point.X + xOffset, -point.Y + yOffset)); if ((type & 0x80) == 0x80) { polygons.Add(new Polygon(poly)); poly.Clear(); } } var tri = new List<Polygon>(); Polygon polygon = null; foreach (var p in polygons) { if (polygon == null) { polygon = p; tri.Add(p); } else if (p.CalculateWindingOrder() != polygon.CalculateWindingOrder()) { polygon.AddHole(p); } else { polygon = null; tri.Add(p); } } foreach (var pp in tri) { try { P2T.Triangulate(pp); set.Add(pp); } catch { // Ignore } } xOffset += size.Width; } var zOffset = box.Start.Z; foreach (var polygon in set.Polygons) { foreach (var t in polygon.Triangles) { var points = t.Points.Select(x => new Coordinate((decimal) x.X, (decimal) x.Y, zOffset).Round(roundDecimals)).ToList(); var faces = new List<Coordinate[]>(); // Add the vertical faces var z = new Coordinate(0, 0, height).Round(roundDecimals); for (var j = 0; j < points.Count; j++) { var next = (j + 1) % points.Count; faces.Add(new[] {points[j], points[j] + z, points[next] + z, points[next]}); } // Add the top and bottom faces faces.Add(points.ToArray()); faces.Add(points.Select(x => x + z).Reverse().ToArray()); // Nothing new here, move along var solid = new Solid(generator.GetNextObjectID()) {Colour = Colour.GetRandomBrushColour()}; foreach (var arr in faces) { var face = new Face(generator.GetNextFaceID()) { Parent = solid, Plane = new Plane(arr[0], arr[1], arr[2]), Colour = solid.Colour, Texture = {Texture = texture} }; face.Vertices.AddRange(arr.Select(x => new Vertex(x, face))); face.UpdateBoundingBox(); face.AlignTextureToFace(); solid.Faces.Add(face); } solid.UpdateBoundingBox(); yield return solid; } } }