// 从归灯实际源码截取：小灯笼结构。依赖Kit/WorldMaterials由原项目提供；供理解构造，按本项目改写。
  body: Geo;
  cap: Geo;
  tassel: Geo;
  /** Round drum lantern body (灯彩 variety). */
  drum: Geo;
  drumRim: Geo;
  /** Tall square palace-lamp body. */
  tall: Geo;
  tallCap: Geo;
}

export function smallLanternParts(kit: Kit): SmallLanternParts {
  const drumPts: THREE.Vector2[] = [];
  for (let i = 0; i <= 10; i += 1) {
    const t = i / 10;
    const y = -0.2 + t * 0.4;
    drumPts.push(new THREE.Vector2(0.09 + 0.16 * Math.sin(Math.PI * t), y));
  }
  return {
    body: kit.cyl(0.2, 0.2, 0.38, 6),
    cap: kit.cyl(0.13, 0.24, 0.08, 6),
    tassel: kit.cyl(0.025, 0.04, 0.26, 6),
    drum: kit.geo(new THREE.LatheGeometry(drumPts, 12)),
    drumRim: kit.cyl(0.11, 0.11, 0.05, 12),
    tall: kit.cyl(0.15, 0.15, 0.5, 4),
    tallCap: kit.cyl(0.08, 0.2, 0.07, 4),
  };
}

/** Stable small hash of a name, so lantern variety is deterministic. */
function nameHash(name: string): number {
  let h = 2166136261;
  for (let i = 0; i < name.length; i += 1) h = Math.imul(h ^ name.charCodeAt(i), 16777619);
  return h >>> 0;
}

/**
 * Small decorative lantern. Three silhouettes (hexagonal, round drum, tall
 * square palace lamp) are chosen from the name so a row of lamps never reads
 * as one copied object; all share the same paper material (lit together).
 */
export function buildSmallLantern(kit: Kit, parts: SmallLanternParts, name: string): THREE.Group {
  const g = new THREE.Group();
  g.name = name;
  const kind = nameHash(name) % 3;
  if (kind === 1) {
    const body = kit.mesh(parts.drum, kit.m.smallLanternPaper, { cast: false });
    g.add(body);
    for (const y of [0.215, -0.215]) {
      const rim = kit.mesh(parts.drumRim, kit.m.woodDark, { cast: false });
      rim.position.y = y;
      g.add(rim);
    }
    const tassel = kit.mesh(parts.tassel, kit.m.tassel, { cast: false });
    tassel.position.y = -0.38;
    g.add(tassel);
    g.rotation.y = (nameHash(name + 'r') % 100) * 0.01;
    return g;
  }
  if (kind === 2) {
    const body = kit.mesh(parts.tall, kit.m.smallLanternPaper, { cast: false });
    body.rotation.y = Math.PI / 4;
    g.add(body);
    for (const y of [0.285, -0.285]) {
      const cap = kit.mesh(parts.tallCap, kit.m.woodDark, { cast: false });
      cap.position.y = y;
      cap.rotation.y = Math.PI / 4;
      if (y < 0) cap.rotation.x = Math.PI;
      g.add(cap);
    }
    const tassel = kit.mesh(parts.tassel, kit.m.tassel, { cast: false });
    tassel.position.y = -0.46;
    g.add(tassel);
    return g;
  }
  const body = kit.mesh(parts.body, kit.m.smallLanternPaper, { cast: false });
  g.add(body);
  const top = kit.mesh(parts.cap, kit.m.brass, { cast: false });
  top.position.y = 0.23;
  g.add(top);
  const bottom = kit.mesh(parts.cap, kit.m.brass, { cast: false });
  bottom.position.y = -0.23;
  bottom.rotation.x = Math.PI;
  g.add(bottom);
  const tassel = kit.mesh(parts.tassel, kit.m.tassel, { cast: false });
  tassel.position.y = -0.42;
  g.add(tassel);
  return g;
}

// ---------------------------------------------------------------------------
// Round wooden deck with railing and support pillars
// ---------------------------------------------------------------------------

/** Annular sector shape (XY plane) for the deck's outer frame boards. */
function sectorShape(r0: number, r1: number, a0: number, a1: number): THREE.Shape {
  const shape = new THREE.Shape();
  shape.moveTo(Math.cos(a0) * r0, Math.sin(a0) * r0);
  shape.absarc(0, 0, r1, a0, a1, false);
  shape.lineTo(Math.cos(a1) * r0, Math.sin(a1) * r0);
  shape.absarc(0, 0, r0, a1, a0, true);
  return shape;
}

/**
 * Round floating deck in three readable layers: a honey-brown centre floor,
 * an outer frame of jointed boards with brass clasps, and a darker bearing
 * layer on a few big beams and pillars. Railing has square entrance posts,
 * slightly heavier main posts, thin balusters and thin rails; lamps sit only
 * on entrance posts.
 */
