Commit 41638f21 authored by David Thompson's avatar David Thompson
Browse files

Expose CGM's sweep helix op and fix bugs.

This branch includes initial support for helical sweeps,
but be aware that only Cubit/ACIS support this kind of
sweep; the CGM OpenCascade backend does not yet perform
sweeps.

This also fixes a bug in `ModelEntityItem::appendValue()`.
This bug causes issues when `Operator::associateEntity()` is
called and the association has a minimum number of expected
values; because there are not default UUID values, the first
numberOfValues() entries are null UUIDs; the associated entity
is appended to the end of this array, which leaves null values
at the front of the array. So, even though it is slow, we must
verify that existing slots for model entities are actually in use.

Finally, this branch tweaks the smtk.simple API a little bit:

* Add CreateCylinder to simple python API.
* Solid modeling operations print the error
  log when an operation does not succeed.
parent 87cbd30b
......@@ -77,7 +77,6 @@ endif()
#turn on ctest if we want testing
if (SMTK_ENABLE_TESTING)
message(STATUS "SMTK_ENABLE_TESTING")
enable_testing()
include(CTest)
......
......@@ -140,7 +140,17 @@ bool ModelEntityItem::setValue(std::size_t i, const smtk::model::EntityRef& val)
bool ModelEntityItem::appendValue(const smtk::model::EntityRef& val)
{
// First - are we allowed to change the number of values?
// First - are there unset values waiting to be set?
std::size_t n = this->numberOfValues();
for (std::size_t i = 0; i < n; ++i)
{
if (!this->isSet(i))
{
this->setValue(i, val);
return true;
}
}
// Second - are we allowed to change the number of values?
const ModelEntityItemDefinition* def =
static_cast<const ModelEntityItemDefinition *>(this->definition().get());
if (
......
......@@ -53,7 +53,7 @@ namespace smtk {
smtk::model::OperatorResult Sweep::operateInternal()
{
// 0 = extrude, 1 = revolve, 2 = sweep along curve:
// 0 = extrude, 1 = revolve, 2 = helix, 3 = sweep along curve:
int sweepOp = this->findInt("construction method")->value();
int keepInputs = this->findInt("keep inputs")->value();
smtk::model::EntityRefArray expunged;
......@@ -61,7 +61,7 @@ smtk::model::OperatorResult Sweep::operateInternal()
DLIList<RefEdge*> cgmSweepPath;
bool ok = true;
ok &= this->cgmEntities(*this->specification()->associations().get(), cgmThingsToSweep, keepInputs, expunged);
if (sweepOp == 2) // sweep along curve
if (sweepOp == 3) // sweep along curve
ok &= this->cgmEntities(*this->findModelEntity("sweep path").get(), cgmSweepPath, keepInputs, expunged);
if (!ok)
......@@ -152,7 +152,31 @@ smtk::model::OperatorResult Sweep::operateInternal()
);
}
break;
case 2: // sweep along path
case 2: // helix
{
double helixAngle = this->findDouble("helix angle")->value();
double pitch = this->findDouble("pitch")->value();
bool righthanded = this->findInt("handedness")->value() ? true : false;
smtk::attribute::DoubleItemPtr sweepBaseItem = this->findDouble("axis base point");
smtk::attribute::DoubleItemPtr sweepAxisItem = this->findDouble("axis of revolution");
std::vector<double> sweepBase(sweepBaseItem->begin(), sweepBaseItem->end());
std::vector<double> sweepAxis(sweepAxisItem->begin(), sweepAxisItem->end());
CubitVector cbtSweepBase(&sweepBase[0]);
CubitVector cbtSweepAxis(&sweepAxis[0]);
s = GeometryModifyTool::instance()->sweep_helical(
cgmThingsToSweep,
cbtSweepBase,
cbtSweepAxis,
pitch,
helixAngle,
righthanded,
/* anchor_entity, unused */ CUBIT_FALSE,
keepInputs,
cgmResults
);
}
break;
case 3: // sweep along path
{
s = GeometryModifyTool::instance()->sweep_along_curve(
cgmThingsToSweep,
......
......@@ -89,10 +89,44 @@
</DetailedDescription>
</Double>
<Double Name="sweep angle" NumberOfRequiredValues="1">
<Min Inclusive="true">-360.0</Min>
<Max Inclusive="true">360.</Max>
<DefaultValue>360.</DefaultValue>
<BriefDescription>The angle (in degrees) through which the associated entities should be revolved.</BriefDescription>
</Double>
<!-- Option 3: curves (sweep path) -->
<!-- Option 3: axis base point, axis of revolution, pitch, helix angle, and handedness -->
<Double Name="helix angle" NumberOfRequiredValues="1">
<DefaultValue>360.</DefaultValue>
<BriefDescription>The angle (in degrees) through which the associated entities should be revolved.</BriefDescription>
</Double>
<Double Name="pitch" NumberOfRequiredValues="1">
<DefaultValue>1.</DefaultValue>
<BriefDescription>The distance between corresponding points measured along the axis of the sweep.</BriefDescription>
</Double>
<Int Name="handedness" NumberOfRequiredValues="1">
<BriefDescription>Should the helix be right- or left-handed?</BriefDescription>
<DetailedDescription>
A helix translates geometry along the direction of the axis of revolution while
at the same time rotating it tangent to this axis.
A right-handed helix performs a positive translation (i.e., codirectional with the axis)
while rotating in a direction given by aligning a right thumb with the axis and curling
the fingers toward the thumb.
A left-handed helix rotates the opposite direction for the same positive translation.
</DetailedDescription>
<ChildrenDefinitions/>
<DiscreteInfo DefaultIndex="1">
<!-- Values from CGM's GeometryType enum in util/GeometryDefines.h -->
<Structure>
<Value Enum="left-handed">0</Value>
<Items/>
</Structure>
<Structure>
<Value Enum="right-handed">1</Value>
<Items/>
</Structure>
</DiscreteInfo>
</Int>
<!-- Option 4: curves (sweep path) -->
<ModelEntity Name="sweep path" NumberOfRequiredValues="1" Extensible="true">
<MembershipMask>edge</MembershipMask>
<BriefDescription>The curve along which to sweep the associated entities.</BriefDescription>
......
......@@ -10,7 +10,6 @@ set(smtkCGMSessionPythonDataTests
cgmLoadFile
)
message("PYTHONPATH=${SHIBOKEN_SMTK_PYTHON};${LIB_ENV_VAR}")
foreach (test ${smtkCGMSessionPythonTests})
add_test(${test}Py ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/${test}.py)
set_tests_properties(${test}Py
......
......@@ -64,10 +64,17 @@ res = u1.operate()
su = res.findModelEntity('entities').value(0)
# Note that su has same UUID as sph2
# Test cylinder creation.
from smtk.simple import *
SetActiveSession(sref)
cyl = CreateCylinder(top_radius=1.0)
#json = smtk.io.ExportJSON.fromModelManager(mgr)
#sphFile = open('/tmp/s3.json', 'w')
#print >> sphFile, json
#sphFile.close()
#cylFile = open('cyl.json', 'w')
#print >> cylFile, json
#cylFile.close()
#
# Now verify that mgr.closeSession removes the entity record for the session.
......
......@@ -64,7 +64,8 @@ class SurfaceType:
class SweepType:
EXTRUDE = 0
REVOLVE = 1
ALONG_PATH = 2
HELIX = 2
ALONG_PATH = 3
class DraftType:
RECTANGULAR = 1
......@@ -87,6 +88,12 @@ def GetVectorValue(item):
N = item.numberOfValues()
return [item.value(i) for i in range(N)]
def PrintResultLog(res, always = False):
"""Given an operator result, print log messages if unsuccessful."""
if always or res.findInt('outcome').value(0) != smtk.model.OPERATION_SUCCEEDED:
slog = res.findString('log')
print '\n'.join([slog.value(i) for i in range(slog.numberOfValues())])
def CreateSphere(**args):
"""Create a sphere.
......@@ -113,9 +120,50 @@ def CreateSphere(**args):
cc = cs.findAsDouble('center')
SetVectorValue(cc, args['center'])
res = cs.operate()
PrintResultLog(res)
sph = res.findModelEntity('entities').value(0)
return sph
def CreateCylinder(**args):
"""Create a generalized cylinder (a truncated elliptical cone).
This method accepts optional `height`, `radius`, `minor_radius`,
and `top_radius` arguments; it returns an EntityRef pointing to the
new cylinder.
The cylinder base is always at the origin and has its axis of
radial symmetry on the z axis. Use the Translate() and Rotate()
operators to place the cylinder as required.
Example
-------
.. code:: python
cylinder = CreateCylinder(radius=1.0, height=1.0)
When unspecified, arguments take on their default values:
* `height` defaults to 1.
* `radius` defaults to 1.
* `minor_radius` defaults to 1.
* `top_radius` defaults to 0.
"""
sess = GetActiveSession()
cs = sess.op('create cylinder')
if 'radius' in args:
cs.findAsDouble('major base radius').setValue(args['radius'])
if 'minor_radius' in args:
cs.findAsDouble('minor base radius').setValue(args['minor_radius'])
if 'top_radius' in args:
cs.findAsDouble('major top radius').setValue(args['top_radius'])
if 'height' in args:
cs.findAsDouble('height').setValue(args['height'])
res = cs.operate()
PrintResultLog(res)
cyl = res.findModelEntity('entities').value(0)
return cyl
def CreateBrick(**args):
"""Create a rectangular cuboid (6 rectangular faces).
......@@ -161,6 +209,7 @@ def CreateBrick(**args):
ctrVal = args['center']
SetVectorValue(cb.findAsDouble('center'), ctrVal)
res = cb.operate()
PrintResultLog(res)
brick = res.findModelEntity('entities').value(0)
return brick
......@@ -183,6 +232,7 @@ def Intersect(bodies, **args):
except:
op.associateEntity(bodies)
res = op.operate()
PrintResultLog(res)
return res.findModelEntity('entities').value(0)
def Union(bodies, **args):
......@@ -204,6 +254,7 @@ def Union(bodies, **args):
except:
op.associateEntity(bodies)
res = op.operate()
PrintResultLog(res)
return res.findModelEntity('entities').value(0)
def Subtract(workpiece, tool, **args):
......@@ -236,6 +287,7 @@ def Subtract(workpiece, tool, **args):
SetVectorValue(op.findModelEntity('tools',smtk.attribute.ALL_CHILDREN), tool)
res = op.operate()
PrintResultLog(res)
return res.findModelEntity('entities').value(0)
def Translate(bodies, vec):
......@@ -248,6 +300,7 @@ def Translate(bodies, vec):
top.associateEntity(bodies)
SetVectorValue(top.findAsDouble('offset'),vec)
res = top.operate()
PrintResultLog(res)
return GetVectorValue(res.findModelEntity('entities'))
def CreateVertex(pt, **kwargs):
......@@ -257,7 +310,7 @@ def CreateVertex(pt, **kwargs):
crv = sref.op('create vertex')
x = crv.findAsDouble('point')
c = crv.findAsInt('color')
if c and kwargs['color']:
if c and 'color' in kwargs:
c.setValue(0, kwargs['color'])
SetVectorValue(x, pt)
return crv.operate().findModelEntity('vertex').value(0)
......@@ -322,7 +375,22 @@ def Sweep(stuffToSweep, method = SweepType.EXTRUDE, **kwargs):
SetVectorValue(base, kwargs['base_point'])
if 'sweep_angle' in kwargs:
angl = swp.findAsDouble('sweep angle').setValue(0, kwargs['sweep_angle'])
return swp.operate().findModelEntity('entities').value(0)
elif method == SweepType.HELIX:
if 'axis' in kwargs:
axis = swp.findAsDouble('axis of revolution')
SetVectorValue(axis, kwargs['axis'])
if 'base_point' in kwargs:
base = swp.findAsDouble('axis base point')
SetVectorValue(base, kwargs['base_point'])
if 'helix_angle' in kwargs:
angl = swp.findAsDouble('helix angle').setValue(0, kwargs['helix_angle'])
if 'pitch' in kwargs:
pitch = swp.findAsDouble('pitch').setValue(0, kwargs['pitch'])
if 'handedness' in kwargs:
angl = swp.findAsInt('handedness').setValue(0, kwargs['handedness'])
res = swp.operate()
PrintResultLog(res)
return res.findModelEntity('entities').value(0)
def Write(filename, entities = [], **kwargs):
"""Write a set of entities to an file (in the native modeling kernel format).
......@@ -333,4 +401,6 @@ def Write(filename, entities = [], **kwargs):
[wri.associateEntity(entity) for entity in entities]
if 'filetype' in kwargs:
wri.findAsString('filetype').setValue(0, kwargs['filetype'])
return wri.operate().findInt('outcome').value(0)
res = wri.operate()
PrintResultLog(res)
return res.findInt('outcome').value(0)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment