1
0
mirror of https://git.dev.opencascade.org/repos/occt.git synced 2025-08-24 13:50:49 +03:00

0029692: Add functionality to make the group of touching same-dimensional shapes connected

Implementation of the new class *BOPAlgo_MakeConnected* for making the group of touching same-dimensional shapes connected.
Provide the material association for the first sub-elements of the input shapes.
Provide possibility to make the connected shape periodic.

Draw commands for new algorithm:
* makeconnected - make the input shapes connected or glued, performs material associations;
* cmaterialson - returns the materials located on the requested side of a shape;
* cmakeperiodic - makes the connected shape periodic in requested directions;
* crepeatshape - repeats the periodic connected shape in requested directions requested number of times.

Documentation & test cases for the new algorithm.
This commit is contained in:
emv
2018-04-12 16:22:34 +03:00
parent f6d80ad0bf
commit ea9cfaa201
15 changed files with 1384 additions and 3 deletions

View File

@@ -5823,6 +5823,7 @@ The following topics are covered in the eight sections of this chapter:
* Drafting and blending.
* Defeaturing.
* Making shapes periodic in 3D space.
* Making shapes connected.
* Analysis of shapes.
@@ -7380,6 +7381,73 @@ The command clears all previous repetitions of the periodic shape allowing to st
No arguments are needed for the command.
@subsection occt_draw_makeconnected Making the touching shapes connected
Draw module for @ref occt_modalg_makeconnected "making the touching same-dimensional shapes connected" includes the following commands:
* **makeconnected** - make the input shapes connected or glued, performs material associations;
* **cmaterialson** - returns the materials located on the requested side of a shape;
* **cmakeperiodic** - makes the connected shape periodic in requested directions;
* **crepeatshape** - repeats the periodic connected shape in requested directions requested number of times.
@subsubsection occt_draw_makeconnected_makeconnected makeconnected
The command makes the input touching shapes connected.
Syntax:
~~~~
makeconnected result shape1 shape2 ...
Where:
result - resulting connected shape.
shape1 shape2 ... - shapes to be made connected.
~~~~
@subsubsection occt_draw_makeconnected_cmaterialson cmaterialson
The command returns the materials located on the requested side of the shape.
The command should be called after the shapes have been made connected, i.e. after the command **makeconnected**.
Syntax:
~~~~
cmaterialson result +/- shape
Where:
result - material shapes
shape - shape for which the materials are needed
+/- - side of a given shape ('+' for positive side, '-' - for negative).
~~~~
@subsubsection occt_draw_makeconnected_cmakeperiodic cmakeperiodic
The command makes the connected shape periodic in the required directions with the required period.
The command should be called after the shapes have been made connected, i.e. after the command **makeconnected**.
Syntax:
~~~~
cmakeperiodic result [-x/y/z period [-trim first]]
Where:
result - resulting periodic shape;
shape - input shape to make it periodic:
-x/y/z period - option to make the shape periodic in X, Y or Z direction with the given period;
-trim first - option to trim the shape to fit the required period, starting the period in first.
~~~~
@subsubsection occt_draw_makeconnected_crepeatshape crepeatshape
The command repeats the connected periodic shape in the required periodic directions required number of times.
The command should be called after the shapes have been made connected and periodic, i.e. after the commands **makeconnected** and **cmakeperiodic**.
Syntax:
~~~~
crepeatshape result -x/y/z times
Where:
result - resulting shape;
-x/y/z times - direction for repetition and number of repetitions (negative number of times means the repetition in negative direction).
~~~~
@subsection occt_draw_7_9 Analysis of topology and geometry
Analysis of shapes includes commands to compute length, area, volumes and inertial properties, as well as to compute some aspects impacting shape validity.

View File

