lot

File*
Docs
Download
What do I make?
log in to save
Machine control
/*
@title: Lets Make Cheese!
@author: UserJ
@snapshot: cheese1.png
*/

const width = 125;
const height = 125;
setDocDimensions(width, height);

const finalLines = [];

const cheeseCenter = [88, 68];
const cheeseHeight = 22;
const cheeseRadius = 67;
const cheesePercentage = 0.1;
const cheeseAngle = -4 % 360 / 180 * Math.PI;
const cameraAngle = 101 / 180 * Math.PI;
const holeRadius = 3;
const numHoles = 12;

bt.setRandSeed(5)

const cheeseEndAngle = cheeseAngle + cheesePercentage * 2 * Math.PI
if (cheesePercentage !== 0) {
const backSide = [];

// Make sure it would be visible (Middle Line of cheese
if (cheeseEndAngle < Math.PI * 1.5 || cheeseAngle > Math.PI * 1.5) {
const middleLine = [
cheeseCenter,
[cheeseCenter[0], cheeseCenter[1] - (cheeseHeight * Math.sin(cameraAngle))],
]
// finalLines.push(middleLine)
backSide.push(middleLine)
}

function calcCheeseEdgePos(angle) {
return [
cheeseCenter[0] + (-Math.cos(angle) * cheeseRadius),
cheeseCenter[1] + (-Math.cos(cameraAngle) * Math.sin(angle) * cheeseRadius)
]
}

const curvePoints = [];
for (var i = 0; i < 100; i++) {
// console.log(calcCheeseEdgePos(cheeseAngle +
curvePoints.push(calcCheeseEdgePos(cheeseAngle + ((i / 100) * cheesePercentage * 2 * Math.PI)))
}
finalLines.push(bt.catmullRom(curvePoints))

const firstPart = calcCheeseEdgePos(cheeseAngle)
const finalPart = calcCheeseEdgePos(cheeseEndAngle)

// finalLines.push([cheeseCenter, firstPart])
finalLines.push([cheeseCenter, finalPart])

function isAngleVisible(angle) {
if (angle > Math.PI) return true;
if (angle > cheeseAngle && angle < cheeseEndAngle) return false;
if (angle < Math.PI / 2 && angle > cheeseAngle) return false;
if (angle > Math.PI / 2 && angle < cheeseEndAngle) return false;
return true;
}

const frontEdge = [
firstPart,
[firstPart[0], firstPart[1] - (cheeseHeight * Math.sin(cameraAngle))],
];
if (isAngleVisible(cheeseAngle)) {
// finalLines.push(frontEdge)
}
const backEdge = [
finalPart,
[finalPart[0], finalPart[1] - (cheeseHeight * Math.sin(cameraAngle))],
]
if (isAngleVisible(cheeseEndAngle)) {
finalLines.push(backEdge)
}

const frontBottomEdge = [
[firstPart[0], firstPart[1] - (cheeseHeight * Math.sin(cameraAngle))],
[cheeseCenter[0], cheeseCenter[1] - (cheeseHeight * Math.sin(cameraAngle))]
];
if (cheeseAngle < Math.PI / 2 || cheeseAngle > Math.PI * 1.5) {
// finalLines.push(frontBottomEdge)
}

const backBottomEdge = [
[finalPart[0], finalPart[1] - (cheeseHeight * Math.sin(cameraAngle))],
[cheeseCenter[0], cheeseCenter[1] - (cheeseHeight * Math.sin(cameraAngle))]
]
backSide.push(backBottomEdge)
if (cheeseEndAngle > Math.PI / 2 && cheeseEndAngle < Math.PI * 1.5) {
finalLines.push(backBottomEdge)
}


function circle(x, y, r) {
const t = new bt.Turtle();
t.jump([0, -r])
t.arc(360, r);
const cc = bt.bounds(t.path).cc;
bt.translate(t.path, [x, y], cc);

return t.path[0];
}
const points = [];
for (var i = 0; i < numHoles; i++) {
bt.union(points, [circle(bt.randInRange(-cheeseRadius, 0), bt.randInRange(0, cheeseHeight), holeRadius)])
}

const frontSide = [[
cheeseCenter,
[cheeseCenter[0], cheeseCenter[1] - (cheeseHeight * Math.sin(cameraAngle))],
[firstPart[0], firstPart[1] - (cheeseHeight * Math.sin(cameraAngle))],
firstPart,
cheeseCenter,
]];
drawLines(
bt.difference(
frontSide,
bt.translate(
bt.rotate(bt.scale(points, [1, Math.sin(cameraAngle)]),
cheeseAngle / Math.PI * 180 * Math.cos(cameraAngle), [cheeseHeight/2, 0]),
[cheeseCenter[0], cheeseCenter[1] - (cheeseHeight * Math.sin(cameraAngle))]
)
)
)

drawLines(finalLines);
}

