Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions CadRevealComposer.Exe/CommandLineOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,27 @@ public class CommandLineOptions
)]
public string? NodeNameExcludeRegex { get; init; } = null;

[Option(
longName: "PrioritizedDiscipline",
Required = false,
HelpText = "A regex matching disciplines to be prioritized in sector splitting. Not case sensitive."
)]
public string? PrioritizedDisciplineRegex { get; init; } = "PIPE";

[Option(
longName: "LowPrioritizedDiscipline",
Required = false,
HelpText = "A regex matching disciplines to be prioritized lower in sector splitting. Not case sensitive."
)]
public string? LowPrioritizedDisciplineRegex { get; init; } = null;

[Option(
longName: "PrioritizedNodeName",
Required = false,
HelpText = "A regex matching node names to be prioritized in sector splitting. Not case sensitive."
)]
public string? PrioritizedNodeNameRegex { get; init; } = null;

[Option(longName: "SplitIntoZones", shortName: 'z', Required = false, HelpText = "Split models into zones.")]
public bool SplitIntoZones { get; init; }

Expand Down
30 changes: 30 additions & 0 deletions CadRevealComposer.Exe/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,41 @@ private static int RunOptionsAndReturnExitCode(CommandLineOptions options)
);
}

if (options.PrioritizedDisciplineRegex != null)
{ // Ensure regex is valid.
if (!RegexUtils.IsValidRegex(options.PrioritizedDisciplineRegex))
throw new ArgumentException(
$"The {nameof(options.PrioritizedDisciplineRegex)} is not a valid regex. Check its syntax. "
+ $"The input was: {options.PrioritizedDisciplineRegex}"
);
}

if (options.LowPrioritizedDisciplineRegex != null)
{ // Ensure regex is valid.
if (!RegexUtils.IsValidRegex(options.LowPrioritizedDisciplineRegex))
throw new ArgumentException(
$"The {nameof(options.LowPrioritizedDisciplineRegex)} is not a valid regex. Check its syntax. "
+ $"The input was: {options.LowPrioritizedDisciplineRegex}"
);
}

if (options.PrioritizedNodeNameRegex != null)
{ // Ensure regex is valid.
if (!RegexUtils.IsValidRegex(options.PrioritizedNodeNameRegex))
throw new ArgumentException(
$"The {nameof(options.PrioritizedNodeNameRegex)} is not a valid regex. Check its syntax. "
+ $"The input was: {options.PrioritizedNodeNameRegex}"
);
}

var toolsParameters = new ComposerParameters(
options.NoInstancing,
options.SingleSector,
options.SplitIntoZones,
new NodeNameExcludeRegex(options.NodeNameExcludeRegex),
new PrioritizedDisciplinesRegex(options.PrioritizedDisciplineRegex),
new LowPrioritizedDisciplineRegex(options.LowPrioritizedDisciplineRegex),
new PrioritizedNodeNamesRegex(options.PrioritizedNodeNameRegex),
options.SimplificationThreshold,
options.DevPrimitiveCacheFolder
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,5 @@ public void InitialCameraPositionLongestAxisY()
}

private record TestPrimitiveWithBoundingBox(Vector3 Min, Vector3 Max)
: APrimitive(int.MaxValue, Color.Red, new BoundingBox(Min, Max));
: APrimitive(int.MaxValue, Color.Red, new BoundingBox(Min, Max), NodePriority.Default);
}
49 changes: 39 additions & 10 deletions CadRevealComposer.Tests/Operations/Splitting/SplittingUtilsTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
namespace CadRevealComposer.Tests.Operations.Splitting;

using System.Numerics;
using CadRevealComposer.Operations;
using CadRevealComposer.Operations.SectorSplitting;
using NUnit.Framework.Legacy;

