BOM preview in MFC

This section is a practical example of creating a “bill of materials preview” dialog in MFC. However, the techniques used are applicable to any GUI system.

This section describes how to create a subwindow which displays the component referenced by an entry in the BOM table.

Going back to the last section, when we created the IFCQuantityRecord, we could have just saved an instance count. However, instead we chose to save the paths of the instances. One way to use these paths is to highlight all the instances of the component in the main window.

For this tutorial, we’re going to use these paths, or at least one path to generate a view of a specific component, when we select it in the table.

../../_images/bom_preview.png

As we did for the plan view, we’re also going to use a subwindow. The main difference is that we’re not going to display the whole scene - just one component.

Just like the plan view, we’ve encapsulated the functionality in a class.

struct IFCQuantityRecord;

class IFCSamplePreview
{
        HPS::Canvas     _canvas;
        HPS::CADModel   _cadModel;
        HPS::SegmentKey _previewKey;
        HPS::IncludeKey _includedComponentKey;

public:
        IFCSamplePreview();
        ~IFCSamplePreview();

        bool    (HPS::Canvas canvas, HPS::CADModel cadModel);
        void DestroyPreview();
        void UpdatePreview( IFCQuantityRecord &qr);
};

The process consists of creating the window, destroying it when we’re finished, and updating its contents based passing an IFCQuantityRecord. Let’s look at the implementation.

CreatePreview

In our CreatePreview function, we select a solid background only for illustrative purposes.

bool IFCSamplePreview::CreatePreview(HPS::Canvas canvas, HPS::CADModel cadModel)
{
        _canvas = canvas;
        _cadModel = cadModel;

        // show a plan view window
        HPS::SegmentKey viewKey = _canvas.GetFrontView().GetSegmentKey();

        _previewKey = viewKey.Subsegment("previewView", true);

        HPS::SubwindowKit subwindowKit;
        subwindowKit.SetSubwindow(HPS::Rectangle(0.49f, 0.99f, -0.99f, -0.49),
                HPS::Subwindow::Type::Standard);
        subwindowKit.SetBorder(HPS::Subwindow::Border::InsetBold);
        subwindowKit.SetBackground(HPS::Subwindow::Background::SolidColor);

        _previewKey.SetSubwindow(subwindowKit);
        _previewKey.InsertDistantLight(HPS::Vector(0.5f, 0.5f, 0.5f));


        return true;
}

UpdatePreview

Most of the work takes place in the UpdatePreview function. We pass a quantity record to this function, get a segment key for the component, then add it to the view. Next we set up a camera that fits the component as closely as possible. All the functions used here we have already used elsewhere.

One thing to note. To keep this sample simple, we have made some assumptions about the component we’re rendering. First, we assume the component comes from a ProductOccurrence. Our “Add to BOM” functionality filters for these.

We also assume that all color information is contained locally. For the general case, it is possible that the visual appearance of an object might be affected by attributes inherited from its parents.

void IFCSamplePreview::UpdatePreview(IFCQuantityRecord &qr )
{
        if (_previewKey.Empty())
                return;

        HPS::ComponentPath cPath = qr._componentGroup[0]; // lets get one instance of a component
        HPS::KeyPathArray keyPaths = cPath.GetKeyPaths();
        HPS::KeyPath keyPath = keyPaths[0];
        HPS::SegmentKey segKey(keyPath.Front());

        if (!_includedComponentKey.Empty())
                _includedComponentKey.Delete();

        _includedComponentKey = _previewKey.IncludeSegment(segKey); // add it to this sub- window

        // now get a bound on the contents
        HPS::KeyPath newkeyPath;
        newkeyPath.PushBack(segKey);
        newkeyPath.PushBack(_previewKey);
        newkeyPath.PushBack(_canvas.GetFrontView().GetSegmentKey());

        HPS::BoundingKit bound;
        newkeyPath.ShowNetBounding(bound);

        HPS::SimpleSphere boundingSphere;
        HPS::SimpleCuboid boundingCuboid;

        bound.ShowVolume(boundingSphere, boundingCuboid);

        float scale = 1.75;

        float delta = boundingSphere.radius  * scale;

        HPS::Point camCenter(boundingSphere.center);
        HPS::Point camPos(camCenter.x + 1, camCenter.y + 1, camCenter.z + 1);

        HPS::CameraKit camKit;
        camKit.SetTarget(camCenter);
        camKit.SetPosition(camPos);
        camKit.SetField(delta, delta);
        camKit.SetUpVector(HPS::Vector(0, 0, 1));
        camKit.SetProjection(HPS::Camera::Projection::Orthographic);
        _previewKey.SetCamera(camKit);

        _canvas.GetFrontView().Update();
}