'use client';
import { memo, type ReactNode } from 'react';
import type { FdiToothId } from '@/types/treatment';
import type { ToothShapeKind } from '@/components/treatment/fdiToothMeta';
import type { FdiChartDesignId } from '@/components/ui/treatment/fdiChartDesigns';
import { getToothGlyphPaint } from '@/components/ui/treatment/toothGlyphPaint';
import type { ToothGlyphPaint } from '@/components/ui/treatment/toothGlyphPaint';
import { getToothPathModel, type ToothPathModel } from '@/components/ui/treatment/toothPathModel';
import { getTextbookToothPathModel } from '@/components/ui/treatment/toothPathModelTextbook';
import { wireframeInnerScale, wireframeOriginY } from '@/components/ui/treatment/toothGlyphStructural';
interface ToothGlyphProps {
fdi: FdiToothId;
kind: ToothShapeKind;
gradientId: string;
selected: boolean;
upper?: boolean;
mirrored?: boolean;
upsideDown?: boolean;
className?: string;
variant?: FdiChartDesignId;
}
function filledSilhouette(
model: ToothPathModel,
crownFill: string,
rootFill: string,
stroke: string,
strokeW: number,
): ReactNode {
return (
<>
{model.roots.map((d, i) => (
))}
>
);
}
function detailOnly(model: ToothPathModel, stroke: string, strokeW: number): ReactNode {
if (!model.detailStroke) return null;
const { d, widthScale } = model.detailStroke;
return ;
}
function renderClinicalFill(model: ToothPathModel, paint: ToothGlyphPaint): ReactNode {
const { crownFill, rootFill, stroke, strokeW } = paint;
return (
<>
{filledSilhouette(model, crownFill, rootFill, stroke, strokeW)}
{detailOnly(model, stroke, strokeW)}
>
);
}
function renderWireframe(model: ToothPathModel, paint: ToothGlyphPaint, kind: ToothShapeKind): ReactNode {
const { stroke, strokeW } = paint;
const outer = paint.wireOuter ?? '#f8fafc';
const innerCol = paint.wireInner ?? '#64748b';
const s = wireframeInnerScale(kind);
const crownOy = wireframeOriginY('crown', kind);
const rootOy = wireframeOriginY('root', kind);
const rootScale = (i: number) => s - (model.roots.length > 1 ? i * 0.018 : 0);
return (
<>
{model.roots.map((d, i) => (
))}
{detailOnly(model, stroke, strokeW)}
>
);
}
/** Clinical uses FDI-specific paths; wireframe uses textbook silhouettes + line sculpture (original combo). */
export const ToothGlyph = memo(function ToothGlyph({
fdi,
kind,
gradientId,
selected,
upper,
mirrored,
upsideDown,
className = 'w-8 h-[4.85rem]',
variant = 'clinical',
}: ToothGlyphProps) {
const lastDigit = Number(fdi[1]);
const model: ToothPathModel | null =
variant === 'clinical'
? getToothPathModel(fdi, kind, upper)
: getTextbookToothPathModel(kind, upper, lastDigit);
const paint = getToothGlyphPaint(variant, gradientId, selected);
const { showClinicalGradient, filter, structuralMode } = paint;
if (!model) {
return (
);
}
const body = structuralMode === 'wireframe' ? renderWireframe(model, paint, kind) : renderClinicalFill(model, paint);
return (
);
});