Expand All @@ -12,10 +13,17 @@ public void ReturnsCorrectlySplitNodes()
{
Node[] nodes = new Node[]
{
new Node(4, null, 0, 0, new BoundingBox(Vector3.One, Vector3.Zero)),
new Node(2, null, 0, 0, new BoundingBox(Vector3.Zero, Vector3.One)),
new Node(4, null, 0, 0, new BoundingBox(new Vector3(99, 99, 99), new Vector3(100, 100, 100))),
new Node(6, null, 0, 0, new BoundingBox(Vector3.One, Vector3.One))
new Node(4, null, 0, 0, new BoundingBox(Vector3.One, Vector3.Zero), NodePriority.Default),
new Node(2, null, 0, 0, new BoundingBox(Vector3.Zero, Vector3.One), NodePriority.Default),
new Node(
4,
null,
0,
0,
new BoundingBox(new Vector3(99, 99, 99), new Vector3(100, 100, 100)),
NodePriority.Default
),
new Node(6, null, 0, 0, new BoundingBox(Vector3.One, Vector3.One), NodePriority.Default)
};

(Node[] regularNodes, Node[] outlierNodes) = nodes.SplitNodesIntoRegularAndOutlierNodes();
Expand All @@ -30,9 +38,16 @@ public void Splitting_ReturnsOutlierSectors()
{
Node[] nodes = new Node[]
{
new Node(4, null, 0, 0, new BoundingBox(Vector3.One, Vector3.Zero)),
new Node(4, null, 0, 0, new BoundingBox(new Vector3(99, 99, 99), new Vector3(100, 100, 100))),
new Node(6, null, 0, 0, new BoundingBox(Vector3.One, Vector3.One))
new Node(4, null, 0, 0, new BoundingBox(Vector3.One, Vector3.Zero), NodePriority.Default),
new Node(
4,
null,
0,
0,
new BoundingBox(new Vector3(99, 99, 99), new Vector3(100, 100, 100)),
NodePriority.Default
),
new Node(6, null, 0, 0, new BoundingBox(Vector3.One, Vector3.One), NodePriority.Default)
};

var groups = SplittingUtils.GroupOutliersRecursive(nodes, 10f);
Expand All @@ -45,9 +60,23 @@ public void Splitting_ReturnsOutlierSectorsWhenSymmetrical()
{
Node[] nodes = new Node[]
{
new Node(4, null, 0, 0, new BoundingBox(Vector3.Zero, Vector3.Zero)),
new Node(4, null, 0, 0, new BoundingBox(new Vector3(90, 90, 90), new Vector3(100, 100, 100))),
new Node(4, null, 0, 0, new BoundingBox(new Vector3(-90, -90, -90), new Vector3(-100, -100, -100))),
new Node(4, null, 0, 0, new BoundingBox(Vector3.Zero, Vector3.Zero), NodePriority.Default),
new Node(
4,
null,
0,
0,
new BoundingBox(new Vector3(90, 90, 90), new Vector3(100, 100, 100)),
NodePriority.Default
),
new Node(
4,
null,
0,
0,
new BoundingBox(new Vector3(-90, -90, -90), new Vector3(-100, -100, -100)),
NodePriority.Default
),
};

var groups = SplittingUtils.GroupOutliersRecursive(nodes, 10f);
Expand Down
109 changes: 107 additions & 2 deletions CadRevealComposer/CadRevealComposerRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.IO;
using System.Linq;
Expand Down Expand Up @@ -53,6 +54,11 @@ IReadOnlyList<IModelFormatProvider> modelFormatProviders
var instanceIdGenerator = new InstanceIdGenerator();

var filtering = new NodeNameFiltering(composerParameters.NodeNameExcludeRegex);
var nodePriorityFiltering = new PriorityMapping(
composerParameters.PrioritizedDisciplinesRegex,
composerParameters.LowPrioritizedDisciplineRegex,
composerParameters.PrioritizedNodeNamesRegex
);

ModelMetadata metadataFromAllFiles = new ModelMetadata(new());
foreach (IModelFormatProvider modelFormatProvider in modelFormatProviders)
Expand All @@ -62,7 +68,8 @@ IReadOnlyList<IModelFormatProvider> modelFormatProviders
inputFolderPath.EnumerateFiles(),
treeIndexGenerator,
instanceIdGenerator,
filtering
filtering,
nodePriorityFiltering
);

if (generalMetadata != null)
Expand All @@ -81,7 +88,6 @@ IReadOnlyList<IModelFormatProvider> modelFormatProviders
continue;
}

// collect all nodes for later sector division of the entire scene
nodesToExport.AddRange(cadRevealNodes);

var inputGeometries = cadRevealNodes.AsParallel().AsOrdered().SelectMany(x => x.Geometries).ToArray();
Expand Down Expand Up @@ -168,6 +174,105 @@ ComposerParameters composerParameters
stopwatch.Restart();
}

