-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add some basic factory classes to create known solid types
- Loading branch information
1 parent
ca6619c
commit 183c776
Showing
17 changed files
with
661 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using Sledge.Formats.Geometric; | ||
using Sledge.Formats.Map.Objects; | ||
|
||
namespace Sledge.Formats.Map.Factories | ||
{ | ||
/// <summary> | ||
/// A class that creates <see cref="MapObject"/> instances of any type | ||
/// </summary> | ||
public interface IMapObjectFactory | ||
{ | ||
/// <summary> | ||
/// The name of this object factory | ||
/// </summary> | ||
string Name { get; } | ||
|
||
/// <summary> | ||
/// Create the map objects from the given box | ||
/// </summary> | ||
/// <param name="box">The box to define the bounds of the created objects</param> | ||
/// <returns>0 or more MapObject instances</returns> | ||
IEnumerable<MapObject> Create(Box box); | ||
|
||
/// <summary> | ||
/// Get a list of properties for this factory. | ||
/// </summary> | ||
/// <returns>The list of properties</returns> | ||
IEnumerable<MapObjectFactoryProperty> GetProperties(); | ||
|
||
/// <summary> | ||
/// Get a property value. | ||
/// </summary> | ||
/// <param name="name">The name of the property</param> | ||
/// <exception cref="ArgumentException">If the property of the given name doesn't exist</exception> | ||
object GetProperty(string name); | ||
|
||
/// <summary> | ||
/// Set a property value. | ||
/// </summary> | ||
/// <param name="name">The name of the property</param> | ||
/// <param name="value">The value of the property</param> | ||
/// <exception cref="ArgumentException">If the property of the given name doesn't exist</exception> | ||
/// <exception cref="InvalidCastException">If the property value could not be set due to an invalid type</exception> | ||
void SetProperty(string name, object value); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using Sledge.Formats.Geometric; | ||
using Sledge.Formats.Map.Objects; | ||
|
||
namespace Sledge.Formats.Map.Factories | ||
{ | ||
/// <summary> | ||
/// Abstract base class that provides automatic property get/set information using reflection. | ||
/// </summary> | ||
public abstract class MapObjectFactoryBase : IMapObjectFactory | ||
{ | ||
private readonly Lazy<Dictionary<string, ReflectionMapObjectFactoryProperty>> _properties; | ||
|
||
protected MapObjectFactoryBase() | ||
{ | ||
_properties = new Lazy<Dictionary<string, ReflectionMapObjectFactoryProperty>>(() => GetType().GetProperties().Where(x => x.CanRead && x.CanWrite).ToDictionary(x => x.Name, x => new ReflectionMapObjectFactoryProperty(x))); | ||
} | ||
|
||
/// <inheritdoc cref="IMapObjectFactory.Name"/> | ||
public abstract string Name { get; } | ||
|
||
/// <inheritdoc cref="IMapObjectFactory.Create"/> | ||
public abstract IEnumerable<MapObject> Create(Box box); | ||
|
||
/// <inheritdoc cref="IMapObjectFactory.GetProperties"/> | ||
public IEnumerable<MapObjectFactoryProperty> GetProperties() | ||
{ | ||
return _properties.Value.Values; | ||
} | ||
|
||
/// <inheritdoc cref="IMapObjectFactory.GetProperty"/> | ||
public object GetProperty(string name) | ||
{ | ||
if (!_properties.Value.ContainsKey(name)) throw new ArgumentException($"Property with name '{name}' not found", nameof(name)); | ||
return _properties.Value[name].Property.GetValue(this); | ||
} | ||
|
||
/// <inheritdoc cref="IMapObjectFactory.SetProperty"/> | ||
public void SetProperty(string name, object value) | ||
{ | ||
if (!_properties.Value.ContainsKey(name)) throw new ArgumentException($"Property with name '{name}' not found", nameof(name)); | ||
_properties.Value[name].Property.SetValue(this, value); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
using System; | ||
|
||
namespace Sledge.Formats.Map.Factories | ||
{ | ||
/// <summary> | ||
/// A class describing a property of a <see cref="IMapObjectFactory"/> | ||
/// </summary> | ||
public class MapObjectFactoryProperty | ||
{ | ||
/// <summary> | ||
/// The name of the property | ||
/// </summary> | ||
public string Name { get; set; } | ||
|
||
/// <summary> | ||
/// The display name of the property | ||
/// </summary> | ||
public string DisplayName { get; set; } | ||
|
||
/// <summary> | ||
/// A description of the property | ||
/// </summary> | ||
public string Description { get; set; } | ||
|
||
/// <summary> | ||
/// The type of the property | ||
/// </summary> | ||
public Type Type { get; set; } | ||
|
||
/// <summary> | ||
/// For decimal properties, the minimum allowed value. Will default to <see cref="int.MinValue"/>. | ||
/// </summary> | ||
public decimal MinValue { get; set; } = int.MinValue; | ||
|
||
/// <summary> | ||
/// For decimal properties, the maximum allowed value. Will default to <see cref="int.MaxValue"/>. | ||
/// </summary> | ||
public decimal MaxValue { get; set; } = int.MaxValue; | ||
|
||
/// <summary> | ||
/// For decimal properties, the number of decimal places allowed. | ||
/// </summary> | ||
public int DecimalPrecision { get; set; } | ||
} | ||
} |
12 changes: 12 additions & 0 deletions
12
Sledge.Formats.Map/Factories/MapObjectFactoryPropertyDataAttribute.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
using System; | ||
|
||
namespace Sledge.Formats.Map.Factories | ||
{ | ||
[AttributeUsage(AttributeTargets.Property)] | ||
public class MapObjectFactoryPropertyDataAttribute : System.Attribute | ||
{ | ||
public double MinValue { get; set; } = int.MinValue; | ||
public double MaxValue { get; set; } = int.MaxValue; | ||
public int DecimalPrecision { get; set; } = 2; | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
Sledge.Formats.Map/Factories/ReflectionMapObjectFactoryProperty.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
using System.ComponentModel; | ||
using System.Reflection; | ||
|
||
namespace Sledge.Formats.Map.Factories | ||
{ | ||
/// <summary> | ||
/// A <see cref="MapObjectFactoryProperty"/> with a <see cref="PropertyInfo"/> instance for get/set. | ||
/// </summary> | ||
public class ReflectionMapObjectFactoryProperty : MapObjectFactoryProperty | ||
{ | ||
/// <summary> | ||
/// The reflection property | ||
/// </summary> | ||
public PropertyInfo Property { get; } | ||
|
||
public ReflectionMapObjectFactoryProperty(PropertyInfo property) | ||
{ | ||
Property = property; | ||
Name = property.Name; | ||
DisplayName = property.GetCustomAttribute<DisplayNameAttribute>(true)?.DisplayName ?? property.Name; | ||
Description = property.GetCustomAttribute<DescriptionAttribute>(true)?.Description ?? ""; | ||
Type = property.PropertyType; | ||
var pd = property.GetCustomAttribute<MapObjectFactoryPropertyDataAttribute>(true); | ||
if (pd != null) | ||
{ | ||
MinValue = (decimal) pd.MinValue; | ||
MaxValue = (decimal) pd.MaxValue; | ||
DecimalPrecision = pd.DecimalPrecision; | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using Sledge.Formats.Geometric; | ||
using Sledge.Formats.Map.Objects; | ||
|
||
namespace Sledge.Formats.Map.Factories.Solids | ||
{ | ||
/// <summary> | ||
/// Factory that creates a six sided rectangular prism | ||
/// </summary> | ||
public class BlockSolidFactory : SolidFactoryBase | ||
{ | ||
public override string Name => "Block"; | ||
|
||
public override IEnumerable<MapObject> Create(Box box) | ||
{ | ||
var solid = new Solid | ||
{ | ||
Color = ColorUtils.GetRandomBrushColour() | ||
}; | ||
|
||
foreach (var polygon in box.GetBoxFaces()) | ||
{ | ||
var face = new Face | ||
{ | ||
Plane = polygon.Plane, | ||
TextureName = VisibleTextureName | ||
}; | ||
face.Vertices.AddRange(polygon.Vertices.Select(x => x.Round(RoundDecimals))); | ||
solid.Faces.Add(face); | ||
} | ||
yield return solid; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.ComponentModel; | ||
using System.Linq; | ||
using System.Numerics; | ||
using Sledge.Formats.Geometric; | ||
using Sledge.Formats.Map.Objects; | ||
|
||
namespace Sledge.Formats.Map.Factories.Solids | ||
{ | ||
public class ConeSolidFactory : SolidFactoryBase | ||
{ | ||
public override string Name => "Cone"; | ||
|
||
[DisplayName("Number of sides")] | ||
[Description("The number of sides the base of the cone will have")] | ||
[MapObjectFactoryPropertyData(MinValue = 3)] | ||
public int NumberOfSides { get; set; } | ||
|
||
public override IEnumerable<MapObject> Create(Box box) | ||
{ | ||
var numSides = NumberOfSides; | ||
if (numSides < 3) throw new ArgumentException("NumberOfSides must be >= 3", nameof(NumberOfSides)); | ||
|
||
// 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 * Math.PI / numSides; | ||
|
||
var points = new List<Vector3>(); | ||
for (var i = 0; i < numSides; i++) | ||
{ | ||
var a = i * angle; | ||
var xval = box.Center.X + major * (float)Math.Cos(a); | ||
var yval = box.Center.Y + minor * (float)Math.Sin(a); | ||
var zval = box.Start.Z; | ||
points.Add(new Vector3(xval, yval, zval).Round(RoundDecimals)); | ||
} | ||
points.Reverse(); | ||
|
||
var faces = new List<Vector3[]>(); | ||
|
||
var point = new Vector3(box.Center.X, box.Center.Y, box.End.Z).Round(RoundDecimals); | ||
for (var i = 0; i < numSides; i++) | ||
{ | ||
var next = (i + 1) % numSides; | ||
faces.Add(new[] { points[next], points[i], point }); | ||
} | ||
faces.Add(points.ToArray()); | ||
|
||
var solid = new Solid | ||
{ | ||
Color = ColorUtils.GetRandomBrushColour() | ||
}; | ||
|
||
foreach (var arr in faces) | ||
{ | ||
var face = new Face | ||
{ | ||
Plane = Plane.CreateFromVertices(arr[0], arr[1], arr[2]), | ||
TextureName = VisibleTextureName | ||
}; | ||
face.Vertices.AddRange(arr); | ||
solid.Faces.Add(face); | ||
} | ||
yield return solid; | ||
} | ||
} | ||
} |
79 changes: 79 additions & 0 deletions
79
Sledge.Formats.Map/Factories/Solids/CylinderSolidFactory.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.ComponentModel; | ||
using System.Linq; | ||
using System.Numerics; | ||
using Sledge.Formats.Geometric; | ||
using Sledge.Formats.Map.Objects; | ||
|
||
namespace Sledge.Formats.Map.Factories.Solids | ||
{ | ||
public class CylinderSolidFactory : SolidFactoryBase | ||
{ | ||
public override string Name => "Cone"; | ||
|
||
[DisplayName("Number of sides")] | ||
[Description("The number of sides the base of the cylinder will have")] | ||
[MapObjectFactoryPropertyData(MinValue = 3)] | ||
public int NumberOfSides { get; set; } | ||
|
||
public override IEnumerable<MapObject> Create(Box box) | ||
{ | ||
var numSides = NumberOfSides; | ||
if (numSides < 3) throw new ArgumentException("NumberOfSides must be >= 3", nameof(NumberOfSides)); | ||
|
||
// 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 * (float)Math.PI / numSides; | ||
|
||
// Calculate the X and Y points for the ellipse | ||
var points = new List<Vector3>(); | ||
for (var i = 0; i < numSides; i++) | ||
{ | ||
var a = i * angle; | ||
var xval = box.Center.X + major * (float)Math.Cos(a); | ||
var yval = box.Center.Y + minor * (float)Math.Sin(a); | ||
var zval = box.Start.Z; | ||
points.Add(new Vector3(xval, yval, zval).Round(RoundDecimals)); | ||
} | ||
points.Reverse(); | ||
|
||
var faces = new List<Vector3[]>(); | ||
|
||
// Add the vertical faces | ||
var z = new Vector3(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 | ||
{ | ||
Color = ColorUtils.GetRandomBrushColour() | ||
}; | ||
|
||
foreach (var arr in faces) | ||
{ | ||
var face = new Face | ||
{ | ||
Plane = Plane.CreateFromVertices(arr[0], arr[1], arr[2]), | ||
TextureName = VisibleTextureName | ||
}; | ||
face.Vertices.AddRange(arr); | ||
solid.Faces.Add(face); | ||
} | ||
yield return solid; | ||
} | ||
} | ||
} |
Oops, something went wrong.