| | 1 | | using Newtonsoft.Json; |
| | 2 | | using System; |
| | 3 | | using System.Linq; |
| | 4 | |
|
| | 5 | | namespace MorphoGeometry |
| | 6 | | { |
| | 7 | | /// <summary> |
| | 8 | | /// Face class. |
| | 9 | | /// </summary> |
| | 10 | | public class Face : IEquatable<Face> |
| | 11 | | { |
| | 12 | | private Vector[] _vertices; |
| | 13 | |
|
| | 14 | | [JsonIgnore] |
| | 15 | | /// <summary> |
| | 16 | | /// First vertex. |
| | 17 | | /// </summary> |
| 52 | 18 | | public Vector A => Vertices[0]; |
| | 19 | |
|
| | 20 | | [JsonIgnore] |
| | 21 | | /// <summary> |
| | 22 | | /// Second vertex. |
| | 23 | | /// </summary> |
| 26 | 24 | | public Vector B => Vertices[1]; |
| | 25 | |
|
| | 26 | | [JsonIgnore] |
| | 27 | | /// <summary> |
| | 28 | | /// Third vertex. |
| | 29 | | /// </summary> |
| 26 | 30 | | public Vector C => Vertices[2]; |
| | 31 | |
|
| | 32 | | [JsonIgnore] |
| | 33 | | /// <summary> |
| | 34 | | /// Fourth vertex |
| | 35 | | /// </summary> |
| 0 | 36 | | public Vector D => Vertices[3]; |
| | 37 | |
|
| | 38 | | [JsonProperty("vertices", Required = Required.Always)] |
| | 39 | | /// <summary> |
| | 40 | | /// Vertices of the face. |
| | 41 | | /// </summary> |
| | 42 | | public Vector[] Vertices |
| | 43 | | { |
| 537 | 44 | | get { return _vertices; } |
| | 45 | | private set |
| 89187 | 46 | | { |
| 89187 | 47 | | if (value.Length == 3 || value.Length == 4) |
| 89185 | 48 | | _vertices = value; |
| | 49 | | else |
| 2 | 50 | | throw new ArgumentOutOfRangeException( |
| 2 | 51 | | $"{nameof(value)} has contain 3 or 4 vectors."); |
| 89185 | 52 | | } |
| | 53 | | } |
| | 54 | |
|
| | 55 | | /// <summary> |
| | 56 | | /// Is point behind a Face. |
| | 57 | | /// </summary> |
| | 58 | | /// <param name="point">Point for testing.</param> |
| | 59 | | /// <returns>0, 1, -1. 0 onto the face. 1 in front |
| | 60 | | /// of the face. -1 behind the face</returns> |
| | 61 | | public int IsPointBehind(Vector point) |
| 0 | 62 | | { |
| 0 | 63 | | var v = Vector.VectorFrom2Points(A, point); |
| 0 | 64 | | if (Normal.Dot(v) > 0) return 1; |
| 0 | 65 | | else if (Normal.Dot(v) < 0) return -1; |
| 0 | 66 | | else return 0; |
| 0 | 67 | | } |
| | 68 | |
|
| | 69 | | [JsonIgnore] |
| | 70 | | /// <summary> |
| | 71 | | /// Normal vector. |
| | 72 | | /// </summary> |
| | 73 | | public Vector Normal |
| | 74 | | { |
| | 75 | | get |
| 26 | 76 | | { |
| 26 | 77 | | var dir = (B.Sub(A)).Cross(C.Sub(A)); |
| 26 | 78 | | var norm = dir.Normalize(); |
| 26 | 79 | | return norm; |
| 26 | 80 | | } |
| | 81 | | } |
| | 82 | |
|
| | 83 | | [JsonConstructor] |
| | 84 | | /// <summary> |
| | 85 | | /// Create a new face. |
| | 86 | | /// </summary> |
| | 87 | | /// <param name="vertices">Vertices.</param> |
| 89187 | 88 | | public Face(Vector[] vertices) |
| 89187 | 89 | | { |
| 89187 | 90 | | Vertices = vertices; |
| 89185 | 91 | | } |
| | 92 | |
|
| | 93 | | /// <summary> |
| | 94 | | /// Face with 4 vertices. |
| | 95 | | /// </summary> |
| | 96 | | /// <returns>True or false.</returns> |
| | 97 | | public bool IsQuad() |
| 36 | 98 | | { |
| 36 | 99 | | return (Vertices.Length == 4); |
| 36 | 100 | | } |
| | 101 | |
|
| | 102 | | /// <summary> |
| | 103 | | /// Minimun point of the face. |
| | 104 | | /// </summary> |
| | 105 | | /// <returns>Vector.</returns> |
| | 106 | | public Vector Min() |
| 0 | 107 | | { |
| 0 | 108 | | var x = Vertices.Select(_ => _.x).Min(); |
| 0 | 109 | | var y = Vertices.Select(_ => _.y).Min(); |
| 0 | 110 | | var z = Vertices.Select(_ => _.z).Min(); |
| | 111 | |
|
| 0 | 112 | | return new Vector(x, y, z); |
| 0 | 113 | | } |
| | 114 | |
|
| | 115 | | /// <summary> |
| | 116 | | /// Maximum point of the face. |
| | 117 | | /// </summary> |
| | 118 | | /// <returns>Vector.</returns> |
| | 119 | | public Vector Max() |
| 0 | 120 | | { |
| 0 | 121 | | var x = Vertices.Select(_ => _.x).Max(); |
| 0 | 122 | | var y = Vertices.Select(_ => _.y).Max(); |
| 0 | 123 | | var z = Vertices.Select(_ => _.z).Max(); |
| | 124 | |
|
| 0 | 125 | | return new Vector(x, y, z); |
| 0 | 126 | | } |
| | 127 | |
|
| | 128 | | /// <summary> |
| | 129 | | /// From quadrangular face to triangular face. |
| | 130 | | /// </summary> |
| | 131 | | /// <param name="face">Face to divide.</param> |
| | 132 | | /// <returns>Array of triangular faces.</returns> |
| | 133 | | public static Face[] Triangulate(Face face) |
| 0 | 134 | | { |
| 0 | 135 | | return new Face[] |
| 0 | 136 | | { |
| 0 | 137 | | new Face( new Vector[3] { face.A, face.B, face.C } ), |
| 0 | 138 | | new Face( new Vector[3] { face.C, face.D, face.A } ) |
| 0 | 139 | | }; |
| 0 | 140 | | } |
| | 141 | |
|
| | 142 | | /// <summary> |
| | 143 | | /// String representation of the face. |
| | 144 | | /// </summary> |
| | 145 | | /// <returns>String representation.</returns> |
| | 146 | | public override String ToString() |
| 0 | 147 | | { |
| 0 | 148 | | return string.Format("Face::{0}", Vertices.Length); |
| 0 | 149 | | } |
| | 150 | |
|
| | 151 | | public string Serialize() |
| 1 | 152 | | { |
| 1 | 153 | | return JsonConvert.SerializeObject(this); |
| 1 | 154 | | } |
| | 155 | |
|
| | 156 | | public static Face Deserialize(string json) |
| 2 | 157 | | { |
| | 158 | | try |
| 2 | 159 | | { |
| 2 | 160 | | return JsonConvert.DeserializeObject<Face>(json); |
| | 161 | | } |
| 1 | 162 | | catch (Exception e) |
| 1 | 163 | | { |
| 1 | 164 | | throw new Exception(e.Message); |
| | 165 | | } |
| 1 | 166 | | } |
| | 167 | |
|
| | 168 | | public bool Equals(Face other) |
| 13 | 169 | | { |
| 13 | 170 | | if (other == null) |
| 0 | 171 | | return false; |
| | 172 | |
|
| 13 | 173 | | if (other != null |
| 13 | 174 | | && Enumerable.SequenceEqual(other.Vertices, this.Vertices) |
| 13 | 175 | | && other.Normal == this.Normal) |
| 13 | 176 | | return true; |
| | 177 | | else |
| 0 | 178 | | return false; |
| 13 | 179 | | } |
| | 180 | |
|
| | 181 | | public override bool Equals(Object obj) |
| 53 | 182 | | { |
| 53 | 183 | | if (obj == null) |
| 0 | 184 | | return false; |
| | 185 | |
|
| 53 | 186 | | var faceObj = obj as Face; |
| 53 | 187 | | if (faceObj == null) |
| 53 | 188 | | return false; |
| | 189 | | else |
| 0 | 190 | | return Equals(faceObj); |
| 53 | 191 | | } |
| | 192 | |
|
| | 193 | | public override int GetHashCode() |
| 0 | 194 | | { |
| | 195 | | unchecked |
| 0 | 196 | | { |
| 0 | 197 | | int hash = 17; |
| 0 | 198 | | hash = hash * 23 + Vertices.GetHashCode(); |
| 0 | 199 | | hash = hash * 23 + Normal.GetHashCode(); |
| 0 | 200 | | return hash; |
| | 201 | | } |
| 0 | 202 | | } |
| | 203 | |
|
| | 204 | | public static bool operator ==(Face face1, Face face2) |
| 79 | 205 | | { |
| 79 | 206 | | if (((object)face1) == null || ((object)face2) == null) |
| 79 | 207 | | return Object.Equals(face1, face2); |
| | 208 | |
|
| 0 | 209 | | return face1.Equals(face2); |
| 79 | 210 | | } |
| | 211 | |
|
| | 212 | | public static bool operator !=(Face face1, Face face2) |
| 13 | 213 | | { |
| 13 | 214 | | return !(face1 == face2); |
| 13 | 215 | | } |
| | 216 | | } |
| | 217 | | } |