private static void PrintSectorStats(ImmutableArray<SceneCreator.SectorInfo> sectorsWithDownloadSize)
{
// Helpers
static float BytesToMegabytes(long bytes) => bytes / 1024f / 1024f;

(string, string, string, string, string, string, string, string, string, string) headers = (
"Depth",
"Sectors",
"μ drawCalls",
"μ Triangles",
"μ sectDiam",
"^ sectDiam",
"v sectDiam",
"μ s/l part",
"μ DLsize",
"v DLsize"
);
// Add stuff you would like for a quick overview here:
using (new TeamCityLogBlock("Sector Stats"))
{
Console.WriteLine($"Sector Count: {sectorsWithDownloadSize.Length}");
Console.WriteLine(
$"Sum all sectors .glb size megabytes: {BytesToMegabytes(sectorsWithDownloadSize.Sum(x => x.DownloadSize)):F2}MB"
);
Console.WriteLine(
$"Total Estimated Triangle Count: {sectorsWithDownloadSize.Sum(x => x.EstimatedTriangleCount)}"
);
Console.WriteLine($"Depth Stats:");
Console.WriteLine(
$"|{headers.Item1, 5}|{headers.Item2, 7}|{headers.Item3, 10}|{headers.Item4, 11}|{headers.Item5, 10}|{headers.Item6, 10}|{headers.Item7, 10}|{headers.Item8, 17}|{headers.Item9, 10}|{headers.Item10, 8}|"
);
Console.WriteLine(new String('-', 110));
foreach (
IGrouping<long, SceneCreator.SectorInfo> g in sectorsWithDownloadSize
.GroupBy(x => x.Depth)
.OrderBy(x => x.Key)
)
{
var anyHasGeometry = g.Any(x => x.Geometries.Any());
var sizeMinAvgExceptEmpty = anyHasGeometry
? g.Where(x => x.Geometries.Any()).Average(x => x.MinNodeDiagonal)
: 0;
var sizeMaxAvgExceptEmpty = anyHasGeometry
? g.Where(x => x.Geometries.Any()).Average(x => x.MaxNodeDiagonal)
: 0;
var maxSize = "N/A";
if (g.Count() > 1)
{
maxSize = $"{BytesToMegabytes(g.Max(x => x.DownloadSize)):F2}";
}

var formatted = $@"|
{g.Key, 5}|
{g.Count(), 7}|
{g.Average(x => x.EstimatedDrawCalls), 11:F2}|
{g.Average(x => x.EstimatedTriangleCount), 11:F0}|
{g.Average(x => x.SubtreeBoundingBox.Diagonal), 9:F2}m|
{g.Min(x => x.SubtreeBoundingBox.Diagonal), 9:F2}m|
{g.Max(x => x.SubtreeBoundingBox.Diagonal), 9:F2}m|
{sizeMinAvgExceptEmpty, 7:F2}m/{sizeMaxAvgExceptEmpty, 7:F2}m|
{g.Average(x => x.DownloadSize / 1024f / 1024f), 8:F}MB|
{maxSize, 8}|
".Replace(Environment.NewLine, "");
Console.WriteLine(formatted);
}
}
}

