| | 1 | | using System; |
| | 2 | | using System.Collections.Generic; |
| | 3 | | using System.ComponentModel; |
| | 4 | | using System.ComponentModel.DataAnnotations; |
| | 5 | | using System.Linq; |
| | 6 | | using Morpho25.Utility; |
| | 7 | | using Newtonsoft.Json; |
| | 8 | |
|
| | 9 | | namespace Morpho25.Geometry |
| | 10 | | { |
| | 11 | | [DisplayName("Grid")] |
| | 12 | | /// <summary> |
| | 13 | | /// Grid class. |
| | 14 | | /// </summary> |
| | 15 | | public class Grid |
| | 16 | | { |
| | 17 | | /// <summary> |
| | 18 | | /// Create a new Grid. |
| | 19 | | /// </summary> |
| | 20 | | /// <param name="size">Grid size object.</param> |
| | 21 | | /// <param name="nestingGrids">Optional nestring grids.</param> |
| 8 | 22 | | public Grid(Size size, |
| 8 | 23 | | NestingGrids nestingGrids = null) |
| 8 | 24 | | { |
| 8 | 25 | | Size = size; |
| 8 | 26 | | Telescope = 0.0; |
| 8 | 27 | | StartTelescopeHeight = 0.0; |
| 8 | 28 | | CombineGridType = false; |
| | 29 | |
|
| 8 | 30 | | SetSequenceAndExtension(); |
| 8 | 31 | | SetXaxis(); |
| 8 | 32 | | SetYaxis(); |
| | 33 | |
|
| 8 | 34 | | if (nestingGrids == null) |
| 2 | 35 | | NestingGrids = new NestingGrids(); |
| | 36 | | else |
| 6 | 37 | | NestingGrids = nestingGrids; |
| 8 | 38 | | } |
| | 39 | |
|
| | 40 | | [JsonConstructor] |
| | 41 | | /// <summary> |
| | 42 | | /// Create a new Grid. |
| | 43 | | /// </summary> |
| | 44 | | /// <param name="size">Grid size object.</param> |
| | 45 | | /// <param name="telescope">Vertical increment to use for a telescopic grid. |
| | 46 | | /// If 0.0 it will use equidistant grid.</param> |
| | 47 | | /// <param name="startTelescopeHeight">Start increment z dimension at.</param> |
| | 48 | | /// <param name="combineGridType">True to split the first cell.</param> |
| | 49 | | /// <param name="nestingGrids">Optional nesting grids.</param> |
| 11 | 50 | | public Grid(Size size, |
| 11 | 51 | | double telescope, |
| 11 | 52 | | double startTelescopeHeight, |
| 11 | 53 | | bool combineGridType, |
| 11 | 54 | | NestingGrids nestingGrids = null) |
| 11 | 55 | | { |
| 11 | 56 | | Size = size; |
| 11 | 57 | | Telescope = telescope; |
| 11 | 58 | | StartTelescopeHeight = startTelescopeHeight; |
| 11 | 59 | | CombineGridType = combineGridType; |
| | 60 | |
|
| 11 | 61 | | SetSequenceAndExtension(); |
| 11 | 62 | | SetXaxis(); |
| 11 | 63 | | SetYaxis(); |
| | 64 | |
|
| 11 | 65 | | if (nestingGrids == null) |
| 2 | 66 | | NestingGrids = new NestingGrids(); |
| | 67 | | else |
| 9 | 68 | | NestingGrids = nestingGrids; |
| 11 | 69 | | } |
| | 70 | |
|
| | 71 | | private double _telescope; |
| | 72 | | private double _startTelescopeHeight; |
| | 73 | |
|
| | 74 | | [DisplayName("Grid size")] |
| | 75 | | [Description("Settings of the grid unit")] |
| | 76 | | [JsonProperty("size", Required = Required.Always)] |
| | 77 | | /// <summary> |
| | 78 | | /// Grid size. |
| | 79 | | /// </summary> |
| 10938 | 80 | | public Size Size { get; } |
| | 81 | |
|
| | 82 | |
|
| | 83 | | [DisplayName("Nesting grids")] |
| | 84 | | [Description("Addictional grids for calculation scope.")] |
| | 85 | | [JsonProperty("nestingGrids")] |
| | 86 | | /// <summary> |
| | 87 | | /// Nesting grids. |
| | 88 | | /// </summary> |
| 25 | 89 | | public NestingGrids NestingGrids { get; set; } |
| | 90 | |
|
| | 91 | | [DisplayName("Telescope Factor")] |
| | 92 | | [Description("Set a telescopic grid.")] |
| | 93 | | [Range(0, double.MaxValue, ErrorMessage = "Only positive number allowed.")] |
| | 94 | | [JsonProperty("telescope")] |
| | 95 | | /// <summary> |
| | 96 | | /// Telescope value. |
| | 97 | | /// </summary> |
| | 98 | | public double Telescope |
| | 99 | | { |
| 105 | 100 | | get { return _telescope; } |
| | 101 | | set |
| 19 | 102 | | { |
| 19 | 103 | | if (value < 0.0 || value > 18.0) |
| 0 | 104 | | throw new ArgumentOutOfRangeException( |
| 0 | 105 | | $"{nameof(value)} must be between 0 and 24."); |
| | 106 | |
|
| 19 | 107 | | _telescope = value; |
| 19 | 108 | | } |
| | 109 | | } |
| | 110 | |
|
| | 111 | | [DisplayName("Start height")] |
| | 112 | | [Description("Set a telescopic grid at.")] |
| | 113 | | [Range(0, double.MaxValue, ErrorMessage = "Only positive number allowed.")] |
| | 114 | | [JsonProperty("startTelescopeHeight")] |
| | 115 | | /// <summary> |
| | 116 | | /// Start telescopic grid at. |
| | 117 | | /// </summary> |
| | 118 | | public double StartTelescopeHeight |
| | 119 | | { |
| 48 | 120 | | get { return _startTelescopeHeight; } |
| | 121 | | set |
| 19 | 122 | | { |
| 19 | 123 | | if (value < 0.0) |
| 0 | 124 | | throw new ArgumentOutOfRangeException( |
| 0 | 125 | | $"{nameof(value)} must be positive."); |
| | 126 | |
|
| 19 | 127 | | _startTelescopeHeight = value; |
| 19 | 128 | | } |
| | 129 | | } |
| | 130 | |
|
| | 131 | | /// <summary> |
| | 132 | | /// Convert to List of double. |
| | 133 | | /// </summary> |
| | 134 | | /// <returns>List of double.</returns> |
| | 135 | | public List<double[]> ToList() |
| 0 | 136 | | { |
| 0 | 137 | | var list = new List<double[]>(); |
| 0 | 138 | | foreach (var x in Xaxis) |
| 0 | 139 | | foreach (var y in Yaxis) |
| 0 | 140 | | foreach (var z in Zaxis) |
| 0 | 141 | | { |
| 0 | 142 | | list.Add(new[] { x, y, z }); |
| 0 | 143 | | } |
| | 144 | |
|
| 0 | 145 | | return list; |
| 0 | 146 | | } |
| | 147 | |
|
| | 148 | | [DisplayName("Split First Cell")] |
| | 149 | | [Description("If telescopic grid split first cell.")] |
| | 150 | | [JsonProperty("combineGridType")] |
| | 151 | | /// <summary> |
| | 152 | | /// Is telescopic grid + first cell splitted? |
| | 153 | | /// </summary> |
| 35 | 154 | | public bool CombineGridType { get; } |
| | 155 | |
|
| | 156 | | [JsonIgnore] |
| | 157 | | /// <summary> |
| | 158 | | /// X axis of the grid. |
| | 159 | | /// </summary> |
| 23 | 160 | | public double[] Xaxis { get; private set; } |
| | 161 | |
|
| | 162 | | [JsonIgnore] |
| | 163 | | /// <summary> |
| | 164 | | /// Y axis of the grid. |
| | 165 | | /// </summary> |
| 23 | 166 | | public double[] Yaxis { get; private set; } |
| | 167 | |
|
| | 168 | | [JsonIgnore] |
| | 169 | | /// <summary> |
| | 170 | | /// Z axis of the grid. |
| | 171 | | /// </summary> |
| 23 | 172 | | public double[] Zaxis { get; private set; } |
| | 173 | |
|
| | 174 | | [JsonIgnore] |
| | 175 | | /// <summary> |
| | 176 | | /// Height of cells. |
| | 177 | | /// </summary> |
| 61 | 178 | | public double[] SequenceZ { get; private set; } |
| | 179 | |
|
| | 180 | | [JsonIgnore] |
| | 181 | | /// <summary> |
| | 182 | | /// Is the grid splitted? |
| | 183 | | /// </summary> |
| 22 | 184 | | public bool IsSplitted { get; private set; } |
| | 185 | |
|
| | 186 | | /// <summary> |
| | 187 | | /// String representation of the grid. |
| | 188 | | /// </summary> |
| | 189 | | /// <returns>String representation.</returns> |
| | 190 | | public override string ToString() |
| 0 | 191 | | { |
| 0 | 192 | | return String.Format("Grid::Size {0},{1},{2}", Size.NumX, Size.NumY, Size.NumZ); |
| 0 | 193 | | } |
| | 194 | |
|
| | 195 | | private void SetXaxis() |
| 19 | 196 | | { |
| 19 | 197 | | double[] sequence = new double[Size.NumX]; |
| 3638 | 198 | | for (int i = 0; i < Size.NumX; i++) |
| 1800 | 199 | | sequence[i] = (Size.DimX * i) + Size.MinX; |
| 19 | 200 | | Xaxis = sequence; |
| 19 | 201 | | } |
| | 202 | |
|
| | 203 | | private void SetYaxis() |
| 19 | 204 | | { |
| 19 | 205 | | double[] sequence = new double[Size.NumY]; |
| 3638 | 206 | | for (int i = 0; i < Size.NumY; i++) |
| 1800 | 207 | | sequence[i] = (Size.DimY * i) + Size.MinY; |
| 19 | 208 | | Yaxis = sequence; |
| 19 | 209 | | } |
| | 210 | |
|
| | 211 | | private void SetSequenceAndExtension() |
| 19 | 212 | | { |
| 19 | 213 | | if (CombineGridType && Telescope > 0.0) |
| 9 | 214 | | { |
| 9 | 215 | | SequenceZ = GetCombinedSequence(Telescope, StartTelescopeHeight); |
| 9 | 216 | | IsSplitted = true; |
| 9 | 217 | | } |
| 10 | 218 | | else if (CombineGridType == false && Telescope > 0.0) |
| 1 | 219 | | { |
| 1 | 220 | | SequenceZ = GetTelescopeSequence(Telescope, StartTelescopeHeight); |
| 1 | 221 | | IsSplitted = false; |
| 1 | 222 | | } |
| | 223 | | else |
| 9 | 224 | | { |
| 9 | 225 | | SequenceZ = GetEquidistantSequence(); |
| 9 | 226 | | IsSplitted = true; |
| 9 | 227 | | } |
| | 228 | |
|
| 19 | 229 | | var accumulated = Util.Accumulate(SequenceZ) |
| 19 | 230 | | .ToArray(); |
| 494 | 231 | | Zaxis = accumulated.Zip(SequenceZ, (a, b) => a - (b / 2)) |
| 19 | 232 | | .ToArray(); |
| 19 | 233 | | } |
| | 234 | |
|
| | 235 | | public string Serialize() |
| 2 | 236 | | { |
| 2 | 237 | | return JsonConvert.SerializeObject(this); |
| 2 | 238 | | } |
| | 239 | |
|
| | 240 | | public static Grid Deserialize(string json) |
| 3 | 241 | | { |
| | 242 | | try |
| 3 | 243 | | { |
| 3 | 244 | | return JsonConvert.DeserializeObject<Grid>(json); |
| | 245 | | } |
| 1 | 246 | | catch (Exception e) |
| 1 | 247 | | { |
| 1 | 248 | | throw new Exception(e.Message); |
| | 249 | | } |
| 2 | 250 | | } |
| | 251 | |
|
| | 252 | | public bool Equals(Grid other) |
| 2 | 253 | | { |
| 2 | 254 | | if (other == null) |
| 0 | 255 | | return false; |
| | 256 | |
|
| 2 | 257 | | if (other != null |
| 2 | 258 | | && other.Size == this.Size |
| 2 | 259 | | && other.Telescope == other.Telescope |
| 2 | 260 | | && other.StartTelescopeHeight == other.StartTelescopeHeight |
| 2 | 261 | | && other.CombineGridType == other.CombineGridType |
| 2 | 262 | | && other.NestingGrids == other.NestingGrids) |
| 2 | 263 | | return true; |
| | 264 | | else |
| 0 | 265 | | return false; |
| 2 | 266 | | } |
| | 267 | |
|
| | 268 | | public override bool Equals(Object obj) |
| 10 | 269 | | { |
| 10 | 270 | | if (obj == null) |
| 0 | 271 | | return false; |
| | 272 | |
|
| 10 | 273 | | var gridObj = obj as Grid; |
| 10 | 274 | | if (gridObj == null) |
| 8 | 275 | | return false; |
| | 276 | | else |
| 2 | 277 | | return Equals(gridObj); |
| 10 | 278 | | } |
| | 279 | |
|
| | 280 | | public override int GetHashCode() |
| 0 | 281 | | { |
| | 282 | | unchecked |
| 0 | 283 | | { |
| 0 | 284 | | int hash = 17; |
| 0 | 285 | | hash = hash * 23 + Size.GetHashCode(); |
| 0 | 286 | | hash = hash * 23 + Telescope.GetHashCode(); |
| 0 | 287 | | hash = hash * 23 + StartTelescopeHeight.GetHashCode(); |
| 0 | 288 | | hash = hash * 23 + CombineGridType.GetHashCode(); |
| 0 | 289 | | hash = hash * 23 + NestingGrids.GetHashCode(); |
| 0 | 290 | | return hash; |
| | 291 | | } |
| 0 | 292 | | } |
| | 293 | |
|
| | 294 | | public static bool operator ==(Grid grid1, Grid grid2) |
| 14 | 295 | | { |
| 14 | 296 | | if (((object)grid1) == null || ((object)grid2) == null) |
| 14 | 297 | | return Object.Equals(grid1, grid2); |
| | 298 | |
|
| 0 | 299 | | return grid1.Equals(grid2); |
| 14 | 300 | | } |
| | 301 | |
|
| | 302 | | public static bool operator !=(Grid grid1, Grid grid2) |
| 2 | 303 | | { |
| 2 | 304 | | return !(grid1 == grid2); |
| 2 | 305 | | } |
| | 306 | |
|
| | 307 | | #region Sequence |
| | 308 | | private double[] GetEquidistantSequence() |
| 9 | 309 | | { |
| 9 | 310 | | var baseCell = Size.DimZ / 5; |
| 9 | 311 | | var cell = Size.DimZ; |
| | 312 | |
|
| 9 | 313 | | double[] sequence = new double[Size.NumZ]; |
| | 314 | |
|
| 468 | 315 | | for (int k = 0; k < sequence.Length; k++) |
| 225 | 316 | | { |
| 225 | 317 | | if (k < 5) |
| 45 | 318 | | sequence[k] = baseCell; |
| | 319 | | else |
| 180 | 320 | | sequence[k] = cell; |
| 225 | 321 | | } |
| | 322 | |
|
| 9 | 323 | | return sequence; |
| 9 | 324 | | } |
| | 325 | |
|
| | 326 | | private double[] GetTelescopeSequence(double telescope, double start) |
| 1 | 327 | | { |
| 1 | 328 | | var cell = Size.DimZ; |
| | 329 | |
|
| 1 | 330 | | double[] sequence = new double[Size.NumZ]; |
| | 331 | |
|
| 1 | 332 | | double val = cell; |
| | 333 | |
|
| 52 | 334 | | for (int k = 0; k < sequence.Length; k++) |
| 25 | 335 | | { |
| 25 | 336 | | if (val * k < start) |
| 2 | 337 | | { |
| 2 | 338 | | sequence[k] = cell; |
| 2 | 339 | | } |
| | 340 | | else |
| 23 | 341 | | { |
| 23 | 342 | | sequence[k] = val + (val * telescope / 100); |
| 23 | 343 | | val = sequence[k]; |
| 23 | 344 | | } |
| 25 | 345 | | } |
| | 346 | |
|
| 1 | 347 | | return sequence; |
| 1 | 348 | | } |
| | 349 | |
|
| | 350 | | private double[] GetCombinedSequence(double telescope, double start) |
| 9 | 351 | | { |
| 9 | 352 | | var cell = Size.DimZ; |
| 9 | 353 | | var baseCell = Size.DimZ / 5; |
| 9 | 354 | | double val = cell; |
| | 355 | |
|
| 9 | 356 | | double[] firstSequence = new double[5]; |
| 9 | 357 | | double[] sequence = new double[Size.NumZ - 1]; |
| | 358 | |
|
| 108 | 359 | | for (int k = 0; k < 5; k++) |
| 45 | 360 | | firstSequence[k] = baseCell; |
| | 361 | |
|
| 450 | 362 | | for (int k = 0; k < sequence.Length; k++) |
| 216 | 363 | | { |
| 216 | 364 | | if (val * (k + 1) < start) |
| 9 | 365 | | { |
| 9 | 366 | | sequence[k] = cell; |
| 9 | 367 | | } |
| | 368 | | else |
| 207 | 369 | | { |
| 207 | 370 | | sequence[k] = val + (val * telescope / 100); |
| 207 | 371 | | val = sequence[k]; |
| 207 | 372 | | } |
| 216 | 373 | | } |
| | 374 | |
|
| 9 | 375 | | double[] completeSequence = new double[sequence.Length + firstSequence.Length]; |
| | 376 | |
|
| 9 | 377 | | firstSequence.CopyTo(completeSequence, 0); |
| 9 | 378 | | sequence.CopyTo(completeSequence, firstSequence.Length); |
| 9 | 379 | | Array.Resize(ref completeSequence, sequence.Length + 1); |
| | 380 | |
|
| 9 | 381 | | return completeSequence; |
| 9 | 382 | | } |
| | 383 | | #endregion |
| | 384 | | } |
| | 385 | | } |