@@ -3482,3 +3482,145 @@ repeatshape drills -x 24 -y 24
bcut result plate drills
~~~~
@figure{/user_guides/modeling_algos/images/modeling_algos_mkperiodic_im006.png,"Plate with drills",220}
@section occt_modalg_makeconnected Making touching shapes connected
Open CASCADE Technology provides tools for making the same-dimensional touching shapes connected (or glued), i.e. for making the coinciding geometries topologically shared among shapes.
To make the shapes connected they are glued by the means of @ref occt_algorithms_7 "General Fuse algorithm". The option BOPAlgo_GlueShift is used, thus if the input shapes have been interfering the algorithm will be unable to recognize this.
Making the group of shapes connected can be a useful e.g. before meshing the group. It will allow making the resulting mesh conformal.
The algorithm for making the shapes connected is is implemented in the class *BOPAlgo_MakeConnected*.
@subsection occt_modalg_makeconnected_materials Material association
In frames of this tool the input shapes are called materials, and each input shape has a unique material.
After making the shapes connected, the first sub-elements of the input shapes are associated with the shapes to which the belong. At that, the orientation of the sub-elements in the shape is taken into account.
The associations are made for the following types:
* For input SOLIDS the resulting FACES are associated with the input solids;
* For input FACES the resulting EDGES are associated with the input faces;
* For input EDGES the resulting VERTICES are associated with the input edges.
The association process is called the material association. It allows finding the coinciding elements for the opposite input shapes. These elements will be associated to at least two materials (one on the positive side of the shape, the other - on negative).
For obtaining the material information the following methods should be used
* *MaterialsOnPositiveSide()* - returns the original shapes (materials) located on the positive side of the given shape (i.e. with FORWARD orientation);
* *MaterialsOnNegativeSide()* - returns the original shapes (materials) located on the negative side of the given shape (i.e. with REVERSED orientation);
~~~~
// Returns the original shapes which images contain the given shape with FORWARD orientation.
const TopTools_ListOfShape& BOPAlgo_MakeConnected::MaterialsOnPositiveSide(const TopoDS_Shape& theS)
// Returns the original shapes which images contain the given shape with REVERSED orientation.
const TopTools_ListOfShape& BOPAlgo_MakeConnected::MaterialsOnNegativeSide(const TopoDS_Shape& theS)
~~~~
@subsection occt_modalg_makeconnected_makeperiodic Making connected shape periodic
The tool provides possibility to make the connected shape @ref occt_modalg_makeperiodic "periodic".
Since by making the shape periodic it ensures that the geometry of coinciding shapes on the opposite sides will be the same it allows reusing the mesh of the shape for its periodic twins.
After making the shape periodic the material associations are updated to correspond to the actual state of the result shape. Repetition of the periodic shape is also possible from here. Material associations are not going to be lost.
@subsection occt_modalg_makeconnected_history History support
The algorithm supports history of shapes modifications during the operation. Additionally to standard history method provided by *BRepTools_History* and used here as a history tool, the algorithm also provides the method to track the back connection - from resulting shapes to the input ones.
The method is called *GetOrigins()*:
~~~~
// Returns the list of original shapes from which the current shape has been created.
const TopTools_ListOfShape& BOPAlgo_MakeConnected::GetOrigins(const TopoDS_Shape& theS);
~~~~
Both Gluing history and history of making the shape periodic and periodic shape repetition are available here. Note, that all repeated shapes are stored as generated into the history.
@subsection occt_modalg_makeconnected_errors Errors/Warnings
The algorithm supports the Error/Warning reporting system which allows obtaining the extended overview of the errors and warning occurred during the operation.
As soon as any error appears the algorithm stops working. The warnings allow continuing the job, informing the user that something went wrong.
The algorithm returns the following alerts:
* *BOPAlgo_AlertTooFewArguments* - error alert is given on the attempt to run the algorithm without the arguments;
* *BOPAlgo_AlertMultiDimensionalArguments* - error alert is given on the attempt to run the algorithm on multi-dimensional arguments;
* *BOPAlgo_AlertUnableToGlue* - error alert is given if the gluer algorithm is unable to glue the given arguments;
* *BOPAlgo_AlertUnableToMakePeriodic* - warning alert is given if the periodicity maker is unable to make the connected shape periodic with given options;
* *BOPAlgo_AlertShapeIsNotPeriodic* - warning alert is given on the attempt to repeat the shape before making it periodic.
For more information on the error/warning reporting system please see the chapter @ref occt_algorithms_ers "Errors and warnings reporting system" of Boolean operations user guide.
@subsection occt_modalg_makeconnected_usage Usage
Here is the example of usage of the *BOPAlgo_MakePeriodic* algorithm on the API level:
~~~~
TopTools_ListOfShape anArguments = ...; // Shapes to make connected
Standard_Boolean bRunParallel = ...; // Parallel processing mode
BOPAlgo_MakeConnected aMC; // Tool for making the shapes connected
aMC.SetArguments(anArguments); // Set the shapes
aMC.SetRunParallel(bRunParallel); // Set parallel processing mode
aMC.Perform(); // Perform the operation
if (aMC.HasErrors()) // Check for the errors
{
// errors treatment
Standard_SStream aSStream;
aMC.DumpErrors(aSStream);
return;
}
if (aMC.HasWarnings()) // Check for the warnings
{
// warnings treatment
Standard_SStream aSStream;
aMC.DumpWarnings(aSStream);
}
const TopoDS_Shape& aGluedShape = aMC.Shape(); // Connected shape
// Checking material associations
TopAbs_ShapeEnum anElemType = ...; // Type of first BRep sub-element
TopExp_Explorer anExp(anArguments.First(), anElemType);
for (; anExp.More(); anExp.Next())
{
const TopoDS_Shape& anElement = anExp.Current();
const TopTools_ListOfShape& aNegativeM = aMC.MaterialsOnNegativeSide(anElement);
const TopTools_ListOfShape& aPositiveM = aMC.MaterialsOnPositiveSide(anElement);
}
// Making the connected shape periodic
BOPAlgo_MakePeriodic::PeriodicityParams aParams = ...; // Options for periodicity of the connected shape
aMC.MakePeriodic(aParams);
// Shape repetition after making it periodic
// Check if the shape has been made periodic successfully
if (aMC.PeriodicityTool().HasErrors())
{
// Periodicity maker error treatment
}
// Shape repetition in periodic directions
aMC.RepeatShape(0, 2);
const TopoDS_Shape& aShape = aMC.PeriodicShape(); // Periodic and repeated shape
~~~~
Please note, that the class is based on the options class *BOPAlgo_Options*, which provides the following options for the algorithm:
* Error/Warning reporting system;
* Parallel processing mode.
The other options of the base class are not supported here and will have no effect.
All the history information obtained during the operation is stored into *BRepTools_History* object and available through *History()* method:
~~~~
// Get the history object
const Handle(BRepTools_History)& BOPAlgo_MakeConnected::History();
~~~~
For the usage of the MakeConnected algorithm on the Draw level the following commands have been implemented:
* **makeconnected**
* **cmaterialson**
* **cmakeperiodic**
* **crepeatshape**
For more details on the connexity commands please refer the @ref occt_draw_makeconnected "MakeConnected commands" of the Draw test harness user guide.
To track the history of a shape modification during MakeConnected operation the @ref occt_draw_hist "standard history commands" can be used.
To have possibility to access the error/warning shapes of the operation use the *bdrawwarnshapes* command before running the algorithm (see command usage in the @ref occt_algorithms_ers "Errors and warnings reporting system" of Boolean operations user guide).

View File

@@ -102,4 +102,16 @@ Error: Unable to trim the shape for making it periodic (BOP Common fails)
Error: Unable to make the shape to look identical on opposite sides (Splitter fails)
.BOPAlgo_AlertUnableToRepeat
Error: Unable to repeat the shape (Gluer fails)
Error: Unable to repeat the shape (Gluer fails)
.BOPAlgo_AlertMultiDimensionalArguments
Error: Multi-dimensional arguments
.BOPAlgo_AlertUnableToMakePeriodic
Warning: Unable to make the shape periodic
.BOPAlgo_AlertUnableToGlue
Error: Unable to glue the shapes
.BOPAlgo_AlertShapeIsNotPeriodic
Warning: The shape is not periodic

View File

@@ -116,4 +116,16 @@ DEFINE_ALERT_WITH_SHAPE(BOPAlgo_AlertUnableToMakeIdentical)
//! Unable to repeat the shape (Gluer fails)
DEFINE_ALERT_WITH_SHAPE(BOPAlgo_AlertUnableToRepeat)
//! Multi-dimensional arguments
DEFINE_SIMPLE_ALERT(BOPAlgo_AlertMultiDimensionalArguments)
//! Unable to make the shape periodic
DEFINE_ALERT_WITH_SHAPE(BOPAlgo_AlertUnableToMakePeriodic)
//! Unable to glue the shapes
DEFINE_ALERT_WITH_SHAPE(BOPAlgo_AlertUnableToGlue)
//! The shape is not periodic
DEFINE_ALERT_WITH_SHAPE(BOPAlgo_AlertShapeIsNotPeriodic)
#endif // _BOPAlgo_Alerts_HeaderFile