The Toolkit

This is a quick reference sheet. For full documentation refer to this.

For an introduction to Blot check out this guide.

Check out our 38 second trailer for a brief overview of the whole Blot project.

There are three names that provide functionality available in the Blot editor: setDocDimensions, drawLines, and blotToolkit (which can also be referenced as bt).

The first two affect the drawing environment itself, and the blotToolkit is used for creating line drawings.

Environment Affecting

setDocDimensions(width: number, height: number)
drawLines(polylines: [number, number][][])

Modify Polylines

Take and modify polylines in place returns first passed polylines.

These functions are available in the blotToolkit or bt object.

bt.iteratePoints(polylines, (pt, t) => { ... }) // return pt to modify, "BREAK" to split, "REMOVE" to filter out point
bt.scale(polylines, scale : scaleXY | [scaleX, scaleY], ?origin: [ x, y ]) 
bt.rotate(polylines, degrees, ?origin: [ x, y ]) 
bt.translate(polylines, [dx, dy], ?origin: [ x, y ]) 
bt.originate(polylines) // moves center to [0, 0] 
bt.resample(polylines, sampleRate) 
bt.simplify(polylines, tolerance) 
bt.trim(polylines, tStart, tEnd)
bt.merge(polylines)  
bt.join(polylines0, ...morePolylines) 
bt.copy(polylines)
bt.cut(polylines0, polylines1) 
bt.cover(polylines0, polylines1) 
bt.union(polylines0, polylines1)
bt.difference(polylines0, polylines1)
bt.intersection(polylines0, polylines1)
bt.xor(polylines0, polylines1)
bt.offset(polylines, delta, ?ops = { endType, joinType, miterLimit, arcTolerance })

Get Data From Polylines

These functions are available in the blotToolkit or bt object.

// take polylines return other
bt.getAngle(polylines, t: [0 to 1]) // returns angle in degrees
bt.getPoint(polylines, t: [0 to 1]) // returns point as [x, y]
bt.getNormal(polylines, t: [0 to 1]) // returns normal vector as [x, y]

bt.pointInside(polylines, pt)

bt.bounds(polylines) 
/*
returns { 
  xMin, xMax, 
  yMin, yMax, 
  lt, ct, rt, 
  lc, cc, rc,
  lb, cb, rb,
  width, height
}

l is left
c is center
r is right
t is top
b is bottom

they are arranged in this configuration around the bounding box of the polylines

lt--ct--rt
 |   |   |
lc--cc--rc
 |   |   | 
lb--cb--rb
*/

Generate Polylines

These functions are available in the blotToolkit or bt object.

const myTurtle = new bt.Turtle()
  .forward(distance: number)
  .arc(angle: number, radius: number)
  .goTo( [ x: number, y: number ] ) // move with up/down state
  .jump( [ x: number, y: number ] ) // move but don't draw
  .step( [ dx: number, dy: number ] ) // add delta to turtles current position
  .right(angle: number)
  .left(angle: number)
  .setAngle(angle: number)
  .up() // sets drawing to false
  .down() // sets drawing to true
  .copy()
  .applyToPath(fn) // takes (turtlePath) => { }
  .lines() // get copy of the Turtle's path

// data
const position = myTurtle.pos // [x: number, y: number]
const angle = myTurtle.angle // number
const path = myTurtle.path // is array of polylines [number, number][][]
const drawing = myTurtle.drawing // boolean
bt.catmullRom(points, ?steps = 1000) // returns polyline [number, number][]
bt.nurbs(points, ?ops = { steps: 100, degree: 2}) // returns polyline [number, number][]

Randomness

These functions are available in the blotToolkit or bt object.

bt.rand();

bt.randInRange(min: number, max: number);

bt.randIntInRange(min: number, max: number); 

bt.setRandSeed(seed: number);

bt.noise(
  number | [ x:number , ?y: number , ?z: number ], 
  { 
    octaves: number [0 to 8], 
    falloff: number [0 to 100] 
  }
);

Idioms

These are small useful code snippets.

function centerPolylines(polylines, documentWidth, documentHeight) {
  const cc = bt.bounds(polylines).cc;
  bt.translate(polylines, [documentWidth / 2, documentHeight / 2], cc);
}