ghostyles/smokey-eyes.jsjavascript
/**
 * ==Ghostyle==
 * @name         Smokey Eyes (split demo)
 * @version      2.0.0
 * @author       vecna, but actually inspired by a paper.
 * @release_date 2025-01-01
 * @description  Demo split: pixel-space su meta' sinistra, UV-space su meta' destra.
 * ==/Ghostyle==
 */

/**
 * Meta' sinistra: landmark face-api, disegno diretto in pixel-space.
 * Il clip evita sovrapposizioni con la controparte UV della meta' destra.
 */
export function onDraw(ctx, landmarks) {
  const leftEye = landmarks.getLeftEye();
  const rightEye = landmarks.getRightEye();
  const leftBrow = landmarks.getLeftEyeBrow();
  const rightBrow = landmarks.getRightEyeBrow();

  ctx.save();
  if (!gstmxx.clipLeftHalf(ctx, landmarks)) {
    ctx.restore();
    return;
  }

  const leftShape = gstmxx.expandEyePolygon(leftEye, leftBrow, 1.32, 0.8).map((p) => gstmxx.point(p.x - 8, p.y + 4));
  const rightShape = gstmxx.expandEyePolygon(rightEye, rightBrow, 1.32, 0.8).map((p) => gstmxx.point(p.x + 8, p.y + 4));

  gstmxx.drawClosedPath(ctx, leftShape, 'rgba(125, 86, 172, 0.32)', 'rgba(194, 157, 255, 0.44)', 2);
  gstmxx.drawClosedPath(ctx, rightShape, 'rgba(125, 86, 172, 0.32)', 'rgba(194, 157, 255, 0.44)', 2);
  gstmxx.drawOpenPath(ctx, [leftEye[0], leftEye[1], leftEye[2], leftEye[3]], 'rgba(244, 236, 255, 0.92)', 2.6);
  gstmxx.drawOpenPath(ctx, [rightEye[0], rightEye[1], rightEye[2], rightEye[3]], 'rgba(244, 236, 255, 0.92)', 2.6);

  const lc = gstmxx.avgPoint(leftEye);
  gstmxx.drawLabel(ctx, 'smokey 2D', lc.x - 24, lc.y - 36);
  ctx.restore();
}

/**
 * Meta' destra: la mesh MediaPipe viene dipinta in UV-space.
 * Questa texture viene poi warpata sui triangoli del volto dal renderer UV.
 */
export function paintUV(ctx) {
  const landmarks3d = gstmxx.lastLandmarks3d;

  ctx.save();
  if (!gstmxx.clipRightHalfUV(ctx, landmarks3d)) {
    ctx.restore();
    return;
  }

  const w = ctx.canvas.width;
  const h = ctx.canvas.height;

  const g1 = ctx.createRadialGradient(w * 0.36, h * 0.42, w * 0.03, w * 0.36, h * 0.42, w * 0.16);
  g1.addColorStop(0, 'rgba(205, 165, 255, 0.34)');
  g1.addColorStop(1, 'rgba(78, 42, 125, 0.04)');
  ctx.fillStyle = g1;
  ctx.beginPath();
  ctx.ellipse(w * 0.36, h * 0.42, w * 0.17, h * 0.10, -0.12, 0, Math.PI * 2);
  ctx.fill();

  const g2 = ctx.createRadialGradient(w * 0.65, h * 0.42, w * 0.03, w * 0.65, h * 0.42, w * 0.16);
  g2.addColorStop(0, 'rgba(205, 165, 255, 0.34)');
  g2.addColorStop(1, 'rgba(78, 42, 125, 0.04)');
  ctx.fillStyle = g2;
  ctx.beginPath();
  ctx.ellipse(w * 0.65, h * 0.42, w * 0.17, h * 0.10, 0.12, 0, Math.PI * 2);
  ctx.fill();

  ctx.restore();
}