View File

@@ -105,4 +105,16 @@ static const char BOPAlgo_BOPAlgo_msg[] =
"Error: Unable to make the shape to look identical on opposite sides (Splitter fails)\n"
"\n"
".BOPAlgo_AlertUnableToRepeat\n"
"Error: Unable to repeat the shape (Gluer fails)\n";
"Error: Unable to repeat the shape (Gluer fails)\n"
"\n"
".BOPAlgo_AlertMultiDimensionalArguments\n"
"Error: Multi-dimensional arguments\n"
"\n"
".BOPAlgo_AlertUnableToMakePeriodic\n"
"Warning: Unable to make the shape periodic\n"
"\n"
".BOPAlgo_AlertUnableToGlue\n"
"Error: Unable to glue the shapes\n"
"\n"
".BOPAlgo_AlertShapeIsNotPeriodic\n"
"Warning: The shape is not periodic\n";

View File

@@ -0,0 +1,305 @@
// Created on: 2018-03-29
// Created by: Eugeny MALTCHIKOV
// Copyright (c) 2018 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.
#include <BOPAlgo_MakeConnected.hxx>
#include <BOPAlgo_Alerts.hxx>
#include <BOPAlgo_Builder.hxx>
#include <BOPAlgo_Tools.hxx>
#include <BOPTools_AlgoTools.hxx>
#include <BRep_Builder.hxx>
#include <TopExp_Explorer.hxx>
#include <TopoDS_Iterator.hxx>
//=======================================================================
//function : Perform
//purpose : Makes the shapes connected
//=======================================================================
void BOPAlgo_MakeConnected::Perform()
{
// Check the input data
CheckData();
if (HasErrors())
return;
if (myHistory.IsNull())
myHistory = new BRepTools_History;
// Glue the arguments
MakeConnected();
if (HasErrors())
return;
// Perform material associations for the faces
AssociateMaterials();
if (HasErrors())
return;
}
//=======================================================================
//function : CheckData
//purpose : Check the validity of input data
//=======================================================================
void BOPAlgo_MakeConnected::CheckData()
{
// Check the number of arguments
if (myArguments.IsEmpty())
{
// Not enough arguments
AddError(new BOPAlgo_AlertTooFewArguments());
return;
}
// Check that all shapes in arguments are of the same type
// Extract the shapes from the compound arguments
TopTools_ListOfShape aLA;
// Fence map
TopTools_MapOfShape aMFence;
TopTools_ListIteratorOfListOfShape itLA(myArguments);
for (; itLA.More(); itLA.Next())
BOPAlgo_Tools::TreatCompound(itLA.Value(), aMFence, aLA);
if (aLA.IsEmpty())
{
// It seems that all argument shapes are empty compounds
AddError(new BOPAlgo_AlertTooFewArguments());
return;
}
// Check dimensions of the extracted non-compound shapes
itLA.Initialize(aLA);
Standard_Integer iDim = BOPTools_AlgoTools::Dimension(itLA.Value());
for (itLA.Next(); itLA.More(); itLA.Next())
{
if (iDim != BOPTools_AlgoTools::Dimension(itLA.Value()))
{
// The arguments are of different type
AddError(new BOPAlgo_AlertMultiDimensionalArguments());
return;
}
}
}
//=======================================================================
//function : MakeConnected
//purpose : Glues the argument shapes
//=======================================================================
void BOPAlgo_MakeConnected::MakeConnected()
{
// Initialize the history
if (myGlueHistory.IsNull())
myGlueHistory = new BRepTools_History;
if (myArguments.Extent() == 1)
{
// No need to glue the single shape
myShape = myArguments.First();
}
else
{
// Glue the shapes
BOPAlgo_Builder aGluer;
aGluer.SetArguments(myArguments);
aGluer.SetGlue(BOPAlgo_GlueShift);
aGluer.SetRunParallel(myRunParallel);
aGluer.SetNonDestructive(Standard_True);
aGluer.Perform();
if (aGluer.HasErrors())
{
// Unable to glue the shapes
TopoDS_Compound aCW;
BRep_Builder().MakeCompound(aCW);
for (TopTools_ListIteratorOfListOfShape it(myArguments); it.More(); it.Next())
BRep_Builder().Add(aCW, it.Value());
AddError(new BOPAlgo_AlertUnableToGlue(aCW));
return;
}
myShape = aGluer.Shape();
// Save the gluing history
myGlueHistory->Merge(aGluer.Arguments(), aGluer);
myHistory->Merge(myGlueHistory);
}
// Keep the glued shape
myGlued = myShape;
// Fill the map of origins
FillOrigins();
}
//=======================================================================
//function : FillOrigins
//purpose : Fills the map of origins
//=======================================================================
void BOPAlgo_MakeConnected::FillOrigins()
{
myOrigins.Clear();
// Map the history shapes of the arguments
if (myAllInputsMap.IsEmpty())
{
TopTools_ListIteratorOfListOfShape itLA(myArguments);
for (; itLA.More(); itLA.Next())
TopExp::MapShapes(itLA.Value(), myAllInputsMap);
}
const Standard_Integer aNbS = myAllInputsMap.Extent();
for (Standard_Integer i = 1; i <= aNbS; ++i)
{
const TopoDS_Shape& aS = myAllInputsMap(i);
if (!BRepTools_History::IsSupportedType(aS))
continue;
// Get Modified & Generated shapes
for (Standard_Integer j = 0; j < 2; ++j)
{
const TopTools_ListOfShape& aLH = !j ? myHistory->Modified(aS) : myHistory->Generated(aS);
TopTools_ListIteratorOfListOfShape itLH(aLH);
for (; itLH.More(); itLH.Next())
{
const TopoDS_Shape& aHS = itLH.Value();
TopTools_ListOfShape* pLOr = myOrigins.ChangeSeek(aHS);
if (!pLOr)
pLOr = myOrigins.Bound(aHS, TopTools_ListOfShape());
if (!pLOr->Contains(aS))
pLOr->Append(aS);
}
}
}
}
//=======================================================================
//function : AssociateMaterials
//purpose : Associates the materials for the first BRep sub-elements
//=======================================================================
void BOPAlgo_MakeConnected::AssociateMaterials()
{
myMaterials.Clear();
// Extract all non-compound shapes from the result
TopTools_ListOfShape aLShapes;
TopTools_MapOfShape aMFence;
BOPAlgo_Tools::TreatCompound(myShape, aMFence, aLShapes);
if (aLShapes.IsEmpty())
return;
// Define the element type and the material type
TopAbs_ShapeEnum anElemType;
const TopAbs_ShapeEnum aMaterialType = aLShapes.First().ShapeType();
if (aMaterialType == TopAbs_SOLID || aMaterialType == TopAbs_COMPSOLID)
anElemType = TopAbs_FACE;
else if (aMaterialType == TopAbs_FACE || aMaterialType == TopAbs_SHELL)
anElemType = TopAbs_EDGE;
else if (aMaterialType == TopAbs_EDGE || aMaterialType == TopAbs_WIRE)
anElemType = TopAbs_VERTEX;
else
return;
TopTools_ListIteratorOfListOfShape itLS(aLShapes);
for (; itLS.More(); itLS.Next())
{
const TopoDS_Shape& aS = itLS.Value();
const TopTools_ListOfShape& aLOr = GetOrigins(aS);
const TopoDS_Shape& aSOr = aLOr.IsEmpty() ? aS : aLOr.First();
TopExp_Explorer anExp(aS, anElemType);
for (; anExp.More(); anExp.Next())
{
const TopoDS_Shape& anElement = anExp.Current();
TopTools_ListOfShape* pLM = myMaterials.ChangeSeek(anElement);
if (!pLM)
pLM = myMaterials.Bound(anElement, TopTools_ListOfShape());
pLM->Append(aSOr);
}
}
}
//=======================================================================
//function : MakePeriodic
//purpose : Makes the shape periodic according to the given parameters
//=======================================================================
void BOPAlgo_MakeConnected::MakePeriodic(const BOPAlgo_MakePeriodic::PeriodicityParams& theParams)
{
if (HasErrors())
return;
// Make the shape periodic
myPeriodicityMaker.Clear();
myPeriodicityMaker.SetShape(myGlued);
myPeriodicityMaker.SetPeriodicityParameters(theParams);
myPeriodicityMaker.SetRunParallel(myRunParallel);
myPeriodicityMaker.Perform();
if (myPeriodicityMaker.HasErrors())
{
// Add warning informing the user that periodicity with
// given parameters is not possible
AddWarning(new BOPAlgo_AlertUnableToMakePeriodic(myShape));
return;
}
myShape = myPeriodicityMaker.Shape();
// Update history
myHistory->Clear();
if (!myGlueHistory.IsNull())
myHistory->Merge(myGlueHistory);
myHistory->Merge(myPeriodicityMaker.History());
// Fill the map of origins
FillOrigins();
// Update the material associations after making the shape periodic
AssociateMaterials();
}
//=======================================================================
//function : RepeatShape
//purpose : Repeats the shape in the given direction given number of times
//=======================================================================
void BOPAlgo_MakeConnected::RepeatShape(const Standard_Integer theDirectionID,
const Standard_Integer theTimes)
{
if (HasErrors())
return;
if (myPeriodicityMaker.Shape().IsNull() || myPeriodicityMaker.HasErrors())
{
// The shape has not been made periodic yet
AddWarning(new BOPAlgo_AlertShapeIsNotPeriodic(myShape));
return;
}
// Repeat the shape
myShape = myPeriodicityMaker.RepeatShape(theDirectionID, theTimes);
// Update history
myHistory->Clear();
if (!myGlueHistory.IsNull())
myHistory->Merge(myGlueHistory);
myHistory->Merge(myPeriodicityMaker.History());
// Fill the map of origins
FillOrigins();
// Update the material associations after shape repetitions
AssociateMaterials();
}

