Jediah Lankitus
Jediah Lankitus

Reputation: 31

Autodesk Forge: Model Compare (BIM 360 Feature)

Bim 360 has a great tool called "compare versions" which allows the viewing of changes between two Revit models. More info can be found on the feature here:

Compare versions info

Is this feature found within Autodesk Forge? I haven't been able to locate it, I do see that forge has a BIM 360 API, but I don't see this feature available.

Upvotes: 1

Views: 660

Answers (2)

bimangle
bimangle

Reputation: 21

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

Upvotes: 2

Mikako Harada
Mikako Harada

Reputation: 646

No, at least now. Would be nice, isn't it? Maybe in future ...

Upvotes: 2

Related Questions