How to partially stretch a model - biselection modification tutorial

trCAD features introduced:

A function that is often needed in a customization setting is fitting parts of a model to some length defined only at runtime. A popular application is fitting the nose part for a premade frame for glasses to a desired width, but this type of only changing a part of a model while leaving the rest unchanged is needed in many different scenarios.

In trCAD this can be achieved by the special biselection modification function biselect_mod(). Here we explain it for a partial stretching, where one part of the model is moved linearly relative to the rest of the part, which stays static, but the biselection modification can as well be used for different combinations of modifiers in general.

A configurator using the trcad script explained in this tutorial could look like this. The control for stretching the part in this example is the slider input at the right side.

For defining which part of the model shall be stretched, two selections are used. Then one modifier is defined per selection, that should be applied to that selection. In the case of a simple, partial stretch this is the translation modifier for the selected part that should be moved. For the selected part that should be static the identity modifier can be used. The part of the model that is not within one of the selections is used as an “elastic region” to smoothly generate a transition between the selected modified parts.

The code snipped for defining the call of the biselect_mod() function for a partial stretch would look like that:

solid oSolid_stretch = biselect_mod( oSolid, oSelStatic, mModStatic, oSelMove, mModMove )

where oSolid_stretch is the partly stretched solid, oSolid is the original solid to be stretched, oSelStatic is the selection defining the part that should stay static, mModStatic is the corresponding modifier, identity() in this case, oSelMove is the selection defining the part that should move, and mModMove the corresponding modifier, a translation in this case.

To keep it simple, in this example we use selections that consist of one selectbox each. A selectbox is defined by its boundaries in the coordinate system: minimal and maximal value in x-direction, in y-direction and in z-direction, 6 values in total. The selectbox selects every part of the model that is inside these boundaries.

In code defining a selectbox named oSel looks like that:

selectbox oSel = selectbox( minX, maxX, minY, maxY, minZ, maxZ )

where minX, maxX, minY, maxY, minZ, maxZ are the minimal and maximal float values for the three coordinates defining the boundaries.

One thing to keep in mind when stretching models is the limits of tessellation in the elastic region. The biselection modification does only change the position of vertices, but no new ones are inserted. If the mesh in the elastic region is tessellated with few triangles there may not be enough to generate a smooth stretched model, which will be visible in the result. To overcome this, trCAD provides the modifier subdiv for refining the mesh before doing the stretch.

There are three variants of the subdiv, the first for subdividing any triangle edge that is longer than a defined maximum length, and a second and a third for only doing it if the edge is too long projected along one or two defined vector(s), respectively.

In code, defining and using the subdiv modifier for the three variants looks like this:

oSolid_subdiv = subdiv( maxlen ) >> oSolid
oSolid_subdiv = subdiv( maxlen, v1 ) >> oSolid
oSolid_subdiv = subdiv( maxlen, v1, v2 ) >> oSolid

where oSolid is the original solid, oSolid_subdiv is the subdivided solid, maxlen is the maximum allowed length for the edges, v1 and v2 are the projection vectors along which to check if the edges are too long.

Let’s make a small example configurator illustrating the usage. In the example a torus will be stretched partly along its main rotation axis, changing its shape from a ring more towards a tire. The amount of stretching will be adjustable through user input using an open parameter. To get a nicer result in the elastic region the mesh will be refined before stretching. This script is used for the example configurator at the beginning of this tutorial as well.

// The object to be stretched

solid oTorus

float dTorusRad = 100.0
float dTorusTubeRad = 30.0

oTorus = torus(dTorusRad, dTorusTubeRad)

// The extent of the torus in directions x,y,z, used for the selectboxes

float dTorus_maxX = dTorusRad + dTorusTubeRad
float dTorus_minX = - dTorus_maxX

float dTorus_maxY = dTorus_maxX
float dTorus_minY = - dTorus_maxX

float dTorus_maxZ = dTorusTubeRad
float dTorus_minZ = - dTorusTubeRad

// Define the height in z direction for the elastic region for stretching

float dElasticHeight = 20.0

// Define the two selectboxes:

// One for the part that gets moved and one for the part that stays static.
// Stretching will be in z direction. We need two parallel boxes that include
// the whole torus in x- and y-direction, but only the upper and lower part in
// z-direction, separated by the elastic height. The upper part will be moved,
// the lower part will stay static.

// selectbox takes 6 values: minX, maxX, minY, maxY, minZ, maxZ

selectbox oSelMove = selectbox( dTorus_minX, dTorus_maxX, dTorus_minY, dTorus_maxY, 0.5 * dElasticHeight, dTorus_maxZ)

selectbox oSelStatic = selectbox( dTorus_minX, dTorus_maxX, dTorus_minY, dTorus_maxY, dTorus_minZ , - 0.5 * dElasticHeight)

// For defining the distance the upper part should be moved via user input, we
// use an open parameter and set the input to have the form of a slider using
// the style attribute

open float dMoveDistance
  name = "Stretching"
  descr = "How much to stretch the object [mm]"
  value = 0.0
  min = 0.0
  max = 30.0
  style = "display: range;"

// The modifiers applied to the selected regions for stretching:
// Translate the one part in z-direction, leave the other unchanged

modifier mMove = translation(<[0, 0, dMoveDistance]>)
modifier mStatic = identity()

// Subdivide the mesh for better stretching results.
// It is sufficient to do that in stretch direction z, vector <[0,0,1]>.

oTorus = subdiv( 3.0, <[0,0,1]> ) >> oTorus

// Doing the stretch and making the result

oTorus = biselect_mod( oTorus, oSelMove, mMove, oSelStatic, mStatic )

make oTorus