View File

@@ -0,0 +1,330 @@
// Created on: 2018-03-29
// Created by: Eugeny MALTCHIKOV
// Copyright (c) 2018 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.
#ifndef _BOPAlgo_MakeConnected_HeaderFile
#define _BOPAlgo_MakeConnected_HeaderFile
#include <Standard.hxx>
#include <Standard_DefineAlloc.hxx>
#include <Standard_Handle.hxx>
#include <BOPAlgo_Options.hxx>
#include <BOPAlgo_MakePeriodic.hxx>
#include <BRepTools_History.hxx>
#include <NCollection_DataMap.hxx>
#include <TopTools_OrientedShapeMapHasher.hxx>
//! BOPAlgo_MakeConnected is the algorithm for making the touching
//! shapes connected or glued, i.e. for making the coinciding geometries
//! be topologically shared among the shapes.
//!
//! The input shapes should be of the same dimension, otherwise
//! the gluing will not make any sense.
//!
//! After the shapes are made connected, the first sub-elements of input shapes
//! are associated with the shapes to which they belong. At that, the orientation of
//! the sub-element in the shape is taken into account.
//! The associations are made for the following types:
//! - For input SOLIDS, the resulting FACES are associated with the input solids;
//! - For input FACES, the resulting EDGES are associated with the input faces;
//! - For input EDGES, the resulting VERTICES are associated with the input edges.
//!
//! In frames of this algorithm the input shapes are called materials,
//! and the association process is called the material association.
//! The material association allows finding the coinciding elements for the opposite
//! input shapes. These elements will be associated to at least two materials.
//!
//! After making the shapes connected, it is possible to make the connected
//! shape periodic using the *BOPAlgo_MakePeriodic* tool.
//! After making the shape periodic, the material associations will be updated
//! to correspond to the actual state of the result shape.
//! Repetition of the periodic shape is also possible here. Material associations
//! are not going to be lost.
//!
//! The algorithm supports history of shapes modification, thus it is possible
//! to track the modification of the input shapes during the operations.
//! Additionally to standard history methods, the algorithm provides the
//! the method *GetOrigins()* which allows obtaining the input shapes from which
//! the resulting shape has been created.
//!
//! The algorithm supports the parallel processing mode, which allows faster
//! completion of the operations.
//!
//! The algorithm returns the following Error/Warning messages:
//! - *BOPAlgo_AlertTooFewArguments* - error alert is given on the attempt to run
//! the algorithm without the arguments;
//! - *BOPAlgo_AlertMultiDimensionalArguments* - error alert is given on the attempt
//! to run the algorithm on multi-dimensional arguments;
//! - *BOPAlgo_AlertUnableToGlue* - error alert is given if the gluer algorithm
//! is unable to glue the given arguments;
//! - *BOPAlgo_AlertUnableToMakePeriodic* - warning alert is given if the periodicity
//! maker is unable to make the connected shape periodic with given options;
//! - *BOPAlgo_AlertShapeIsNotPeriodic* - warning alert is given on the attempt to
//! repeat the shape before making it periodic.
//!
//! Here is the example of usage of the algorithm:
//! ~~~~
//! TopTools_ListOfShape anArguments = ...; // Shapes to make connected
//! Standard_Boolean bRunParallel = ...; // Parallel processing mode
//!
//! BOPAlgo_MakeConnected aMC; // Tool for making the shapes connected
//! aMC.SetArguments(anArguments); // Set the shapes
//! aMC.SetRunParallel(bRunParallel); // Set parallel processing mode
//! aMC.Perform(); // Perform the operation
//!
//! if (aMC.HasErrors()) // Check for the errors
//! {
//! // errors treatment
//! Standard_SStream aSStream;
//! aMC.DumpErrors(aSStream);
//! return;
//! }
//! if (aMC.HasWarnings()) // Check for the warnings
//! {
//! // warnings treatment
//! Standard_SStream aSStream;
//! aMC.DumpWarnings(aSStream);
//! }
//!
//! const TopoDS_Shape& aGluedShape = aMC.Shape(); // Connected shape
//!
//! // Checking material associations
//! TopAbs_ShapeEnum anElemType = ...; // Type of first BRep sub-element
//! TopExp_Explorer anExp(anArguments.First(), anElemType);
//! for (; anExp.More(); anExp.Next())
//! {
//! const TopoDS_Shape& anElement = anExp.Current();
//! const TopTools_ListOfShape& aNegativeM = aMC.MaterialsOnNegativeSide(anElement);
//! const TopTools_ListOfShape& aPositiveM = aMC.MaterialsOnPositiveSide(anElement);
//! }
//!
//! // Making the connected shape periodic
//! BOPAlgo_MakePeriodic::PeriodicityParams aParams = ...; // Options for periodicity of the connected shape
//! aMC.MakePeriodic(aParams);
//!
//! // Shape repetition after making it periodic
//! // Check if the shape has been made periodic successfully
//! if (aMC.PeriodicityTool().HasErrors())
//! {
//! // Periodicity maker error treatment
//! }
//!
//! // Shape repetition in periodic directions
//! aMC.RepeatShape(0, 2);
//!
//! const TopoDS_Shape& aShape = aMC.PeriodicShape(); // Periodic and repeated shape
//! ~~~~
//!
class BOPAlgo_MakeConnected : public BOPAlgo_Options
{
public:
DEFINE_STANDARD_ALLOC
public: //! @name Constructor
//! Empty constructor
BOPAlgo_MakeConnected() : BOPAlgo_Options()
{
}
public: //! @name Setters for the shapes to make connected
//! Sets the shape for making them connected.
//! @param theArgs [in] The arguments for the operation.
void SetArguments(const TopTools_ListOfShape& theArgs)
{
myArguments = theArgs;
}
//! Adds the shape to the arguments.
//! @param theS [in] One of the argument shapes.
void AddArgument(const TopoDS_Shape& theS)
{
myArguments.Append(theS);
}
//! Returns the list of arguments of the operation.
const TopTools_ListOfShape& Arguments() const
{
return myArguments;
}
public: //! @name Performing the operations
//! Performs the operation, i.e. makes the input shapes connected.
Standard_EXPORT void Perform();
public: //! @name Shape periodicity & repetition
//! Makes the connected shape periodic.
//! Repeated calls of this method overwrite the previous calls
//! working with the basis connected shape.
//! @param theParams [in] Periodic options.
Standard_EXPORT void MakePeriodic(const BOPAlgo_MakePeriodic::PeriodicityParams& theParams);
//! Performs repetition of the periodic shape in specified direction
//! required number of times.
//! @param theDirectionID [in] The direction's ID (0 for X, 1 for Y, 2 for Z);
//! @param theTimes [in] Requested number of repetitions (sign of the value defines
//! the side of the repetition direction (positive or negative)).
Standard_EXPORT void RepeatShape(const Standard_Integer theDirectionID,
const Standard_Integer theTimes);
//! Returns the periodicity tool.
const BOPAlgo_MakePeriodic& PeriodicityTool() const
{
return myPeriodicityMaker;
}
public: //! @name Material transitions
//! Returns the original shapes which images contain the
//! the given shape with FORWARD orientation.
//! @param theS [in] The shape for which the materials are necessary.
const TopTools_ListOfShape& MaterialsOnPositiveSide(const TopoDS_Shape& theS)
{
const TopTools_ListOfShape* pLM = myMaterials.Seek(theS.Oriented(TopAbs_FORWARD));
return (pLM ? *pLM : EmptyList());
}
//! Returns the original shapes which images contain the
//! the given shape with REVERSED orientation.
//! @param theS [in] The shape for which the materials are necessary.
const TopTools_ListOfShape& MaterialsOnNegativeSide(const TopoDS_Shape& theS)
{
const TopTools_ListOfShape* pLM = myMaterials.Seek(theS.Oriented(TopAbs_REVERSED));
return (pLM ? *pLM : EmptyList());
}
public: //! @name History methods
//! Returns the history of operations
const Handle(BRepTools_History)& History() const
{
return myHistory;
}
//! Returns the list of shapes modified from the given shape.
//! @param theS [in] The shape for which the modified shapes are necessary.
const TopTools_ListOfShape& GetModified(const TopoDS_Shape& theS)
{
return (myHistory.IsNull() ? EmptyList() : myHistory->Modified(theS));
}
//! Returns the list of original shapes from which the current shape has been created.
//! @param theS [in] The shape for which the origins are necessary.
const TopTools_ListOfShape& GetOrigins(const TopoDS_Shape& theS)
{
const TopTools_ListOfShape* pLOr = myOrigins.Seek(theS);
return (pLOr ? *pLOr : EmptyList());
}
public: //! @name Getting the result shapes
//! Returns the resulting connected shape
const TopoDS_Shape& Shape() const
{
return myGlued;
}
//! Returns the resulting periodic & repeated shape
const TopoDS_Shape& PeriodicShape() const
{
return myShape;
}
public: //! @name Clearing the contents of the algorithm from previous runs
//! Clears the contents of the algorithm.
void Clear()
{
BOPAlgo_Options::Clear();
myArguments.Clear();
myAllInputsMap.Clear();
myPeriodicityMaker.Clear();
myOrigins.Clear();
myMaterials.Clear();
if (!myGlueHistory.IsNull())
myGlueHistory->Clear();
if (!myHistory.IsNull())
myHistory->Clear();
myGlued.Nullify();
myShape.Nullify();
}
protected: //! @name Protected methods performing the operation
//! Checks the validity of input data.
Standard_EXPORT void CheckData();
//! Makes the argument shapes connected (or glued).
Standard_EXPORT void MakeConnected();
//! Associates the materials transitions for the first BRep sub-elements:
//! - For input Solids, associates the Faces to Solids;
//! - For input Faces, associates the Edges to Faces;
//! - For input Edges, associates the Vertices to Edges.
Standard_EXPORT void AssociateMaterials();
//! Fills the map of origins
Standard_EXPORT void FillOrigins();
private:
//! Returns an empty list.
const TopTools_ListOfShape& EmptyList()
{
static const TopTools_ListOfShape anEmptyList;
return anEmptyList;
}
protected: //! @name Fields
// Inputs
TopTools_ListOfShape myArguments; //!< Input shapes for making them connected
TopTools_IndexedMapOfShape myAllInputsMap; //!< Map of all BRep sub-elements of the input shapes
// Tools
BOPAlgo_MakePeriodic myPeriodicityMaker; //!< Tool for making the shape periodic
// Results
NCollection_DataMap
<TopoDS_Shape,
TopTools_ListOfShape,
TopTools_OrientedShapeMapHasher> myMaterials; //!< Map of the materials associations
//! for the first BRep sub-elements
TopTools_DataMapOfShapeListOfShape myOrigins; //!< Map of origins
//! (allows tracking the shape's ancestors)
Handle(BRepTools_History) myGlueHistory; //!< Gluing History
Handle(BRepTools_History) myHistory; //!< Final History of shapes modifications
//! (including making the shape periodic and repetitions)
TopoDS_Shape myGlued; //!< The resulting connected (glued) shape
TopoDS_Shape myShape; //!< The resulting shape
};
#endif // _BOPAlgo_MakeConnected_HeaderFile

