ShapeJS Developer Tutorials

Volume Patterns

ShapeJS has mathematical volumetric patterns built into it, which it can load on command. In this tutorial, we will learn how to load volume patterns and how to use them to modify other data sources.

1. Loading Volume Patterns

Let's try out a Schwarz D volume pattern.

function main(args) {
    var period = 12*MM;
    var thickness = 1*MM;
    var pattern = new VolumePatterns.SchwarzD(period, thickness);
    var s = 10*MM;
    return new Scene(pattern,new Bounds(-s,s,-s,s,-s,s));
}

2. Limiting Volume Patterns

As you can see, these patterns will, by default, fill the entire scene and stop only when they run into the bounding box. In order to avoid this happening, we will need to use Booleans to limit the size of the Gyroid. Booleans were covered in a previous tutorial. Even though this is just a sphere, we can already see how much more interesting it becomes with a volume pattern involved.

function main(args) {
    var period = 12*MM;
    var thickness = 1*MM;
    var pattern = new VolumePatterns.SchwarzD(period, thickness);
    var s = 10*MM;
    return new Scene(pattern,new Bounds(-s,s,-s,s,-s,s));
}

3. Volume Patterns and Models

Alternately, we could apply the pattern itself as an Intersection (we could also do a Subtraction) to a different data source. This is useful in hollowing out shapes, so as to use less material and come up with something that will ultimately be cheaper to print. However, keep in mind that being able to model something in ShapeJS does not necessarily mean that it can be safely printed. You need to look at your model and see if parts of it have become disconnected or extremely thin.

function main(args) {
  var blend = 1*MM;
  //Turret Variables
  var turret_top = new Vector3d(0, 18*MM, 0);
  var turret_bottom = new Vector3d(0, 12*MM, 0);
  var remove_top = new Vector3d(0, 18.1*MM, 0);
  var remove_bottom = new Vector3d(0, 14*MM, 0);
  var remove_radius = 10*MM;
  var turret_radius = 12*MM;
  var remove_box_angle = Math.PI*(1+2/3);
  var remove_box_size = 4*MM;
  var remove_box_y = 18*MM;
  var remove_cone_apex = new Vector3d(0, 29*MM, 0);
  var remove_cone_angle = Math.PI/4;
  //Trunk Variables
  var trunk_scaley = 20*MM;
  var trunk_scalexz = 12*MM;
  var xz_start = 6*MM;
  var y_start = 10*MM;
  var xz_end = 8*MM;
  var y_end = 12*MM;
  //Base Variables
  var base1_top = new Vector3d(0, -10*MM, 0);
  var base1_bottom = new Vector3d(0, -12*MM, 0);
  var base2_bottom = new Vector3d(0, -13*MM, 0);
  var base3_bottom = new Vector3d(0, -18*MM, 0);
  var base1_radius = 10*MM;
  var base2_radius = 12*MM;
  var base_torus_radius = 1.5*MM;

  //Turret Data Sources
  var turret = new Cylinder(turret_bottom, turret_top, turret_radius);
  var remove_cyl = new Cylinder(remove_top, remove_bottom, remove_radius);
  var remove_box1 = new Box(0,remove_box_y,0,remove_box_size,remove_box_size,1);
  var remove_box2 = new Box(0,remove_box_y,0,remove_box_size,remove_box_size,1);
  remove_box2.setTransform(new Rotation(0,1,0,remove_box_angle));
  var remove_box3 = new Box(0,remove_box_y,0,remove_box_size,remove_box_size,1);
  remove_box3.setTransform(new Rotation(0,1,0,-remove_box_angle));
  var cone = new Cone(remove_cone_apex, new Vector3d(0,-1,0), remove_cone_angle);
  var remove_cone = new Complement(cone);
  var remove = new Union();
  remove.add(remove_cyl);
  remove.add(remove_box1);
  remove.add(remove_box2);
  remove.add(remove_box3);
  var turret_carved_initial = new Subtraction(turret, remove);
  var turret_carved = new Subtraction(turret_carved_initial, remove_cone);
  turret_carved.setBlend(blend);
  //Trunk Data Sources
  var trunk = new Box(trunk_scalexz, trunk_scaley, trunk_scalexz);
  var grow_side1 = new Plane(new Vector3d(0,-1,1), new Vector3d(xz_start, y_start, xz_start));
  var grow_side2 = new Plane(new Vector3d(0,-1,-1), new Vector3d(xz_start, y_start, -xz_start));
  var grow_side3 = new Plane(new Vector3d(1,-1,0), new Vector3d(xz_start, y_start, xz_start));
  var grow_side4 = new Plane(new Vector3d(-1,-1,0), new Vector3d(-xz_start, y_start, xz_start));
  var grow_top = new Plane(new Vector3d(0,1,0), new Vector3d(0, y_end, 0));
  var grow = new Intersection();
  grow.add(grow_side1);
  grow.add(grow_side2);
  grow.add(grow_side3);
  grow.add(grow_side4);
  grow.add(grow_top);
  //Base Data Sources
  var base1 = new Cylinder(base1_top, base1_bottom, base1_radius);
  var base2 = new Cylinder(base1_bottom, base2_bottom, base1_radius, base2_radius);
  var base3 = new Cylinder(base2_bottom, base3_bottom, base2_radius, base2_radius);
  var base_torus1 = new Torus(base2_radius, base_torus_radius);
  compTransform = new CompositeTransform();
  base_torus1.setTransform(compTransform);
  compTransform.add(new Rotation(1,0,0,Math.PI/2));
  compTransform.add(new Translation(0, -14.5*MM, 0));
  var base_torus2 = new Torus(base2_radius, base_torus_radius);
  compTransform2 = new CompositeTransform();
  base_torus2.setTransform(compTransform2);
  compTransform2.add(new Rotation(1,0,0,Math.PI/2));
  compTransform2.add(new Translation(0, -17.5*MM, 0));
  var base_blended = new Union();
  base_blended.getParam("blend").setValue(blend);
  base_blended.add(base3);
  base_blended.add(base_torus1);
  base_blended.add(base_torus2);

  //Assembly
  var assembled = new Union();
  assembled.add(turret_carved);
  assembled.add(grow);
  assembled.add(trunk);
  assembled.add(base1);
  assembled.add(base_blended);
  assembled.add(base2);

  //Smooth the Base
  var base_smooth = new Cylinder(base3_bottom, new Vector3d(0,-1,0), 1);
  var assembled_based = new Subtraction(assembled, base_smooth);

  //Add Pattern
  var pattern = new VolumePatterns.SchwarzD(12*MM, 1*MM);
  var patterned = new Intersection(pattern, assembled_based);
  patterned.setBlend(blend);

  //Display
  var s = 20*MM;
  return new Scene(patterned, new Bounds(-s,s,-s,s,-s,s));
}

