9.7 Integrating Visualize with Parasolid and HOOPS Exchange


9.7.10 Boolean Operations

Visualize supports the subtraction boolean operation via the native Parasolid API. To perform a subtraction in a scene, we'll load two CAD models into a scene, defining one of them as a target (the 3D model we'll be subtracting from) and the other as a tool (the 3D shape that will define the bounds of the subtraction operation). In this example, we'll load an engine model as our target and then load a cylinder to serve as the tool for the subtraction.

Please note, this is a very basic sample boolean subtraction operator, merely a starting point from which to build a more robust boolean operator leveraging Parasolid. Specifically, this operator implementation does not address the case in which a boolean operation would result in multiple bodies (although the comments in the operator code explain how to address that eventuality).

CAD model of an engine that will be used as the target for a boolean operation
CAD model of a cylinder that will be used
as the tool for a boolean operation
 

The full code for importing two CAD models into a scene is available below, and, for the most part, we'rejust using a standard approach for importing files in Visualize. However, please note that we're using a Exchange::ImportOptionsKit:SetLocation() to import our second model (the tool) into the same Component tree as the first file.

In addition, please note that – in terms of positioning – our models have been designed to overlap each other in World Space to facilitate the subtraction operation. If you want to use two models that weren't necessarily designed for this purpose, there are a variety of approaches for positioning them on top of each other to perform the boolean operation. For example, using the Handles Operator is an easy way to manually super-impose one model on top of another.

Once loaded, our two CAD models will look like this, with the cylinder piercing the engine model on the left:

Two intersecting CAD models loaded via the Exchange sprocket

Defining a custom operator

Perhaps the simplest method for implementing a boolean operation in Visualize is to define a custom operator that handles a series of mouse clicks for defining the target and tool. The full source code for the class is available at the bottom of the page, but for now we'll just focus on the OnMouseUp method, since that's where the bulk of the logic is contained. (More info about custom operators is available in this section.)

We'll call our custom operator class TestBooleanOperator, and like all custom operators, it inherits the HPS::Operator class. In this example, we'll overload the Operator::OnMouseUp() method, and in the overloaded method we'll place the logic for selecting the target and tool for the boolean subtraction operation:

[snippet 9.7.10.a]
bool OnMouseUp(HPS::MouseState const & in_state) override
{
if (!IsMouseTriggered(in_state) && in_state.GetLocation() == click_location)
{
size_t count = canvas.GetWindowKey().GetSelectionControl().SelectByPoint(click_location, selection_options, results);
if (count == 0)
return false;
while (s_it.IsValid())
{
HPS::KeyPath selected_path;
s_it.GetItem().ShowPath(selected_path);
HPS::ComponentPath component_path = cad_model.GetComponentPath(selected_path);
HPS::ComponentArray components = component_path.GetComponents();
for (auto it = components.rbegin(), e = components.rend(); it != e; ++it)
{
if (it->GetComponentType() == HPS::Component::ComponentType::ParasolidTopoBody)
{
HPS::Parasolid::Component para_component(*it);
auto highlight_control = canvas.GetWindowKey().GetHighlightControl();
PK_ENTITY_t selected_parasolid_entity = para_component.GetParasolidEntity();
if (target == selected_parasolid_entity)
{
//deselect target
highlight_control.Unhighlight(selected_path, target_highlight);
canvas.Update();
target = PK_ENTITY_null;
target_component.Reset();
target_path.Reset();
return true;
}
else if (tool == selected_parasolid_entity)
{
//deselect tool
highlight_control.Unhighlight(selected_path, tool_highlight);
canvas.Update();
tool = PK_ENTITY_null;
tool_component.Reset();
tool_path.Reset();
return true;
}
else if (target == PK_ENTITY_null)
{
target = selected_parasolid_entity;
target_component = *it;
target_path = selected_path;
highlight_control.Highlight(selected_path, target_highlight);
canvas.Update();
return true;
}
else
{
tool = selected_parasolid_entity;
tool_component = *it;
tool_path = selected_path;
highlight_control.Highlight(selected_path, tool_highlight);
canvas.Update();
return true;
}
}
}
s_it.Next();
}
return true;
}
return false;
}
public override bool OnMouseUp(MouseState in_state)
{
if (!IsMouseTriggered(in_state) && in_state.GetLocation() == click_location)
{
ulong count = canvas.GetWindowKey().GetSelectionControl().SelectByPoint(click_location, selection_options, out results);
if (count == 0)
return false;
HPS.SelectionResultsIterator s_it = results.GetIterator();
while (s_it.IsValid())
{
HPS.KeyPath selected_path;
s_it.GetItem().ShowPath(out selected_path);
HPS.ComponentPath component_path = cad_model.GetComponentPath(selected_path);
HPS.Component[] components = component_path.GetComponents();
foreach (HPS.Component it in components)
{
if (it.GetComponentType() == HPS.Component.ComponentType.ParasolidTopoBody)
{
HPS.Parasolid.Component para_component = new HPS.Parasolid.Component(it);
HPS.HighlightControl highlight_control = canvas.GetWindowKey().GetHighlightControl();
PK.ENTITY_t selected_parasolid_entity = para_component.GetParasolidEntity();
if (target == selected_parasolid_entity)
{
//deselect target
highlight_control.Unhighlight(selected_path, target_highlight);
canvas.Update();
target = PK.ENTITY_t.@null;
target_component.Reset();
target_path.Reset();
return true;
}
else if (tool == selected_parasolid_entity)
{
//deselect tool
highlight_control.Unhighlight(selected_path, tool_highlight);
canvas.Update();
tool = PK.ENTITY_t.@null;
tool_component.Reset();
tool_path.Reset();
return true;
}
else if (target == PK.ENTITY_t.@null)
{
target = selected_parasolid_entity;
target_component = it;
target_path = selected_path;
highlight_control.Highlight(selected_path, target_highlight);
canvas.Update();
return true;
}
else
{
tool = selected_parasolid_entity;
tool_component = it;
tool_path = selected_path;
highlight_control.Highlight(selected_path, tool_highlight);
canvas.Update();
return true;
}
}
}
s_it.Next();
}
return true;
}
return false;
}