private static SceneCreator.SectorInfo SerializeSector(InternalSector p, string outputDirectory)
{
var sectorFilename = p.Geometries.Any() ? $"sector_{p.SectorId}.glb" : null;

if (p.Prioritized && sectorFilename != null)
{
sectorFilename = $"pri_{sectorFilename}";
}

var (estimatedTriangleCount, estimatedDrawCalls) = DrawCallEstimator.Estimate(p.Geometries);

var sectorInfo = new SceneCreator.SectorInfo(
SectorId: p.SectorId,
ParentSectorId: p.ParentSectorId,
Depth: p.Depth,
Path: p.Path,
Filename: sectorFilename,
EstimatedTriangleCount: estimatedTriangleCount,
EstimatedDrawCalls: estimatedDrawCalls,
MinNodeDiagonal: p.MinNodeDiagonal,
MaxNodeDiagonal: p.MaxNodeDiagonal,
Geometries: p.Geometries,
SubtreeBoundingBox: p.SubtreeBoundingBox,
GeometryBoundingBox: p.GeometryBoundingBox
);

if (sectorFilename != null)
SceneCreator.ExportSectorGeometries(sectorInfo.Geometries, sectorFilename, outputDirectory);
return sectorInfo;
}

/// <summary>
/// Writes the input parameters to a file to easier replicate a run.
/// </summary>
Expand Down
9 changes: 9 additions & 0 deletions CadRevealComposer/Configuration/ComposerParameters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,17 @@ public record ComposerParameters(
bool SingleSector,
bool SplitIntoZones,
NodeNameExcludeRegex NodeNameExcludeRegex,
PrioritizedDisciplinesRegex PrioritizedDisciplinesRegex,
LowPrioritizedDisciplineRegex LowPrioritizedDisciplineRegex,
PrioritizedNodeNamesRegex PrioritizedNodeNamesRegex,
float SimplificationThreshold,
DirectoryInfo? DevPrimitiveCacheFolder
);

public record NodeNameExcludeRegex(string? Value);

public record PrioritizedDisciplinesRegex(string? Value);

public record LowPrioritizedDisciplineRegex(string? Value);

public record PrioritizedNodeNamesRegex(string? Value);
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ public interface IModelFormatProvider
IEnumerable<FileInfo> filesToParse,
TreeIndexGenerator treeIndexGenerator,
InstanceIdGenerator instanceIdGenerator,
NodeNameFiltering nodeNameFiltering
NodeNameFiltering nodeNameFiltering,
PriorityMapping priorityMapping
);

public APrimitive[] ProcessGeometries(
Expand Down
62 changes: 62 additions & 0 deletions CadRevealComposer/Operations/PriorityMapping.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
namespace CadRevealComposer.Operations;

using Configuration;
using Microsoft.EntityFrameworkCore.Update;
using System.Text.RegularExpressions;

public enum NodePriority
{
High,
Medium,
Low, // Less than default
Default
}

public class PriorityMapping
{
private readonly Regex? _disciplineRegex;
private readonly Regex? _lowDisciplineRegex;
private readonly Regex? _nodeNameRegex;

public PriorityMapping(
PrioritizedDisciplinesRegex disciplineRegex,
LowPrioritizedDisciplineRegex lowDisciplineRegex,
PrioritizedNodeNamesRegex nodeNameRegex
)
{
if (disciplineRegex.Value != null)
{
_disciplineRegex = new Regex(disciplineRegex.Value, RegexOptions.IgnoreCase);
}

if (lowDisciplineRegex.Value != null)
{
_lowDisciplineRegex = new Regex(lowDisciplineRegex.Value, RegexOptions.IgnoreCase);
}

if (nodeNameRegex.Value != null)
{
_nodeNameRegex = new Regex(nodeNameRegex.Value, RegexOptions.IgnoreCase);
}
}

public NodePriority GetPriority(string discipline)
{
// TODO
// if (_nodeNameRegex != null && _nodeNameRegex.IsMatch(nodeName))
// return NodePriority.High;

if (_disciplineRegex != null && _disciplineRegex.IsMatch(discipline))
return NodePriority.Medium;

// TODO
//if (_lowDisciplineRegex != null && _lowDisciplineRegex.IsMatch(discipline))
// return NodePriority.Low;

// Hardcoded low priority on STRU for testing
//if (discipline.Equals("STRU"))
// return NodePriority.Low;

return NodePriority.Default;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ public record InternalSector(
float MaxNodeDiagonal,
APrimitive[] Geometries,
BoundingBox SubtreeBoundingBox,
BoundingBox? GeometryBoundingBox
BoundingBox? GeometryBoundingBox,
bool Prioritized = false
);
Loading