4. Printing with Volume Patterns

In this case we seem to have made something that will be too thin to safely print, not to mention that it does not look that great. There is even a part of the turret which has become completely disconnected. To fix this, while still using our pattern, let's adjust the thickness parameter in order to create a less fragile piece whilst still keeping the look we want. We should also up the period, since the repetition of the pattern is too obvious. We want to go more for the look of a thoroughly worm-burrowed piece, without a clear pattern. Structural stability, again, is important.

function main(args) {
  var blend = 1*MM;
  //Turret Variables
  var turret_top = new Vector3d(0, 18*MM, 0);
  var turret_bottom = new Vector3d(0, 12*MM, 0);
  var remove_top = new Vector3d(0, 18.1*MM, 0);
  var remove_bottom = new Vector3d(0, 14*MM, 0);
  var remove_radius = 10*MM;
  var turret_radius = 12*MM;
  var remove_box_angle = Math.PI*(1+2/3);
  var remove_box_size = 4*MM;
  var remove_box_y = 18*MM;
  var remove_cone_apex = new Vector3d(0, 29*MM, 0);
  var remove_cone_angle = Math.PI/4;
  //Trunk Variables
  var trunk_scaley = 20*MM;
  var trunk_scalexz = 12*MM;
  var xz_start = 6*MM;
  var y_start = 10*MM;
  var xz_end = 8*MM;
  var y_end = 12*MM;
  //Base Variables
  var base1_top = new Vector3d(0, -10*MM, 0);
  var base1_bottom = new Vector3d(0, -12*MM, 0);
  var base2_bottom = new Vector3d(0, -13*MM, 0);
  var base3_bottom = new Vector3d(0, -18*MM, 0);
  var base1_radius = 10*MM;
  var base2_radius = 12*MM;
  var base_torus_radius = 1.5*MM;

  //Turret Data Sources
  var turret = new Cylinder(turret_bottom, turret_top, turret_radius);
  var remove_cyl = new Cylinder(remove_top, remove_bottom, remove_radius);
  var remove_box1 = new Box(0,remove_box_y,0,remove_box_size,remove_box_size,1);
  var remove_box2 = new Box(0,remove_box_y,0,remove_box_size,remove_box_size,1);
  remove_box2.setTransform(new Rotation(0,1,0,remove_box_angle));
  var remove_box3 = new Box(0,remove_box_y,0,remove_box_size,remove_box_size,1);
  remove_box3.setTransform(new Rotation(0,1,0,-remove_box_angle));
  var cone = new Cone(remove_cone_apex, new Vector3d(0,-1,0), remove_cone_angle);
  var remove_cone = new Complement(cone);
  var remove = new Union();
  remove.add(remove_cyl);
  remove.add(remove_box1);
  remove.add(remove_box2);
  remove.add(remove_box3);
  var turret_carved_initial = new Subtraction(turret, remove);
  var turret_carved = new Subtraction(turret_carved_initial, remove_cone);
  turret_carved.setBlend(blend);
  //Trunk Data Sources
  var trunk = new Box(trunk_scalexz, trunk_scaley, trunk_scalexz);
  var grow_side1 = new Plane(new Vector3d(0,-1,1), new Vector3d(xz_start, y_start, xz_start));
  var grow_side2 = new Plane(new Vector3d(0,-1,-1), new Vector3d(xz_start, y_start, -xz_start));
  var grow_side3 = new Plane(new Vector3d(1,-1,0), new Vector3d(xz_start, y_start, xz_start));
  var grow_side4 = new Plane(new Vector3d(-1,-1,0), new Vector3d(-xz_start, y_start, xz_start));
  var grow_top = new Plane(new Vector3d(0,1,0), new Vector3d(0, y_end, 0));
  var grow = new Intersection();
  grow.add(grow_side1);
  grow.add(grow_side2);
  grow.add(grow_side3);
  grow.add(grow_side4);
  grow.add(grow_top);
  //Base Data Sources
  var base1 = new Cylinder(base1_top, base1_bottom, base1_radius);
  var base2 = new Cylinder(base1_bottom, base2_bottom, base1_radius, base2_radius);
  var base3 = new Cylinder(base2_bottom, base3_bottom, base2_radius, base2_radius);
  var base_torus1 = new Torus(base2_radius, base_torus_radius);
  compTransform = new CompositeTransform();
  base_torus1.setTransform(compTransform);
  compTransform.add(new Rotation(1,0,0,Math.PI/2));
  compTransform.add(new Translation(0, -14.5*MM, 0));
  var base_torus2 = new Torus(base2_radius, base_torus_radius);
  compTransform2 = new CompositeTransform();
  base_torus2.setTransform(compTransform2);
  compTransform2.add(new Rotation(1,0,0,Math.PI/2));
  compTransform2.add(new Translation(0, -17.5*MM, 0));
  var base_blended = new Union();
  base_blended.getParam("blend").setValue(blend);
  base_blended.add(base3);
  base_blended.add(base_torus1);
  base_blended.add(base_torus2);

  //Assembly
  var assembled = new Union();
  assembled.add(turret_carved);
  assembled.add(grow);
  assembled.add(trunk);
  assembled.add(base1);
  assembled.add(base_blended);
  assembled.add(base2);

  //Smooth the Base
  var base_smooth = new Cylinder(base3_bottom, new Vector3d(0,-1,0), 1);
  var assembled_based = new Subtraction(assembled, base_smooth);

  //Add Pattern
  var pattern = new VolumePatterns.SchwarzD(28*MM, 4.5*MM);
  var patterned = new Intersection(pattern, assembled_based);
  patterned.setBlend(blend);

  //Display
  var s = 20*MM;
  return new Scene(patterned, new Bounds(-s,s,-s,s,-s,s));
}

There are many more Volume Patterns available to you. All are listed in the documentation. Try setting them up with the Rook example.