The Parasolid Body upon which the user clicks first will be selected as the target, and the second body selected by the user will be the tool for the boolean operation:

The target is selected and highlighted in green

The tool is selected and highlighted in orange

Instantiating the custom operator

With our custom operator class defined, in our application code (provided in full below) we can now instantiate our custom operator and push it onto the operator stack in the Default priority queue:

[snippet 9.7.10.b]
TestBooleanOperator * boolean_operator = new TestBooleanOperator(canvas, cadmodel);
view.GetOperatorControl().Push(boolean_operator, HPS::Operator::Priority::Default);
TestBooleanOperator boolean_operator = new TestBooleanOperator(ref myCanvas, ref cadmodel);
view.GetOperatorControl().Push(boolean_operator, HPS.Operator.Priority.Default);



This instance of TestBooleanOperator is now available in the operator stack, and we'll learn how to retrieve it in the next section. (For more details about the operator stack, please see this section.)

Once our TestBooleanOperator instance has been pushed to the operator stack, Visualize takes ownership of its memory, so do not call delete on this instance, as it will be automatically freed by Visualize.

Retrieving the operator from the stack

Suppose that after we've pushed our original instance of TestBooleanOperator onto the operator stack, we want to access its member methods – in this case, specifically the ApplyBoolean() method.

In order to retrieve our custom operator from the Default priority operator stack, we can use the following approach:

[snippet 9.7.10.c]
OperatorPtr myOpPointer;
myCanvas.GetFrontView().GetOperatorControl().ShowTop(myOpPointer);
if (myOpPointer->GetName() == "TestBooleanOperator")
{
auto op = std::static_pointer_cast<TestBooleanOperator>(myOpPointer);
op->ApplyBoolean();
myCanvas.UpdateWithNotifier().Wait();
}
HPS.Operator myOperator = new HPS.Operator();
myCanvas.GetFrontView().GetOperatorControl().ShowTop(out myOperator);
if (myOperator.GetName() == "TestBooleanOperator")
{
TestBooleanOperator op = (TestBooleanOperator)myOperator;
op.ApplyBoolean();
myCanvas.UpdateWithNotifier().Wait();
}



We can retrieve a pointer to the operator by calling ShowTop(), passing a newly instantiated OperatorPtr as an out parameter to be populated by the function.

At this point, however, in order to gain access to the methods in our derived TestBooleanOperator class we'll need to create a new pointer to our derived type and use a static_pointer_cast to transform our OperatorPtr to a shared pointer of TestBooleanOperator type.

Now that we have a TestBooleanOperator pointer, we can access its methods and call the ApplyBoolean() function, which will call Parasolid's boolean subtraction function, removing from the target any overlapping volume, as seen in the image below:

Final result of our boolean subtraction operation

After the data has been changed in Parasolid, Visualize needs to be updated. This update can be accomplished by using the three functions: Tessellate() (for geometry that was modified, like the target), Delete() (for geometry that was deleted, like the tool), and AddEntity() (for geometry that was created, which would happen in the case where a boolean operation splits an entity in two, for example).

The source code below demonstrates Tessellate() and Delete(), and this section demonstrates how to use the AddEntity() function.

Full Source for Custom Boolean Operator

TestBooleanOperator derived class