View File

@@ -26,6 +26,8 @@ BOPAlgo_CheckResult.cxx
BOPAlgo_CheckResult.hxx
BOPAlgo_CheckStatus.hxx
BOPAlgo_ListOfCheckResult.hxx
BOPAlgo_MakeConnected.cxx
BOPAlgo_MakeConnected.hxx
BOPAlgo_MakePeriodic.cxx
BOPAlgo_MakePeriodic.hxx
BOPAlgo_MakerVolume.cxx

View File

@@ -58,6 +58,7 @@ void BOPTest::AllCommands(Draw_Interpretor& theCommands)
BOPTest::UtilityCommands (theCommands);
BOPTest::RemoveFeaturesCommands(theCommands);
BOPTest::PeriodicityCommands(theCommands);
BOPTest::MkConnectedCommands(theCommands);
}
//=======================================================================
//function : Factory

View File

@@ -62,6 +62,8 @@ public:
Standard_EXPORT static void PeriodicityCommands (Draw_Interpretor& aDI);
Standard_EXPORT static void MkConnectedCommands (Draw_Interpretor& aDI);
//! Prints errors and warnings if any and draws attached shapes
//! if flag BOPTest_Objects::DrawWarnShapes() is set
Standard_EXPORT static void ReportAlerts (const Handle(Message_Report)& theReport);

