# ShapeJS Developer Tutorials

Debugging

## Distance Operations

The voxel operations provided in ShapeJS are ways of combining the distances of voxels from the origin from two different data sources. In this Tutorial we will learn how to use these advanced operations, which take the interaction of two data sources to produce a third one.

This and the subtract (Sub) function might seem like Booleans from their names. However, rather than use one shape to either add to or remove from the other, these actually combine the two, not as a Boolean Union, but as a sort of distortion shaped like the second data source. This allows for the creation of unusual shapes just by combining primitives.

```function main(args) {
var r = 15*MM;
var sphere = new Torus(r, r/5);
var box = new Box(0,0,0, 1.5*r,1.5*r, 1.5*r);

var s = 26*MM;
return new Scene(shape,new Bounds(-s,s,-s,s,-s,s));
}
```
###### Try it!

(Note: These work off of the vector of each voxel as related to the origin. For this reason, if one data source or the other does not reach in a certain direction, nothing will display there, since nothing can be added. )

### 2. Subtract

Unlike a Boolean Subtraction, Sub does not remove any of the data source, but rather subtracts from the distance from the origin.

```function main(args) {
var r = 15*MM;
var sphere = new Torus(r, r/5);
var box = new Box(0,0,0, 1.5*r,1.5*r, 1.5*r);
var shape = new Sub(sphere,box);

var s = 26*MM;
return new Scene(shape,new Bounds(-s,s,-s,s,-s,s));
}
```

### 3. Mix

The Mix function is perhaps the most commonly useful of this set of functions. It allows you to create a blended shape based on two other shapes. The value for this transformation (the third argument in the function) is essentially the ratio of the two, zero being entirely the first data source, one entirely the second. Let's try putting this one to work, to try and finish up our coffee cup maker.

Our coffee cup has a rather boring handle, and probably not too comfortable of one. What we need to do is to give it more of a usual handle shape.

```var uiParams = [
{
name: "height",
label: "Cup height",
desc: "Height of the cup",
type: "double",
rangeMin: 50,
rangeMax: 200,
step: 1,
defaultVal: 120
},
{
name: "thickness",
label: "Cup Thickness",
desc: "Thickness of the cup",
type: "double",
rangeMin: 3,
rangeMax: 15,
step: 0.5,
defaultVal: 5
},
{
type: "double",
rangeMin: 25,
rangeMax: 120,
step: 1,
defaultVal: 60
},
{
name: "h_thickness",
label: "Handle Thickness",
desc: "Thickness of the handle",
type: "double",
rangeMin: 3,
rangeMax: 15,
step: 0.25,
defaultVal: 5
},
{
type: "double",
rangeMin: 10,
rangeMax: 100,
step: 1,
defaultVal: 35
},
{
name: "mixer",
label: "Handle Squaring",
desc: "Squaring of the handle",
type: "double",
rangeMin: 0,
rangeMax: 1,
step: 0.1,
defaultVal: 0
}
];

function main(args) {
//Retrieve and scale shopper inputs
var height = args.height*MM;
var thickness = args.thickness*MM;
var thickness = 5*MM;
}

var h_thickness = args.h_thickness*MM;

//Base Solid shape
var base = new Union();
var base_cyl = new Cylinder(new Vector3d(0,-height/2, 0), new Vector3d(0,height/2, 0), radius);
var base_rim = new Torus(radius-thickness/2, thickness/2);
var compTransform = new CompositeTransform();
base_rim.setTransform(compTransform);

//handle_sq.setBlend(1*MM);
var handle_base = new Mix(handle_cyl, handle_sq, args.mixer);
var h_remove = new Mix(h_remove_cyl, h_remove_sq, args.mixer);
var handle = new Subtraction(handle_base, h_remove);
var seat = new Cylinder(new Vector3d(0,-height/2, 0), new Vector3d(0,-height/2+thickness, 0), radius);

//Portions for removal
var remove_cyl = new Cylinder(new Vector3d(0,(-height+thickness)/2,0), new Vector3d(0,(height+thickness)/2,0), radius-thickness);

//Assembly
var cup = new Subtraction(base, remove_cyl);
var s = 250*MM;
return new Scene(cup,new Bounds(-s,s,-s,s,-s,s));
}
```
###### Try it!

(Note: Unlike simple Transformations, Mix creates an entirely new data source.)

By adding in the removal portions though, we've created a problem that could crash our maker. Specifically, is could try to create a cylinder with a negative radius if someone create a very small radius but very high thickness handle.

Also, if we have a thin handle, high handle radius, and set the squaring near the midpoint it can cause the handle to split into three , so we'll deal with that as well.

