docs: function(options) {
              var polygons = [],
                  csg = null,
                  prev = null,
                  bottom = null,
                  top = null,
                  numSlices = 2,
                  bLoop = false,
                  fnCallback,
                  flipped = null;
  
              if (options) {
                  bLoop = Boolean(options['loop']);
  
                  if (options.numslices)
                      numSlices = options.numslices;
  
                  if (options.callback)
                      fnCallback = options.callback;
              }
              if (!fnCallback) {
                  var square = new CSG.Polygon.createFromPoints([
                      [0, 0, 0],
                      [1, 0, 0],
                      [1, 1, 0],
                      [0, 1, 0]
                  ]);
                  fnCallback = function(t, slice) {
                      return t == 0 || t == 1 ? square.translate([0, 0, t]) : null;
                  }
              }
              for (var i = 0, iMax = numSlices - 1; i <= iMax; i++) {
                  csg = fnCallback.call(this, i / iMax, i);
                  if (csg) {
                      if (!(csg instanceof CSG.Polygon)) {
                          throw new Error("CSG.Polygon.solidFromSlices callback error: CSG.Polygon expected");
                      }
                      csg.checkIfConvex();
  
                      if (prev) { //generate walls
                          if (flipped === null) { //not generated yet
                              flipped = prev.plane.signedDistanceToPoint(csg.vertices[0].pos) < 0;
                          }
                          this._addWalls(polygons, prev, csg, flipped);
  
                      } else { //the first - will be a bottom
                          bottom = csg;
                      }
                      prev = csg;
                  } //callback can return null to skip that slice
              }
              top = csg;
  
              if (bLoop) {
                  var bSameTopBottom = bottom.vertices.length == top.vertices.length &&
                      bottom.vertices.every(function(v, index) {
                          return v.pos.equals(top.vertices[index].pos)
                      });
                  //if top and bottom are not the same -
                  //generate walls between them
                  if (!bSameTopBottom) {
                      this._addWalls(polygons, top, bottom, flipped);
                  } //else - already generated
              } else {
                  //save top and bottom
                  //TODO: flip if necessary
                  polygons.unshift(flipped ? bottom : bottom.flipped());
                  polygons.push(flipped ? top.flipped() : top);
              }
              return CSG.fromPolygons(polygons);
          }