View File

@@ -0,0 +1,342 @@
// Created on: 04/02/2018
// Created by: Eugeny MALTCHIKOV
// Copyright (c) 2018 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.
#include <BOPTest.hxx>
#include <BOPAlgo_MakeConnected.hxx>
#include <BOPTest_DrawableShape.hxx>
#include <BOPTest_Objects.hxx>
#include <BRep_Builder.hxx>
#include <BRepTest_Objects.hxx>
#include <DBRep.hxx>
#include <Draw.hxx>
#include <Precision.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Compound.hxx>
static Standard_Integer MakeConnected(Draw_Interpretor&, Standard_Integer, const char**);
static Standard_Integer MakePeriodic(Draw_Interpretor&, Standard_Integer, const char**);
static Standard_Integer MaterialsOn(Draw_Interpretor&, Standard_Integer, const char**);
static Standard_Integer RepeatShape(Draw_Interpretor&, Standard_Integer, const char**);
namespace
{
static BOPAlgo_MakeConnected TheMakeConnectedTool;
}
//=======================================================================
//function : MkConnectedCommands
//purpose :
//=======================================================================
void BOPTest::MkConnectedCommands(Draw_Interpretor& theCommands)
{
static Standard_Boolean done = Standard_False;
if (done) return;
done = Standard_True;
// Chapter's name
const char* group = "BOPTest commands";
// Commands
theCommands.Add("makeconnected", "makeconnected result shape1 shape2 ...\n"
"\t\tMake the given shapes connected (glued).",
__FILE__, MakeConnected, group);
theCommands.Add("cmakeperiodic", "cmakeperiodic result [-x/y/z period [-trim first]]\n"
"\t\tMake the connected shape periodic in the required directions.\n"
"\t\tresult - resulting periodic shape;\n"
"\t\t-x/y/z period - option to make the shape periodic in X, Y or Z\n "
"\t\t direction with the given period;\n"
"\t\t-trim first - option to trim the shape to fit the required period,\n"
"\t\t starting the period in first.",
__FILE__, MakePeriodic, group);
theCommands.Add("cmaterialson", "cmaterialson r +/- shape\n"
"\t\tReturns the original shapes located on the required side of a shape:\n"
"\t\t'+' - on a positive side of a shape (containing the shape with orientation FORWARD)\n"
"\t\t'-' - on a negative side of a shape (containing the shape with orientation REVERSED).",
__FILE__, MaterialsOn, group);
theCommands.Add("crepeatshape", "crepeatshape result -x/y/z times\n"
"\t\tRepeats the periodic connected shape in periodic directions required number of times.\n"
"\t\tresult - resulting shape;\n"
"\t\t-x/y/z times - direction for repetition and number of repetitions.",
__FILE__, RepeatShape, group);
}
//=======================================================================
//function : MakeConnected
//purpose :
//=======================================================================
Standard_Integer MakeConnected(Draw_Interpretor& theDI,
Standard_Integer theArgc,
const char ** theArgv)
{
if (theArgc < 3)
{
theDI.PrintHelp(theArgv[0]);
return 1;
}
TheMakeConnectedTool.Clear();
for (Standard_Integer i = 2; i < theArgc; ++i)
{
TopoDS_Shape aS = DBRep::Get(theArgv[i]);
if (aS.IsNull())
{
theDI << "Error: " << theArgv[i] << " is a null shape. Skip it.\n";
continue;
}
TheMakeConnectedTool.AddArgument(aS);
}
TheMakeConnectedTool.SetRunParallel(BOPTest_Objects::RunParallel());
TheMakeConnectedTool.Perform();
// Print Error/Warning messages
BOPTest::ReportAlerts(TheMakeConnectedTool.GetReport());
// Set the history of the operation in session
BRepTest_Objects::SetHistory(TheMakeConnectedTool.History());
if (TheMakeConnectedTool.HasErrors())
return 0;
// Draw the result shape
const TopoDS_Shape& aResult = TheMakeConnectedTool.Shape();
DBRep::Set(theArgv[1], aResult);
return 0;
}
//=======================================================================
//function : MakePeriodic
//purpose :
//=======================================================================
Standard_Integer MakePeriodic(Draw_Interpretor& theDI,
Standard_Integer theArgc,
const char ** theArgv)
{
if (theArgc < 4)
{
theDI.PrintHelp(theArgv[0]);
return 1;
}
if (TheMakeConnectedTool.Shape().IsNull() || TheMakeConnectedTool.HasErrors())
{
theDI << "Make the shapes connected first.\n";
return 1;
}
BOPAlgo_MakePeriodic::PeriodicityParams aParams;
for (Standard_Integer i = 2; i < theArgc;)
{
Standard_Integer aDirID = -1;
if (!strcasecmp(theArgv[i], "-x"))
aDirID = 0;
else if (!strcasecmp(theArgv[i], "-y"))
aDirID = 1;
else if (!strcasecmp(theArgv[i], "-z"))
aDirID = 2;
else
{
theDI << theArgv[i] << " - Invalid key\n";
return 1;
}
char cDirName[2];
sprintf(cDirName, "%c", theArgv[i][1]);
Standard_Real aPeriod = 0;
if (theArgc > i + 1)
aPeriod = Draw::Atof(theArgv[++i]);
if (aPeriod <= Precision::Confusion())
{
theDI << "Period for " << cDirName << " direction is not set\n";
return 1;
}
aParams.myPeriodic[aDirID] = Standard_True;
aParams.myPeriod[aDirID] = aPeriod;
++i;
if (theArgc > i + 1)
{
// Check if trimming is necessary
if (!strcmp(theArgv[i], "-trim"))
{
if (theArgc == (i + 1))
{
theDI << "Trim bounds for " << cDirName << " direction are not set\n";
return 1;
}
Standard_Real aFirst = Draw::Atof(theArgv[++i]);
aParams.myIsTrimmed[aDirID] = Standard_False;
aParams.myPeriodFirst[aDirID] = aFirst;
++i;
}
}
}
TheMakeConnectedTool.MakePeriodic(aParams);
// Print Error/Warning messages
BOPTest::ReportAlerts(TheMakeConnectedTool.GetReport());
// Set the history of the operation in session
BRepTest_Objects::SetHistory(TheMakeConnectedTool.History());
if (TheMakeConnectedTool.HasErrors())
return 0;
// Draw the result shape
const TopoDS_Shape& aResult = TheMakeConnectedTool.PeriodicShape();
DBRep::Set(theArgv[1], aResult);
return 0;
}
//=======================================================================
//function : RepeatShape
//purpose :
//=======================================================================
Standard_Integer RepeatShape(Draw_Interpretor& theDI,
Standard_Integer theArgc,
const char ** theArgv)
{
if (theArgc < 4)
{
theDI.PrintHelp(theArgv[0]);
return 1;
}
if (TheMakeConnectedTool.PeriodicityTool().HasErrors())
{
theDI << "The shapes have not been made periodic yet.\n";
return 1;
}
for (Standard_Integer i = 2; i < theArgc; ++i)
{
Standard_Integer aDirID = -1;
if (!strcasecmp(theArgv[i], "-x"))
aDirID = 0;
else if (!strcasecmp(theArgv[i], "-y"))
aDirID = 1;
else if (!strcasecmp(theArgv[i], "-z"))
aDirID = 2;
else
{
theDI << theArgv[i] << " - Invalid key\n";
return 1;
}
char cDirName[2];
sprintf(cDirName, "%c", theArgv[i][1]);
Standard_Integer aTimes = 0;
if (theArgc > i + 1)
aTimes = Draw::Atoi(theArgv[++i]);
if (aTimes == 0)
{
theDI << "Number of repetitions for " << cDirName << " direction is not set\n";
return 1;
}
TheMakeConnectedTool.RepeatShape(aDirID, aTimes);
}
// Print Error/Warning messages
BOPTest::ReportAlerts(TheMakeConnectedTool.GetReport());
// Set the history of the operation in session
BRepTest_Objects::SetHistory(TheMakeConnectedTool.History());
if (TheMakeConnectedTool.HasErrors())
return 0;
// Draw the result shape
const TopoDS_Shape& aResult = TheMakeConnectedTool.PeriodicShape();
DBRep::Set(theArgv[1], aResult);
return 0;
}
//=======================================================================
//function : MaterialsOn
//purpose :
//=======================================================================
Standard_Integer MaterialsOn(Draw_Interpretor& theDI,
Standard_Integer theArgc,
const char ** theArgv)
{
if (theArgc != 4)
{
theDI.PrintHelp(theArgv[0]);
return 1;
}
// Get the shape to get materials
TopoDS_Shape aShape = DBRep::Get(theArgv[3]);
if (aShape.IsNull())
{
theDI << "Error: " << theArgv[3] << " is a null shape.\n";
return 1;
}
// Get the sign of a shape
Standard_Boolean bPositive;
if (!strcmp("+", theArgv[2]))
bPositive = Standard_True;
else if (!strcmp("-", theArgv[2]))
bPositive = Standard_False;
else
{
theDI << theArgv[2] << " - invalid key.\n";
return 1;
}
const TopTools_ListOfShape& aLS = bPositive ?
TheMakeConnectedTool.MaterialsOnPositiveSide(aShape) :
TheMakeConnectedTool.MaterialsOnNegativeSide(aShape);
TopoDS_Shape aResult;
if (aLS.IsEmpty())
theDI << "No materials on this side.\n";
else if (aLS.Extent() == 1)
aResult = aLS.First();
else
{
BRep_Builder().MakeCompound(TopoDS::Compound(aResult));
for (TopTools_ListIteratorOfListOfShape it(aLS); it.More(); it.Next())
BRep_Builder().Add(aResult, it.Value());
}
DBRep::Set(theArgv[1], aResult);
return 0;
}