```var uiParams = [
{
name: "height",
label: "Cup height",
desc: "Height of the cup",
type: "double",
rangeMin: 50,
rangeMax: 200,
step: 1,
defaultVal: 120
},
{
name: "thickness",
label: "Cup Thickness",
desc: "Thickness of the cup",
type: "double",
rangeMin: 3,
rangeMax: 15,
step: 0.5,
defaultVal: 5
},
{
type: "double",
rangeMin: 25,
rangeMax: 120,
step: 1,
defaultVal: 60
},
{
name: "h_thickness",
label: "Handle Thickness",
desc: "Thickness of the handle",
type: "double",
rangeMin: 3,
rangeMax: 15,
step: 0.25,
defaultVal: 5
},
{
type: "double",
rangeMin: 10,
rangeMax: 100,
step: 1,
defaultVal: 35
},
{
name: "mixer",
label: "Handle Squaring",
desc: "Squaring of the handle",
type: "double",
rangeMin: 0,
rangeMax: 1,
step: 0.1,
defaultVal: 0
}
];

function main(args) {
//Retrieve and scale shopper inputs
var height = args.height*MM;
var thickness = args.thickness*MM;
var thickness = 5*MM;
}

var h_thickness = args.h_thickness*MM;

//Base Solid shape
var base = new Union();
var base_cyl = new Cylinder(new Vector3d(0,-height/2, 0), new Vector3d(0,height/2, 0), radius);
var base_rim = new Torus(radius-thickness/2, thickness/2);
var compTransform = new CompositeTransform();
base_rim.setTransform(compTransform);
var handle_base = new Mix(handle_cyl, handle_sq, args.mixer);

// check on handle
var h_remove = new Mix(h_remove_cyl, h_remove_sq, Math.pow(args.mixer,2));
var handle = new Subtraction(handle_base, h_remove);}
else {
var handle = handle_base;
}

var seat = new Cylinder(new Vector3d(0,-height/2, 0), new Vector3d(0,-height/2+thickness, 0), radius);

//Portions for removal
var remove_cyl = new Cylinder(new Vector3d(0,(-height+thickness)/2,0), new Vector3d(0,(height+thickness)/2,0), radius-thickness);

//Assembly
var cup = new Subtraction(base, remove_cyl);
var s = 250*MM;
return new Scene(cup,new Bounds(-s,s,-s,s,-s,s));
}
```
###### Try it!

But what if we want to mix part of the data source, not all of it? For this situation, we can use masking. Masking allows us to apply distance operations to a limited portion of the data source.

```var uiParams = [
{
name: "height",
label: "Cup height",
desc: "Height of the cup",
type: "double",
rangeMin: 50,
rangeMax: 200,
step: 1,
defaultVal: 120
},
{
name: "thickness",
label: "Cup Thickness",
desc: "Thickness of the cup",
type: "double",
rangeMin: 3,
rangeMax: 15,
step: 0.5,
defaultVal: 5
},
{
type: "double",
rangeMin: 25,
rangeMax: 120,
step: 1,
defaultVal: 60
},
{
name: "h_thickness",
label: "Handle Thickness",
desc: "Thickness of the handle",
type: "double",
rangeMin: 3,
rangeMax: 15,
step: 0.25,
defaultVal: 5
},
{
type: "double",
rangeMin: 10,
rangeMax: 100,
step: 1,
defaultVal: 35
},
{
name: "mixer",
label: "Handle Squaring",
desc: "Squaring of the handle",
type: "double",
rangeMin: 0,
rangeMax: 1,
step: 0.1,
defaultVal: 0
},
{
name: "basemix",
label: "Base Squaring",
desc: "Squaring of the base",
type: "double",
rangeMin: -1.5,
rangeMax: 0,
step: 0.05,
defaultVal: 0
}
];

function main(args) {
//Retrieve and scale shopper inputs
var height = args.height*MM;
var thickness = args.thickness*MM;
var thickness = 5*MM;
}

var h_thickness = args.h_thickness*MM;

//Base Solid shape
var base = new Union();
var base_cyl = new Cylinder(new Vector3d(0,-height/2, 0), new Vector3d(0,height/2, 0), radius);

// more mixing
var plane = new Plane(0,1,0,height*args.basemix);
var based = new Mix(base_cyl,base_box, mask);

var base_rim = new Torus(radius-thickness/2, thickness/2);
var compTransform = new CompositeTransform();
base_rim.setTransform(compTransform);
var handle_base = new Mix(handle_cyl, handle_sq, args.mixer);

// check on handle
var h_remove = new Mix(h_remove_cyl, h_remove_sq, Math.pow(args.mixer,2));
var handle = new Subtraction(handle_base, h_remove);}
else {
var handle = handle_base;
}

var seat = new Cylinder(new Vector3d(0,-height/2, 0), new Vector3d(0,-height/2+thickness, 0), radius);

//Portions for removal
var remove_cyl = new Cylinder(new Vector3d(0,(-height+thickness)/2,0), new Vector3d(0,(height+thickness)/2,0), radius-thickness);

//Assembly
var cup = new Subtraction(base, remove_cyl);
var s = 250*MM;
return new Scene(cup,new Bounds(-s,s,-s,s,-s,s));
}
```
###### Try it!

Now try adding in checks to ensure that, say, if we wanted to print ceramics, we would not end up with too thick a wall.

(Note: Masks can be used for more than just Mix. They can be used for all distance operations, whenever you are trying to affect only a limited part of a data source.)