[snippet 9.7.10.appendix_1]
class TestBooleanOperator : public HPS::Operator
{
public:
TestBooleanOperator(Canvas const & in_canvas, CADModel const & in_cad_model)
: canvas(in_canvas)
, cad_model(in_cad_model)
, target(PK_ENTITY_null)
, tool(PK_ENTITY_null)
{ }
virtual ~TestBooleanOperator()
{
}
HPS::UTF8 GetName() const override
{
return "TestBooleanOperator";
}
bool OnMouseDown(HPS::MouseState const & in_state) override
{
if (IsMouseTriggered(in_state))
{
click_location = in_state.GetLocation();
}
return false;
}
bool OnMouseUp(HPS::MouseState const & in_state) override
{
if (!IsMouseTriggered(in_state) && in_state.GetLocation() == click_location)
{
size_t count = canvas.GetWindowKey().GetSelectionControl().SelectByPoint(click_location, selection_options, results);
if (count == 0)
return false;
while (s_it.IsValid())
{
HPS::KeyPath selected_path;
s_it.GetItem().ShowPath(selected_path);
HPS::ComponentPath component_path = cad_model.GetComponentPath(selected_path);
HPS::ComponentArray components = component_path.GetComponents();
for (auto it = components.rbegin(), e = components.rend(); it != e; ++it)
{
if (it->GetComponentType() == HPS::Component::ComponentType::ParasolidTopoBody)
{
HPS::Parasolid::Component para_component(*it);
auto highlight_control = canvas.GetWindowKey().GetHighlightControl();
PK_ENTITY_t selected_parasolid_entity = para_component.GetParasolidEntity();
if (target == selected_parasolid_entity)
{
//deselect target
highlight_control.Unhighlight(selected_path, target_highlight);
canvas.Update();
target = PK_ENTITY_null;
target_component.Reset();
target_path.Reset();
return true;
}
else if (tool == selected_parasolid_entity)
{
//deselect tool
highlight_control.Unhighlight(selected_path, tool_highlight);
canvas.Update();
tool = PK_ENTITY_null;
tool_component.Reset();
tool_path.Reset();
return true;
}
else if (target == PK_ENTITY_null)
{
target = selected_parasolid_entity;
target_component = *it;
target_path = selected_path;
highlight_control.Highlight(selected_path, target_highlight);
canvas.Update();
return true;
}
else
{
tool = selected_parasolid_entity;
tool_component = *it;
tool_path = selected_path;
highlight_control.Highlight(selected_path, tool_highlight);
canvas.Update();
return true;
}
}
}
s_it.Next();
}
return true;
}
return false;
}
void OnViewAttached(HPS::View const & in_attached_view) override
{
attached_view = GetAttachedView();
HPS::SprocketPath sprocket_path(canvas, canvas.GetAttachedLayout(), attached_view, attached_view.GetAttachedModel());
.SetScope(sprocket_path.GetKeyPath());
//Create target style
target_style_source = HPS::Database::CreateRootSegment();
HPS::MaterialKit highlight_material;
highlight_material.SetDiffuse(HPS::RGBAColor(0, 0.8f, 0.0f, 0.3f));
highlight_material.SetSpecular(HPS::RGBColor(1.0f, 0.847f, 0.0f));
target_style_source.GetMaterialMappingControl()
.SetFaceMaterial(highlight_material)
.SetLineColor(HPS::RGBColor(0, 0.8f, 0));
HPS::PortfolioKey portfolio = attached_view.GetPortfolioKey();
portfolio.DefineNamedStyle("BooleanOperator_TargetStyle", target_style_source);
target_highlight.SetOverlay(HPS::Drawing::Overlay::None).SetStyleName("BooleanOperator_TargetStyle");
//Create tool style
tool_style_source = HPS::Database::CreateRootSegment();
highlight_material.SetDiffuse(HPS::RGBAColor(1.0f, 0.647f, 0.0f, 1.0f));
tool_style_source.GetMaterialMappingControl()
.SetFaceMaterial(highlight_material)
.SetEdgeColor(HPS::RGBColor(1.0f, 0.510f, 0.3f))
.SetTextColor(HPS::RGBColor(1.0f, 0.510f, 0.3f))
.SetLineColor(HPS::RGBColor(1.0f, 0.510f, 0.3f));
portfolio.DefineNamedStyle("BooleanOperator_ToolStyle", tool_style_source);
tool_highlight.SetOverlay(HPS::Drawing::Overlay::None).SetStyleName("BooleanOperator_ToolStyle");
}
void OnViewDetached(HPS::View const & in_detached_view) override
{
// check to make sure the canvas still exists
if (canvas.Type() != HPS::Type::None)
{
// cleanup
auto highlight_control = canvas.GetWindowKey().GetHighlightControl();
highlight_control.Unhighlight(target_highlight);
highlight_control.Unhighlight(tool_highlight);
auto portfolio_key = attached_view.GetPortfolioKey();
portfolio_key.UndefineNamedStyle("BooleanOperator_TargetStyle");
portfolio_key.UndefineNamedStyle("BooleanOperator_ToolStyle");
target_style_source.Delete();
tool_style_source.Delete();
canvas.Update();
}
}
bool ApplyBoolean()
{
canvas.GetWindowKey().GetHighlightControl().UnhighlightEverything();
// Now that we have all the data we need for the boolean target and tool, we move on to performing
// the boolean operation itself
// Step 1: Transform the tool, so that it is in the same frame of reference as the target
MatrixKit target_matrix;
target_path.ShowNetModellingMatrix(target_matrix);
MatrixKit tool_matrix;
tool_path.ShowNetModellingMatrix(tool_matrix);
MatrixKit transform_matrix = target_matrix.Invert() * tool_matrix;
if (transform_matrix != MatrixKit())
{
//The matrix has to be specified in meters.
//The imported model uses millimeters as its units, so we will need to scale the matrix accordingly
double scale = 0.001;
//Recreate the matrix into Parasolid
float * elements = transform_matrix.data;
PK_TRANSF_sf_t transform_data;
transform_data.matrix[0][0] = elements[0]; transform_data.matrix[1][0] = elements[1]; transform_data.matrix[2][0] = elements[2]; transform_data.matrix[3][0] = elements[3];
transform_data.matrix[0][1] = elements[4]; transform_data.matrix[1][1] = elements[5]; transform_data.matrix[2][1] = elements[6]; transform_data.matrix[3][1] = elements[7];
transform_data.matrix[0][2] = elements[8]; transform_data.matrix[1][2] = elements[9]; transform_data.matrix[2][2] = elements[10]; transform_data.matrix[3][2] = elements[11];
transform_data.matrix[0][3] = elements[12] * scale; transform_data.matrix[1][3] = elements[13] * scale; transform_data.matrix[2][3] = elements[14] * scale; transform_data.matrix[3][3] = elements[15];
PK_TRANSF_t transform;
PK_TRANSF_create(&transform_data, &transform);
//Transform the tool using it
PK_BODY_transform_o_t transform_options;
PK_BODY_transform_o_m(transform_options);
PK_TOPOL_track_r_t tracking;
PK_TOPOL_local_r_t results;
PK_ERROR_t error = PK_BODY_transform_2(tool, transform, 0.001, &transform_options, &tracking, &results);
//Check that the transformation completed successfully
if (error != PK_ERROR_none)
return false; // something went wrong
else if (results.status != PK_local_status_ok_c && results.status != PK_local_status_nocheck_c)
return false; // something went wrong
}
//Step 2: Determine whether both entities are faceted, both have b-rep data available, or if they are mixed.
//Both entities need to be either faceted or both need to have b-rep data available.
//In the case where they are not the same, we convert the entity with b-rep to a faceted entity
bool target_faceted = IsEntityFaceted(target);
bool tool_faceted = IsEntityFaceted(tool);
//Step 3: If one of the bodies is faceted and the other isn't we need to convert the non-faceted one to
//a faceted body as well.
PK_BODY_t facet_body = PK_ENTITY_null;
PK_TOPOL_track_r_t tracking;
PK_ERROR_t error = PK_ERROR_none;
if (tool_faceted != target_faceted)
{
PK_BODY_t classical_body = PK_ENTITY_null;
if (!tool_faceted)
classical_body = tool;
else if (!target_faceted)
classical_body = target;
PK_BODY_make_facet_body_o_t make_facet_options;
PK_BODY_make_facet_body_o_m(make_facet_options);
PK_TOPOL_track_r_t redundant_topol;
error = PK_BODY_make_facet_body(classical_body, PK_ENTITY_null, &make_facet_options, &facet_body, &tracking, &redundant_topol);
if (error != PK_ERROR_none)
return false; // something went wrong
}
//Step 4. Perform the boolean operation
PK_BODY_boolean_o_t boolean_options;
PK_BODY_boolean_o_m(boolean_options);
boolean_options.function = PK_boolean_subtract_c;
PK_boolean_r_t boolean_results;
PK_BODY_t tool_to_use = tool;
PK_BODY_t target_to_use = target;
if (facet_body != PK_ENTITY_null && !tool_faceted)
tool_to_use = facet_body;
else if (facet_body != PK_ENTITY_null && !target_faceted)
target_to_use = facet_body;
error = PK_BODY_boolean_2(target_to_use, 1, &tool_to_use, &boolean_options, &tracking, &boolean_results);
if (error == PK_ERROR_none
&& boolean_results.result == PK_boolean_result_success_c
&& boolean_results.n_bodies > 0)
{
//Step 5. Show the result:
for (size_t i = 0; i < boolean_results.n_bodies; ++i)
{
// In this case there will only be a single resulting body from the boolean,
// the modified target entity.
// We re-tessellate it to sync the Parasolid changes with the Visualize
// representation of the part
if (boolean_results.bodies[i] != target)
return false; // something went wrong
HPS::Parasolid::Component parasolid_target_component(target_component);
// In the case where we have multiple bodies resulting from a boolean operation
// (for example if a boolean results in the target body being cut in half), we
// will need to Add the bodies which were not previously in the scene to it, using
// the ExchangeParasolid::File::AddEntity function
}
// Delete the Visualize component associated with the tool, since the boolean
// operation deleted its associated Parasolid body
tool_component.Delete();
return true;
}
else
return false; // something went wrong
}
private:
HPS::Canvas canvas;
HPS::CADModel cad_model;
HPS::View attached_view;
PK_BODY_t target;
HPS::Component target_component;
HPS::KeyPath target_path;
PK_BODY_t tool;
HPS::Component tool_component;
HPS::KeyPath tool_path;
HPS::WindowPoint click_location;
HPS::SelectionOptionsKit selection_options;
HPS::SegmentKey target_style_source;
HPS::HighlightOptionsKit target_highlight;
HPS::SegmentKey tool_style_source;
HPS::HighlightOptionsKit tool_highlight;
bool IsEntityFaceted(PK_BODY_t body)
{
int number_of_faces = 0;
PK_FACE_t * faces = nullptr;
PK_ERROR_t error = PK_BODY_ask_faces(body, &number_of_faces, &faces);
if (error != PK_ERROR_none || number_of_faces == 0)
{
//something went wrong
return false;
}
PK_SURF_t surface;
error = PK_FACE_ask_surf(faces[0], &surface);
if (error != PK_ERROR_none)
{
//something went wrong
return false;
}
PK_GEOM_ask_geom_category_o_t geom_options;
PK_GEOM_ask_geom_category_o_m(geom_options);
PK_GEOM_category_t category;
error = PK_GEOM_ask_geom_category(surface, &geom_options, &category);
if (error != PK_ERROR_none)
{
//something went wrong
return false;
}
if (category == PK_GEOM_category_facet_c)
return true;
else if (category == PK_GEOM_category_classic_c)
return false;
return false;
}
};
}
class TestBooleanOperator : HPS.Operator
{
private HPS.Canvas canvas;
private HPS.CADModel cad_model;
private HPS.View attached_view;
private PK.BODY_t target;
private HPS.Component target_component;
private HPS.KeyPath target_path;
private PK.BODY_t tool;
private HPS.Component tool_component;
private HPS.KeyPath tool_path;
private HPS.WindowPoint click_location;
private HPS.SelectionOptionsKit selection_options;
private HPS.SegmentKey target_style_source;
private HPS.HighlightOptionsKit target_highlight;
private HPS.SegmentKey tool_style_source;
private HPS.HighlightOptionsKit tool_highlight;
public TestBooleanOperator(ref Canvas in_canvas, ref CADModel in_cad_model)
{
canvas = in_canvas;
cad_model = in_cad_model;
attached_view = new HPS.View();
target = PK.ENTITY_t.@null;
target_component = new HPS.Component();
target_path = new HPS.KeyPath();
tool = PK.ENTITY_t.@null;
tool_component = new HPS.Component();
tool_path = new HPS.KeyPath();
click_location = new HPS.WindowPoint();
selection_options = new HPS.SelectionOptionsKit();
target_style_source = new SegmentKey();
target_highlight = new HighlightOptionsKit();
tool_style_source = new SegmentKey();
tool_highlight = new HighlightOptionsKit();
}
public override string GetName()
{
return "TestBooleanOperator";
}
public override bool OnMouseDown(HPS.MouseState in_state)
{
if (IsMouseTriggered(in_state))
{
click_location = in_state.GetLocation();
}
return false;
}
public override bool OnMouseUp(MouseState in_state)
{
if (!IsMouseTriggered(in_state) && in_state.GetLocation() == click_location)
{
ulong count = canvas.GetWindowKey().GetSelectionControl().SelectByPoint(click_location, selection_options, out results);
if (count == 0)
return false;
HPS.SelectionResultsIterator s_it = results.GetIterator();
while (s_it.IsValid())
{
HPS.KeyPath selected_path;
s_it.GetItem().ShowPath(out selected_path);
HPS.ComponentPath component_path = cad_model.GetComponentPath(selected_path);
HPS.Component[] components = component_path.GetComponents();
foreach (HPS.Component it in components)
{
if (it.GetComponentType() == HPS.Component.ComponentType.ParasolidTopoBody)
{
HPS.Parasolid.Component para_component = new HPS.Parasolid.Component(it);
HPS.HighlightControl highlight_control = canvas.GetWindowKey().GetHighlightControl();
PK.ENTITY_t selected_parasolid_entity = para_component.GetParasolidEntity();
if (target == selected_parasolid_entity)
{
//deselect target
highlight_control.Unhighlight(selected_path, target_highlight);
canvas.Update();
target = PK.ENTITY_t.@null;
target_component.Reset();
target_path.Reset();
return true;
}
else if (tool == selected_parasolid_entity)
{
//deselect tool
highlight_control.Unhighlight(selected_path, tool_highlight);
canvas.Update();
tool = PK.ENTITY_t.@null;
tool_component.Reset();
tool_path.Reset();
return true;
}
else if (target == PK.ENTITY_t.@null)
{
target = selected_parasolid_entity;
target_component = it;
target_path = selected_path;
highlight_control.Highlight(selected_path, target_highlight);
canvas.Update();
return true;
}
else
{
tool = selected_parasolid_entity;
tool_component = it;
tool_path = selected_path;
highlight_control.Highlight(selected_path, tool_highlight);
canvas.Update();
return true;
}
}
}
s_it.Next();
}
return true;
}
return false;
}
public override void OnViewAttached(HPS.View in_attached_view)
{
attached_view = GetAttachedView();
HPS.SprocketPath sprocket_path = new HPS.SprocketPath(canvas, canvas.GetAttachedLayout(), attached_view, attached_view.GetAttachedModel());
selection_options.SetAlgorithm(HPS.Selection.Algorithm.Visual)
.SetLevel(HPS.Selection.Level.Entity)
.SetScope(sprocket_path.GetKeyPath());
//Create target style
target_style_source = HPS.Database.CreateRootSegment();
HPS.MaterialKit highlight_material = new HPS.MaterialKit();
HPS.RGBAColor color_1 = new HPS.RGBAColor(0, 0.8f, 0.0f, 0.3f);
highlight_material.SetDiffuse(color_1);
HPS.RGBAColor color_2 = new HPS.RGBAColor(1.0f, 0.847f, 0.0f);
highlight_material.SetSpecular(color_2);
HPS.RGBAColor color_3 = new HPS.RGBAColor(0, 0.8f, 0);
target_style_source.GetMaterialMappingControl()
.SetFaceMaterial(highlight_material)
.SetEdgeColor(color_3)
.SetTextColor(color_3)
.SetLineColor(color_3);
HPS.PortfolioKey portfolio = attached_view.GetPortfolioKey();
portfolio.DefineNamedStyle("BooleanOperator_TargetStyle", target_style_source);
target_highlight.SetOverlay(HPS.Drawing.Overlay.None).SetStyleName("BooleanOperator_TargetStyle");
//Create tool style
tool_style_source = HPS.Database.CreateRootSegment();
HPS.RGBAColor color_4 = new HPS.RGBAColor(1.0f, 0.647f, 0.0f, 1.0f);
highlight_material.SetDiffuse(color_4);
tool_style_source.GetMaterialMappingControl()
.SetFaceMaterial(highlight_material)
.SetEdgeColor(color_4)
.SetTextColor(color_4)
.SetLineColor(color_4);
portfolio.DefineNamedStyle("BooleanOperator_ToolStyle", tool_style_source);
tool_highlight.SetOverlay(HPS.Drawing.Overlay.None).SetStyleName("BooleanOperator_ToolStyle");
}
public override void OnViewDetached(HPS.View in_detached_view)
{
if (canvas.Type() != HPS.Type.None)
{
HPS.HighlightControl highlight_control = canvas.GetWindowKey().GetHighlightControl();
highlight_control.Unhighlight(target_highlight);
highlight_control.Unhighlight(tool_highlight);
HPS.PortfolioKey portfolio_key = attached_view.GetPortfolioKey();
portfolio_key.UndefineNamedStyle("BooleanOperator_TargetStyle");
portfolio_key.UndefineNamedStyle("BooleanOperator_ToolStyle");
target_style_source.Delete();
tool_style_source.Delete();
canvas.Update();
}
}
unsafe public bool ApplyBoolean()
{
canvas.GetWindowKey().GetHighlightControl().UnhighlightEverything();
//Now that we have all the data we need for the boolean target and tool, we move on to performing the boolean operation itself
//Step 1: Transform the tool, so that it is in the same frame of reference as the target
MatrixKit target_matrix;
target_path.ShowNetModellingMatrix(out target_matrix);
MatrixKit tool_matrix;
tool_path.ShowNetModellingMatrix(out tool_matrix);
MatrixKit transform_matrix = target_matrix.Invert();
transform_matrix.MultiplyAndAssign(tool_matrix);
MatrixKit matrix = new MatrixKit();
if (transform_matrix != matrix)
{
//The matrix has to be specified in meters.
//The imported model uses millimeters as its units, so we will need to scale the matrix accordingly
double scale = 0.001;
//Recreate the matrix into Parasolid
float[] elements;
transform_matrix.ShowElements(out elements);
PK.TRANSF_sf_t transform_data;
transform_data.matrixI0J0 = elements[0]; transform_data.matrixI1J0 = elements[1]; transform_data.matrixI2J0 = elements[2]; transform_data.matrixI3J0 = elements[3];
transform_data.matrixI0J1 = elements[4]; transform_data.matrixI1J1 = elements[5]; transform_data.matrixI2J1 = elements[6]; transform_data.matrixI3J1 = elements[7];
transform_data.matrixI0J2 = elements[8]; transform_data.matrixI1J2 = elements[9]; transform_data.matrixI2J2 = elements[10]; transform_data.matrixI3J2 = elements[11];
transform_data.matrixI0J3 = elements[12] * scale; transform_data.matrixI1J3 = elements[13] * scale; transform_data.matrixI2J3 = elements[14] * scale; transform_data.matrixI3J3 = elements[15];
PK.TRANSF_t transform;
PK.TRANSF.create(&transform_data, &transform);
//Transform the tool using it
PK.BODY.transform_o_t transform_options = new PK.BODY.transform_o_t(true);
PK.TOPOL.track_r_t tracking_1;
PK.TOPOL.local_r_t results;
PK.ERROR.code_t error_1 = PK.BODY.transform_2(tool, transform, 0.001, &transform_options, &tracking_1, &results);
//Check that the transformation completed successfully
if (error_1 != PK.ERROR.code_t.no_errors)
return false;
else if (results.status != PK.local_status_t.ok_c && results.status != PK.local_status_t.nocheck_c)
return false;
}
//Step 2: Determine whether both entities are faceted, both have b-rep data available, or if they are mixed.
//Both entities need to be either faceted or both need to have b-rep data available.
//In the case where they are not the same, we convert the entity with b-rep to a faceted entity
bool target_faceted = IsEntityFaceted(target);
bool tool_faceted = IsEntityFaceted(tool);
//Step 3: If one of the bodies is faceted and the other isn't we need to convert the non-faceted one to
//a faceted body as well.
PK.BODY_t facet_body = PK.ENTITY_t.@null;
PK.TOPOL.track_r_t tracking;
PK.ERROR.code_t error = PK.ERROR.code_t.no_errors;
if (tool_faceted != target_faceted)
{
PK.BODY_t classical_body = PK.ENTITY_t.@null;
if (!tool_faceted)
classical_body = tool;
else if (!target_faceted)
classical_body = target;
PK.BODY.make_facet_body_o_t make_facet_options = new PK.BODY.make_facet_body_o_t(true);
PK.TOPOL.track_r_t redundant_topol;
error = PK.BODY.make_facet_body(classical_body, PK.ENTITY_t.@null, &make_facet_options, &facet_body, &tracking, &redundant_topol);
if (error != PK.ERROR.code_t.no_errors)
return false;
}
//Step 4. Perform the boolean operation
PK.BODY.boolean_o_t boolean_options = new PK.BODY.boolean_o_t(true);
boolean_options.function = PK.boolean_function_t.subtract;
PK.boolean_r_t boolean_results;
PK.BODY_t tool_to_use = tool;
PK.BODY_t target_to_use = target;
if (facet_body != PK.ENTITY_t.@null && !tool_faceted)
tool_to_use = facet_body;
else if (facet_body != PK.ENTITY_t.@null && !target_faceted)
target_to_use = facet_body;
error = PK.BODY.boolean_2(target_to_use, 1, &tool_to_use, &boolean_options, &tracking, &boolean_results);
if (error == PK.ERROR.code_t.no_errors
&& boolean_results.result == PK.boolean_result_t.success_c
&& boolean_results.n_bodies > 0)
{
//Step 5. Show the result:
for (int i = 0; i < boolean_results.n_bodies; ++i)
{
//In this case there will only be a single resulting body from the boolean, the modified target entity.
//We re-tessellate it to sync the Parasolid changes with the Visualize representation of the part
if (boolean_results.bodies[i] != target)
return false;
HPS.Parasolid.Component parasolid_target_component = new HPS.Parasolid.Component(target_component);
//In the case where we have multiple bodies resulting from a boolean operation (for example if a boolean results in the
//target body being cut in half), we will need to Add the bodies which were not previously in the scene to it, using the
//ExchangeParasolid.File.AddEntity function
}
//Delete the Visualize component associated with the tool, since the boolean operation deleted its associated Parasolid body
tool_component.Delete();
return true;
}
else
return false;
}
unsafe private bool IsEntityFaceted(PK.BODY_t body)
{
int number_of_faces = 0;
PK.FACE_t* faces = null;
PK.ERROR.code_t error = PK.BODY.ask_faces(body, &number_of_faces, &faces);
if (error != PK.ERROR.code_t.no_errors || number_of_faces == 0)
{
//something went wrong
return false;
}
PK.SURF_t surface;
error = PK.FACE.ask_surf(faces[0], &surface);
if (error != PK.ERROR.code_t.no_errors)
{
//something went wrong
return false;
}
PK.GEOM.ask_geom_category_o_t geom_options = new PK.GEOM.ask_geom_category_o_t(true);
PK.GEOM.category_t category;
error = PK.GEOM.ask_geom_category(surface, &geom_options, &category);
if (error != PK.ERROR.code_t.no_errors)
{
//something went wrong
return false;
}
if (category == ((PK.GEOM.category_t)25871))
return true;
else if (category == ((PK.GEOM.category_t)25870))
return false;
return false;
}
}