View File

@@ -6,6 +6,7 @@ BOPTest_CheckCommands.cxx
BOPTest_DrawableShape.cxx
BOPTest_DrawableShape.hxx
BOPTest_LowCommands.cxx
BOPTest_MkConnectedCommands.cxx
BOPTest_ObjCommands.cxx
BOPTest_Objects.cxx
BOPTest_Objects.hxx

View File

@@ -29,4 +29,5 @@
029 splitter
030 history
031 removefeatures
032 periodicity
032 periodicity
033 mkconnected

View File

@@ -0,0 +1,61 @@
polyline p 0 0 0 10 0 0 10 0 10 5 0 10 5 0 5 0 0 5 0 0 0
mkplane f p
prism b1 f 0 5 0
box b2 0 0 5 5 5 5
box b3 0 5 0 10 5 10
# make the shapes connected
makeconnected c b3 b1 b2
checkshape c
checknbshapes c -vertex 18 -edge 31 -wire 17 -face 17 -shell 3 -solid 3
checkprops c -s 900 -v 1000
savehistory h
modified m3 h b3
checknbshapes m3 -face 7
# make the shape periodic
cmakeperiodic cp -x 10 -y 10 -z 10
checknbshapes cp -vertex 26 -edge 42 -wire 20 -face 20 -shell 3 -solid 3
savehistory h
modified m1 h b1
checknbshapes m1 -face 10
modified m2 h b2
checknbshapes m2 -ref [nbshapes b2]
modified m3 h b3
checknbshapes m3 -face 8
# check material associations
explode b3 f
# the face b3_3 is REVERSED
# materials on negative side should be only b3
# materials on positive side should be b1 and b2
modified mf h b3_3
compound pos
compound neg
foreach f [explode mf f] {
if {![regexp "No materials on this side" [cmaterialson p + $f]]} {
add p pos
}
if {![regexp "No materials on this side" [cmaterialson n - $f]]} {
add n neg
}
}
# check that neg contains b3 only
checkprops neg -equal b3 -skip
# check that pos contains both
compound b1 b2 comp
checkprops pos -equal comp -skip

