/// <summary> /// A new indexed star lookup wrapping a single body definition. /// </summary> /// <param name="body">The body to index</param> /// <param name="offset">An optional offset in vertex array</param> public FacetStars(FaceBody body, PtOffsetArray ptOffsetArray) { _index = new Dictionary <uint, List <Facet> >(); Body = body; PtOffsetArray = ptOffsetArray; Ingest(body); }
private void TestRunWith(PtArray ptArray, FaceBody body, FacetStarVisitor.InvestigationStrategy strategy) { var facetStar = new FacetStars(body, new PtOffsetArray(ptArray, SHIFT)); Assert.AreEqual(8, facetStar.Vertices.Count(), "8 vertices"); Assert.AreEqual(8, facetStar.Count, "8 stars"); // Use no visitor's index cache var visitor = new FacetStarVisitor(facetStar, (i) => false, strategy); var faces = new List <MeshPtOffsetArray>(); var facets = new HashSet <Facet>(); foreach (Facet f in visitor) { switch (visitor.Strategy) { case FacetStarVisitor.InvestigationStrategy.SameFaceFirst: if (!f.Meshed.Equals(faces.LastOrDefault())) { foreach (var known in faces.Take(faces.Count - 1)) { Assert.AreNotEqual(known, f.Meshed, "Face shouldn't be processed before"); } faces.Add(f.Meshed); Assert.IsTrue(visitor.IsNewFace); } else { Assert.IsFalse(visitor.IsNewFace); } break; } facets.Add(f); Assert.AreEqual(SHIFT, f.Shift); Assert.IsTrue(f.IsTriangle(), "Is Triangle"); Assert.IsTrue(f.IsValid(), "Is Valid"); } Assert.IsFalse(visitor.HasNextCandidate, "All facets have been processed"); Assert.AreEqual(12, facets.Count, "12 facets"); }
/// <summary> /// Runs tessselation with Xbim scene context /// </summary> /// <param name="model">The model to be exported</param> /// <param name="summary">The scene export summary</param> /// <param name="monitor">The progress emitter instance</param> /// <returns>An enumerable of tesselated product representations</returns> public IEnumerable <IfcProductSceneRepresentation> Tesselate(IModel model, IfcSceneExportSummary summary, CancelableProgressing monitor) { ReadGeometryStore(model, monitor); short[] excludeTypeId = ExcludeExpressType.Select(t => model.Metadata.ExpressTypeId(t.ExpressName)).ToArray(); Array.Sort(excludeTypeId); // Start reading the geometry store built before using (var gReader = model.GeometryStore.BeginRead()) { int totalCount = gReader.ShapeGeometries.Count(); int currentCount = 0; // Product label vs. Component and candidate shape labels var packageCache = new SortedDictionary <int, TesselationPackage>(); // Compute contexts ComputeContextTransforms(gReader, summary, ContextsCreateFromSettings(gReader, summary)); monitor?.NotifyProgressEstimateUpdate(totalCount); foreach (var geometry in gReader.ShapeGeometries) { if (monitor?.State.IsAboutCancelling ?? false) { monitor.State.MarkCanceled(); break; } currentCount++; monitor?.State.UpdateDone(currentCount, "Running tesselation..."); monitor?.NotifyOnProgressChange(); if (geometry.ShapeData.Length <= 0) { // No geometry continue; } var shapes = gReader.ShapeInstancesOfGeometry(geometry.ShapeLabel) .Where(i => 0 > Array.BinarySearch(excludeTypeId, i.IfcTypeId) && i.RepresentationType == XbimGeometryRepresentationType.OpeningsAndAdditionsIncluded); if (!shapes.Any()) { // No shape instances continue; } using (var ms = new MemoryStream(((IXbimShapeGeometryData)geometry).ShapeData)) { using (var br = new BinaryReader(ms)) { XbimShapeTriangulation tr = br.ReadShapeTriangulation(); foreach (XbimShapeInstance shape in shapes) { if (monitor?.State.IsAboutCancelling ?? false) { monitor.State.MarkCanceled(); break; } var product = model.Instances[shape.IfcProductLabel] as IIfcProduct; // Try first to find the referenced component TesselationPackage pkg; if (!packageCache.TryGetValue(shape.IfcProductLabel, out pkg)) { // New component ToDo shape tuple built from component and todo ShapeGeometryLabel pkg = new TesselationPackage(gReader.ShapeInstancesOfEntity(product)); packageCache[shape.IfcProductLabel] = pkg; } var ctx = summary.ContextOf(shape.RepresentationContext); if (null == ctx) { Logger?.LogWarning($"Shape of representation #{shape.RepresentationContext} of product #{product.EntityLabel} out of context scope. Skipped."); continue; } // Check for representation var representation = GetOrCreateRepresentation(summary, shape, pkg); if (!pkg.IsShapeGeometryDone(geometry)) { AppendVertices(representation, summary, tr.Vertices); } // TODO Use "bias" definition to adjust biased local offsets var body = new FaceBody { Material = new RefId { Nid = shape.StyleLabel > 0 ? shape.StyleLabel : shape.IfcTypeId * -1 }, Transform = CreateTransform(summary, shape), PtSet = (uint)representation.Points.Count - 1, }; foreach (var face in tr.Faces) { if (face.Indices.Count % 3 != 0) { throw new NotSupportedException("Expecting triangular meshes only"); } // Translate Xbim face definition var bodyFace = new Face { IsPlanar = face.IsPlanar, Mesh = new Mesh { Type = FacetType.TriMesh, Orient = Orientation.Ccw } }; switch (face.NormalCount) { case 0: // No normals at all break; case 1: // Single normal bodyFace.IsPlanar = true; face.Normals[0].Normal.AppendTo(bodyFace.Mesh.Normal); break; default: // No planar face if (face.NormalCount != face.Indices.Count) { throw new NotSupportedException($"Incorrect count of normals per face mesh (expecting {face.Indices.Count}, have {face.NormalCount}"); } foreach (var n in face.Normals.Select(n => n.Normal)) { n.AppendTo(bodyFace.Mesh.Normal); } break; } bodyFace.Mesh.Vertex.AddRange(face.Indices.Select(i => (uint)i)); body.Faces.Add(bodyFace); } // Add body to known component representation.Bodies.Add(body); // Remove geometry label from todo list pkg.RemoveDone(shape); // If no shape instances left if (pkg.IsDone) { yield return(pkg.ToSceneRepresentation(shape.IfcProductLabel)); packageCache.Remove(shape.IfcProductLabel); } } } } } // Return most recent if (packageCache.Count > 0) { Logger?.LogWarning($"Detected {packageCache.Count} unfinished geometry entries. Missing shapes."); foreach (var e in packageCache) { // Announce missing components even if unfinished due to some reason Logger?.LogWarning($"IfcProduct #{e.Key} misses {e.Value.CountOpenInstances} shape(s)."); yield return(e.Value.ToSceneRepresentation(e.Key)); } } } monitor?.NotifyOnProgressChange("Done tesselation."); }