Scene setup and operator instantiation

[snippet 9.7.10.appendix_2]
bool setUpScene(Canvas &canvas, const char* importFileTarget, const char* importFileTool)
{
Exchange::ImportOptionsKit kit = Exchange::ImportOptionsKit::GetDefault();
kit.SetBRepMode(Exchange::BRepMode::BRepAndTessellation);
//load the first file
ExchangeParasolid::ImportNotifier notifier = ExchangeParasolid::File::Import(importFileTarget, kit,
Exchange::TranslationOptionsKit::GetDefault(),
Parasolid::FacetTessellationKit::GetDefault(),
Parasolid::LineTessellationKit::GetDefault());
notifier.Wait();
if (notifier.Status() != IOResult::Success)
return false; // something went wrong
CADModel cadmodel = notifier.GetCADModel();
//load the second file by adding it to the scene
ComponentArray location(1, cadmodel);
kit.SetLocation(ComponentPath(location));
notifier = ExchangeParasolid::File::Import(importFileTool, kit,
Exchange::TranslationOptionsKit::GetDefault(),
Parasolid::FacetTessellationKit::GetDefault(),
Parasolid::LineTessellationKit::GetDefault());
notifier.Wait();
if (notifier.Status() != IOResult::Success)
return false; // something went wrong
//attach the view to the canvas, and setup a nice camera
View view = cadmodel.ActivateDefaultCapture();
canvas.AttachViewAsLayout(view);
HPS::CameraKit camera_kit;
camera_kit.SetPosition(HPS::Point(-116.158989f, -2172.083008f, 921.859985f))
.SetTarget(HPS::Point(-0.000153f, -89.000000f, 117.000000f))
.SetUpVector(HPS::Vector(-0.004887f, -0.360171f, -0.932874f))
.SetField(894.474365f, 894.474365f)
.SetNearLimit(0.000000f)
view.GetSegmentKey().SetCamera(camera_kit);
view.GetSegmentKey().InsertDistantLight(Vector(1, 0, 0));
canvas.Update();
TestBooleanOperator * boolean_operator = new TestBooleanOperator(canvas, cadmodel);
view.GetOperatorControl().Push(boolean_operator, HPS::Operator::Priority::Default);
return true;
}
bool setUpScene(ref HPS.Canvas myCanvas, string importFileTarget, string importFileTool)
{
Exchange.ImportOptionsKit kit = Exchange.ImportOptionsKit.GetDefault();
kit.SetBRepMode(Exchange.BRepMode.BRepAndTessellation);
//load the first file
ExchangeParasolid.ImportNotifier notifier = ExchangeParasolid.File.Import(importFileTarget, kit,
Exchange.TranslationOptionsKit.GetDefault(),
Parasolid.FacetTessellationKit.GetDefault(),
Parasolid.LineTessellationKit.GetDefault());
notifier.Wait();
if (notifier.Status() != IOResult.Success)
return false; // something went wrong
CADModel cadmodel = notifier.GetCADModel();
//load the second file by adding it to the scene
Component[] location = { cadmodel };
ComponentPath path = new ComponentPath(location);
kit.SetLocation(path);
notifier = ExchangeParasolid.File.Import(importFileTool, kit,
Exchange.TranslationOptionsKit.GetDefault(),
Parasolid.FacetTessellationKit.GetDefault(),
Parasolid.LineTessellationKit.GetDefault());
notifier.Wait();
if (notifier.Status() != IOResult.Success)
return false; // something went wrong
//attach the view to the canvas, and setup a nice camera
View view = cadmodel.ActivateDefaultCapture();
myCanvas.AttachViewAsLayout(view);
HPS.CameraKit camera_kit = new HPS.CameraKit();
Point position = new Point(-116.158989f, -2172.083008f, 921.859985f);
Point target = new Point(-0.000153f, -89.000000f, 117.000000f);
Vector up_vector = new Vector(-0.004887f, -0.360171f, -0.932874f);
camera_kit.SetPosition(position)
.SetTarget(target)
.SetUpVector(up_vector)
.SetField(894.474365f, 894.474365f)
.SetNearLimit(0.000000f)
.SetProjection(HPS.Camera.Projection.Orthographic);
view.GetSegmentKey().SetCamera(camera_kit);
Vector distant_light = new Vector(1, 0, 0);
view.GetSegmentKey().InsertDistantLight(distant_light);
myCanvas.Update();
TestBooleanOperator boolean_operator = new TestBooleanOperator(ref myCanvas, ref cadmodel);
view.GetOperatorControl().Push(boolean_operator, HPS.Operator.Priority.Default);
return true;
}



Operator retrieval and boolean operation execution

[snippet 9.7.10.appendix_3]
void executeBooleanOperation(HPS::Canvas &myCanvas)
{
OperatorPtr myOpPointer;
myCanvas.GetFrontView().GetOperatorControl().ShowTop(myOpPointer);
if (myOpPointer->GetName() == "TestBooleanOperator")
{
auto op = std::static_pointer_cast<TestBooleanOperator>(myOpPointer);
op->ApplyBoolean();
myCanvas.UpdateWithNotifier().Wait();
}
}
void executeBooleanOperation(ref HPS.Canvas myCanvas)
{
HPS.Operator myOperator = new HPS.Operator();
myCanvas.GetFrontView().GetOperatorControl().ShowTop(out myOperator);
if (myOperator.GetName() == "TestBooleanOperator")
{
TestBooleanOperator op = (TestBooleanOperator)myOperator;
op.ApplyBoolean();
myCanvas.UpdateWithNotifier().Wait();
}
}