View File

@@ -0,0 +1,90 @@
box b1 10 10 5
box b2 0 0 5 10 10 1
box b3 0 0 6 3 3 4
polyline p 3 0 6 3 3 6 0 3 6 0 4 6 4 4 6 4 0 6 3 0 6
mkplane f p
prism b4 f 0 0 4
box b5 0 9 6 4 1 4
box b6 9 9 6 1 1 4
box b7 9 0 6 1 3 4
# make the solids connected
makeconnected c b1 b2 b3 b4 b5 b6 b7
checkshape c
checknbshapes c -vertex 46 -edge 79 -wire 41 -face 41 -shell 7 -solid 7
checkprops c -s 888 -v 696
savehistory h
explode b6 f
modified f h b6_4
cmaterialson pos + f
if {![regexp "equal shapes" [compare pos b6]]} {
puts "Error: incorrect material associations"
}
if {![regexp "No materials on this side." [cmaterialson neg - f]]} {
puts "Error: incorrect material associations"
}
# make the connected shape periodic
cmakeperiodic cp -x 10 -y 10
checkshape cp
checknbshapes cp -vertex 49 -edge 83 -wire 42 -face 42 -shell 7 -solid 7
checkprops cp -s 888 -v 696
# get modifications of the fifth solid
savehistory h
modified m5 h b5
checknbshapes m5 -vertex 10 -edge 15 -wire 7 -face 7 -shell 1 -solid 1
checkprops m5 -s 48 -v 16
# repeat shape
crepeatshape res1 -x -1 -y -1 -x 1 -y 1
checknbshapes res1 -vertex 496 -edge 947 -wire 564 -face 564 -shell 112 -solid 112
checkprops res1 -s 14208 -v 11136
savehistory h
modified f h b6_4
cmaterialson pos + f
cmaterialson neg - f
if {![regexp "equal shapes" [compare pos b6]]} {
puts "Error: incorrect material associations"
}
if {![regexp "equal shapes" [compare neg b7]]} {
puts "Error: incorrect material associations"
}
# make the connected shape periodic with period grater than the unit cell
cmakeperiodic cp -x 12 -trim -1 -y 12 -trim -1
checknbshapes cp -ref [nbshapes c]
checkprops cp -equal c
crepeatshape res2 -x -1 -y -1 -x 1 -y 1
checknbshapes res2 -vertex 736 -edge 1264 -wire 656 -face 656 -shell 112 -solid 112
checkprops res2 -s 14208 -v 11136
savehistory h
modified f h b6_4
cmaterialson pos + f
if {![regexp "equal shapes" [compare pos b6]]} {
puts "Error: incorrect material associations"
}
if {![regexp "No materials on this side." [cmaterialson neg - f]]} {
puts "Error: incorrect material associations"
}