Open as new workspace

Chained bezier drifting with subpaths

Drifting

View source Pathogen · 136 lines
define ViewBox(0, 0, 12000, 20000);
define default PathLayer('main-path-layer') ${
  fill: #bbb;
  stroke: #222;
  stroke-width: 1;
};

let stroke54UnderlayLayer = PathLayer('stroke-54-underlay') ${
  stroke: #4b8dd2.lighten(20%).alpha(90%);
  stroke-width: 80;
  stroke-linecap: round;
};
let stroke36UnderlayLayer = PathLayer('stroke-36-underlay') ${
  stroke: #c00.alpha(72%);
  stroke-width: 40;
  stroke-linecap: round;
};
let stroke2Layer = PathLayer('stroke-2') ${
  stroke-width: 2;
  stroke-linecap: round;
};
let stroke4Layer = PathLayer('stroke-4') ${
  stroke-width: 4;
  stroke-linecap: round;
};
let stroke8Layer = PathLayer('stroke-8') ${
  stroke-width: 8;
  stroke-linecap: round;
};
let stroke16Layer = PathLayer('stroke-16') ${
  stroke-width: 16;
  stroke-linecap: round;
};
let stroke32Layer = PathLayer('stroke-32') ${
  stroke-width: 32;
  stroke-linecap: round;
};

let baseChainedBezierArgs = [
  { x: 0, y: 0, angle: -0.5pi, exit: 200 },
  { x: 400, y: 0, angle: 0.5pi, exit: 200, entry: 200 },
  { x: 800, y: 0, angle: -0.5pi, exit: 200, entry: 200 },
  { x: 1200, y: 0, angle: 0.5pi, exit: 200, entry: 200 },
  { x: 1600, y: 0, angle: -0.5pi, exit: 200, entry: 200 },
  { x: 2000, y: 0, angle: 0.5pi, exit: 200, entry: 200 },
  { x: 2400, y: 0, angle: -0.5pi, exit: 200, entry: 200 },
  { x: 2800, y: 0, angle: 0.5pi, exit: 200, entry: 200 },
  { x: 3200, y: 0, angle: -0.5pi, exit: 200, entry: 200 },
  { x: 3600, y: 0, angle: 0.5pi, exit: 200, entry: 200 },
  { x: 4000, y: 0, angle: -0.5pi, exit: 200, entry: 200 },
  { x: 4400, y: 0, angle: 0.5pi, exit: 200, entry: 200 },
  { x: 4800, y: 0, angle: -0.5pi, entry: 200 },
];

// randomRange(1, 2); //-- Uncomment this to get the refresh button.

let strokeLayerCycler = Cycler([
  stroke2Layer,
  stroke4Layer,
  stroke8Layer,
  stroke16Layer,
  stroke32Layer,
  stroke16Layer,
  stroke8Layer,
  stroke4Layer,
]);

let xPos = 200;
let lastBezierArgs = baseChainedBezierArgs.slice(0);
let pathBlockCollection = [];

for (yOffset in 193..1) {
  let yPos = yOffset * 100 + 200;
  let selectedStrokeLayer = strokeLayerCycler.pick();
  let pb = @{
    cubicSpline(lastBezierArgs);
  };
  selectedStrokeLayer.apply {
    let pbb = pb.drawTo(xPos, yPos);
    pathBlockCollection.push(pbb);
  }
  
  lastBezierArgs = lastBezierArgs.map() {|item|
    let props = {
      x: randomRange(calc(item.x * 0.98), calc(item.x * 1.025)),
      y: randomRange(calc(item.y * 0.9), calc(item.y * 1.4)),
      angle: randomRange(calc(item.angle * 0.95), calc(item.angle * 1.025)),
    };
    if (item.has('entry')) {
      props['entry'] = randomRange(calc(item.entry * 0.99), calc(item.entry * 1.025));
    }
    if (item.has('exit')) {
      props['exit'] = randomRange(calc(item.exit * 0.99), calc(item.exit * 1.025));
    }
                                         
    return {} << item << props;
  };
}

fn drawSubPaths (pbCollection, layerRef) {
  let subPathStart = 0;
  let subPathEnd = 1;
  let subPathCollection = [];

  for ([pb, index] in pbCollection) {
    if (index == 0) {
      subPathStart = randomRange(0, 1);
      subPathEnd = randomRange(subPathStart, 1);
    } else {
      subPathStart = clamp(
        randomRange(subPathStart - 0.015, subPathStart + 0.015),
        0,
        subPathEnd
      );
      subPathEnd = clamp(
        randomRange(subPathEnd - 0.015, subPathEnd + 0.015),
        subPathStart,
        1
      );
    }
    
    let subPath = pb.subPath(subPathStart, subPathEnd);
    let start = pb.get(subPathStart);
  
    layerRef.apply {
      let projectedSubPath = subPath.drawTo(start.x, start.y);
      subPathCollection.push(projectedSubPath);
    }
  }

  return subPathCollection;
}

let blueSubPaths = drawSubPaths(pathBlockCollection, stroke36UnderlayLayer);
let foo = drawSubPaths(blueSubPaths, stroke54UnderlayLayer);