You can do this use the ForgeAuthor sdk do this.
ForgeAuthor is a .net library based on .net framework 4.5, it provides a nice object oriented way to manipulate the Audesk Forge Svf Model, as simple as manipulate XML or JSON.
The ForgeAuthor can read the Autodesk Forge SVF model, compare the differences, and generate the difference model.
#region setup materials
var matUnmodified = new Material
{
Color = Vector3D.FromColor(0xffffff), //Darker: Vector3D.FromColor(0x101010)
Transparent = 0.95,
Reflectivity = 0
};
var matAdd = new Material
{
Color = Vector3D.FromColor(Color.GreenYellow)
};
var matDelete = new Material
{
Color = Vector3D.FromColor(Color.Red)
};
var matModifiedBefore = new Material
{
Color = Vector3D.FromColor(Color.Orange),
Transparent = 0.5
};
var matModifiedAfter = new Material
{
Color = Vector3D.FromColor(Color.Aqua),
};
#endregion
var baseModelPath = txtBaseModel.Text;
var incrModelPath = txtIncrementModel.Text;
var diffModelPath = txtDiffModel.Text;
var svfbase = baseModelPath.EndsWith(@"zip")
? SvfDocument.LoadFromZipFile(baseModelPath)
: SvfDocument.LoadFromSvfFile(baseModelPath);
var svfincr = incrModelPath.EndsWith(@"zip")
? SvfDocument.LoadFromZipFile(incrModelPath)
: SvfDocument.LoadFromSvfFile(incrModelPath);
var compareResult = CompareModel(svfbase, svfincr);
var svfdiff = new SvfDocument();
svfdiff.Model.Name = @"Diff Model";
svfdiff.Metadata = svfbase.Metadata;
var nodeUnmodified = svfdiff.Model.Children.CreateNode();
nodeUnmodified.Name = @"Unmodified";
var nodeAdded = svfdiff.Model.Children.CreateNode();
nodeAdded.Name = @"Added";
var nodeDeleted = svfdiff.Model.Children.CreateNode();
nodeDeleted.Name = @"Deleted";
var nodeModifiedBefore = svfdiff.Model.Children.CreateNode();
nodeModifiedBefore.Name = @"Modified Before";
var nodeModifiedAfter = svfdiff.Model.Children.CreateNode();
nodeModifiedAfter.Name = @"Modified After";
svfbase.EnumerateNodes(node =>
{
if (node.Children?.Count == 0 &&
node.Fragments?.Count > 0 &&
string.IsNullOrEmpty(node.ExternalId) == false)
{
if (compareResult.Unmodified.Remove(node.ExternalId))
{
ImportNodeWithPath(nodeUnmodified, node, svfbase.Model, matUnmodified);
}
else if (compareResult.Deleted.Remove(node.ExternalId))
{
ImportNodeWithPath(nodeDeleted, node, svfbase.Model, matDelete);
}
else if (compareResult.Modified.Contains(node.ExternalId))
{
var targetNode =
ImportNodeWithPath(nodeModifiedBefore, node, svfbase.Model, matModifiedBefore);
targetNode.ExternalId += @"_Before";
}
}
}, svfbase.Model);
svfincr.EnumerateNodes(node =>
{
if (node.Children?.Count == 0 &&
node.Fragments?.Count > 0 &&
string.IsNullOrEmpty(node.ExternalId) == false)
{
if (compareResult.Added.Remove(node.ExternalId))
{
ImportNodeWithPath(nodeAdded, node, svfincr.Model, matAdd);
}
else if (compareResult.Modified.Remove(node.ExternalId))
{
ImportNodeWithPath(nodeModifiedAfter, node, svfincr.Model, matModifiedAfter);
}
}
}, svfincr.Model);
svfdiff.SaveToFolder(diffModelPath, true);
svfdiff.Dispose();
svfbase.Dispose();
svfincr.Dispose();
private (HashSet<string> Unmodified, HashSet<string> Added, HashSet<string> Deleted, HashSet<string> Modified)
CompareModel(SvfDocument svfbase, SvfDocument svfincr)
{
var baseNodes = new Dictionary<string, SvfNode>();
svfbase.EnumerateNodes(node =>
{
if (node.Children?.Count == 0
&& node.Fragments?.Count > 0
&& string.IsNullOrEmpty(node.ExternalId) == false)
{
baseNodes[node.ExternalId] = node;
}
}, svfbase.Model);
var elementsAdded = new HashSet<string>();
var elementsUnmodified = new HashSet<string>();
var elementsModified = new HashSet<string>();
var elementsDeleted = new HashSet<string>();
svfincr.EnumerateNodes(node =>
{
if (node.Children?.Count == 0
&& node.Fragments?.Count > 0
&& string.IsNullOrEmpty(node.ExternalId) == false)
{
if (baseNodes.TryGetValue(node.ExternalId, out SvfNode baseNode))
{
if (baseNode.Fragments.Equals(node.Fragments))
{
elementsUnmodified.Add(node.ExternalId); //unmdified
}
else
{
elementsModified.Add(node.ExternalId); //modified
}
baseNodes.Remove(node.ExternalId);
}
else
{
elementsAdded.Add(node.ExternalId); //added
}
}
}, svfincr.Model);
foreach (var p in baseNodes.Keys)
{
elementsDeleted.Add(p); //deleted
}
baseNodes.Clear();
return (elementsUnmodified, elementsAdded, elementsDeleted, elementsModified);
}
private SvfNode ImportNodeWithPath(SvfNode targetNodeRoot, SvfNode sourceNode, SvfNode sourceNodeRoot, Material material)
{
var targetNode = sourceNode.Parent == sourceNodeRoot
? targetNodeRoot
: ImportNodeWithPath(targetNodeRoot, sourceNode.Parent, sourceNodeRoot, material);
var resultNode = targetNode.Children.FirstOrDefault(x => x.Name == sourceNode.Name);
if (resultNode == null)
{
resultNode = targetNode.Children.ImportNode(sourceNode, null, false);
foreach (var fragment in resultNode.Fragments)
{
fragment.Material = material;
}
}
return resultNode;
}
snapshot