context: #region netDxf library licensed under the MIT License
  // 
  //                       netDxf library
  // Copyright (c) 2019-2021 Daniel Carvajal (haplokuon@gmail.com)
  // 
  // Permission is hereby granted, free of charge, to any person obtaining a copy
  // of this software and associated documentation files (the "Software"), to deal
  // in the Software without restriction, including without limitation the rights
  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  // copies of the Software, and to permit persons to whom the Software is
  // furnished to do so, subject to the following conditions:
  // 
  // The above copyright notice and this permission notice shall be included in all
  // copies or substantial portions of the Software.
  // 
  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  // SOFTWARE.
  // 
  #endregion
  
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using netDxf.Tables;
  
  namespace netDxf.Entities
  {
      /// <summary>
      /// Represents a polyface mesh <see cref="EntityObject">entity</see>.
      /// </summary>
      public class PolyfaceMesh :
          EntityObject
      {
          #region delegates and events
  
          public delegate void PolyfaceMeshFaceLayerChangedEventHandler(PolyfaceMesh sender, TableObjectChangedEventArgs<Layer> e);
          public event PolyfaceMeshFaceLayerChangedEventHandler PolyfaceMeshFaceLayerChanged;
          protected virtual Layer OnPolyfaceMeshFaceLayerChangedEvent(Layer oldLayer, Layer newLayer)
          {
              PolyfaceMeshFaceLayerChangedEventHandler ae = this.PolyfaceMeshFaceLayerChanged;
              if (ae != null)
              {
                  TableObjectChangedEventArgs<Layer> eventArgs = new TableObjectChangedEventArgs<Layer>(oldLayer, newLayer);
                  ae(this, eventArgs);
                  return eventArgs.NewValue;
              }
              return newLayer;
          }
  
          #endregion
  
          #region private fields
  
          private readonly PolyfaceMeshFace[] faces;
          private readonly Vector3[] vertexes;
          private readonly PolylineTypeFlags flags;
  
          #endregion
  
          #region constructors
  
          /// <summary>
          /// Initializes a new instance of the <c>PolyfaceMesh</c> class.
          /// </summary>
          /// <param name="vertexes">Polyface mesh <see cref="Vector3">vertex</see> list.</param>
          /// <param name="faces">Polyface mesh faces list, a maximum of 4 indexes per face.</param>
          public PolyfaceMesh(IEnumerable<Vector3> vertexes, IEnumerable<short[]> faces)
              : base(EntityType.PolyfaceMesh, DxfObjectCode.Polyline)
          {
              this.flags = PolylineTypeFlags.PolyfaceMesh;
              if (vertexes == null)
              {
                  throw new ArgumentNullException(nameof(vertexes));
              }
              this.vertexes = vertexes.ToArray();
              if (this.vertexes.Length < 3)
              {
                  throw new ArgumentOutOfRangeException(nameof(vertexes), this.vertexes.Length, "The polyface mesh faces list requires at least three points.");
              }
  
              if (faces == null)
              {
                  throw new ArgumentNullException(nameof(vertexes));
              }
  
              int numFaces = faces.Count();
              this.faces = new PolyfaceMeshFace[numFaces];
              for (int i = 0; i < numFaces; i++)
              {
                  this.faces[i] = new PolyfaceMeshFace(faces.ElementAt(i));
              }
              if (this.faces.Length < 1)
              {
                  throw new ArgumentOutOfRangeException(nameof(vertexes), this.faces.Length, "The polyface mesh faces list requires at least one face.");
              }
              foreach (PolyfaceMeshFace face in this.faces)
              {
                  face.LayerChanged += this.PolyfaceMeshFace_LayerChanged;
              }
          }
  
          /// <summary>
          /// Initializes a new instance of the <c>PolyfaceMesh</c> class.
          /// </summary>
          /// <param name="vertexes">Polyface mesh <see cref="Vector3">vertex</see> list.</param>
          /// <param name="faces">Polyface mesh faces list, a maximum of 4 indexes per face.</param>
          public PolyfaceMesh(IEnumerable<Vector3> vertexes, IEnumerable<PolyfaceMeshFace> faces)
              : base(EntityType.PolyfaceMesh, DxfObjectCode.Polyline)
          {
              this.flags = PolylineTypeFlags.PolyfaceMesh;
              if (vertexes == null)
              {
                  throw new ArgumentNullException(nameof(vertexes));
              }
              this.vertexes = vertexes.ToArray();
              if (this.vertexes.Length < 3)
              {
                  throw new ArgumentOutOfRangeException(nameof(vertexes), this.vertexes.Length, "The polyface mesh faces list requires at least three points.");
              }
  
              if (faces == null)
              {
                  throw new ArgumentNullException(nameof(vertexes));
              }
  
              this.faces = faces.ToArray();
              if (this.faces.Length < 1)
              {
                  throw new ArgumentOutOfRangeException(nameof(vertexes), this.faces.Length, "The polyface mesh faces list requires at least one face.");
              }
  
              foreach (PolyfaceMeshFace face in this.faces)
              {
                  face.LayerChanged += this.PolyfaceMeshFace_LayerChanged;
              }
          }
  
          #endregion
  
          #region public properties
  
          /// <summary>
          /// Gets or sets the polyface mesh <see cref="Vector3">vertexes</see>.
          /// </summary>
          public Vector3[] Vertexes
          {
              get { return this.vertexes; }
          }
  
          /// <summary>
          /// Gets or sets the polyface mesh <see cref="PolyfaceMeshFace">faces</see>.
          /// </summary>
          public IReadOnlyList<PolyfaceMeshFace> Faces
          {
              get { return this.faces; }
          }
  
          #endregion
  
          #region internal properties
  
          /// <summary>
          /// Gets the polyface mesh flag type.
          /// </summary>
          internal PolylineTypeFlags Flags
          {
              get { return this.flags; }
          }
  
          #endregion
  
          #region public methods
  
          /// <summary>
          /// Decompose the actual polyface mesh faces in <see cref="Point">points</see> (one vertex polyface mesh face),
          /// <see cref="Line">lines</see> (two vertexes polyface mesh face) and <see cref="Face3D">3d faces</see> (three or four vertexes polyface mesh face).
          /// </summary>
          /// <returns>A list of <see cref="Face3D">3d faces</see> that made up the polyface mesh.</returns>
          public List<EntityObject> Explode()
          {
              List<EntityObject> entities = new List<EntityObject>();
  
              foreach (PolyfaceMeshFace face in this.faces)
              {
                  AciColor faceColor = face.Color == null ? this.Color : face.Color;
                  Layer faceLayer = face.Layer == null ? this.Layer : face.Layer;
  
                  if (face.VertexIndexes.Length == 1)
                  {
                      Point point = new Point
                      {
                          Layer = (Layer) faceLayer.Clone(),
                          Linetype = (Linetype) this.Linetype.Clone(),
                          Color = (AciColor) faceColor.Clone(),
                          Lineweight = this.Lineweight,
                          Transparency = (Transparency) this.Transparency.Clone(),
                          LinetypeScale = this.LinetypeScale,
                          Normal = this.Normal,
                          Position = this.Vertexes[Math.Abs(face.VertexIndexes[0]) - 1],
                      };
                      entities.Add(point);
                      continue;
                  }
                  if (face.VertexIndexes.Length == 2)
                  {
                      Line line = new Line
                      {
                          Layer = (Layer) faceLayer.Clone(),
                          Linetype = (Linetype) this.Linetype.Clone(),
                          Color = (AciColor) faceColor.Clone(),
                          Lineweight = this.Lineweight,
                          Transparency = (Transparency) this.Transparency.Clone(),
                          LinetypeScale = this.LinetypeScale,
                          Normal = this.Normal,
                          StartPoint = this.Vertexes[Math.Abs(face.VertexIndexes[0]) - 1],
                          EndPoint = this.Vertexes[Math.Abs(face.VertexIndexes[1]) - 1],
                      };
                      entities.Add(line);
                      continue;
                  }
  
                  Face3DEdgeFlags edgeVisibility = Face3DEdgeFlags.None;
  
                  short indexV1 = face.VertexIndexes[0];
                  short indexV2 = face.VertexIndexes[1];
                  short indexV3 = face.VertexIndexes[2];
                  // Polyface mesh faces are made of 3 or 4 vertexes, we will repeat the third vertex if the number of face vertexes is three
                  int indexV4 = face.VertexIndexes.Length == 3 ? face.VertexIndexes[2] : face.VertexIndexes[3];
  
                  if (indexV1 < 0)
                  {
                      edgeVisibility |= Face3DEdgeFlags.First;
                  }
  
                  if (indexV2 < 0)
                  {
  
gt:                     edgeVisibility |= Face3DEdgeFlags.Second;