Files
antrean-operasi/components/apps/medical/Odontogram/OdontogramCanvas.vue
2026-01-22 09:11:15 +07:00

258 lines
7.0 KiB
Vue

<template>
<div class="odontogram-container">
<AppsMedicalOdontogramToolbar
:currentMode="mode"
@modeChange="setMode"
@clearAll="clearAll"
/>
<canvas
ref="canvas"
:width="width"
:height="height"
class="odontogram-canvas"
@mousemove="onMouseMove"
@click="onMouseClick"
>
Browser anda tidak support canvas, silahkan update browser anda.
</canvas>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, onMounted, watch, defineEmits } from "vue";
import {
useOdontogram,
convertGeom
} from "~/composables/apps/medical/useOdontogram";
import { useToothRenderer } from "~/composables/apps/medical/useToothRenderer";
import { useOdontogramStore } from "~/store/apps/medical/odontogram";
const emit = defineEmits(["update:geometry"]);
const canvas = ref<HTMLCanvasElement | null>(null);
const width = 1500;
const height = 675;
const store = useOdontogramStore();
const {
odontogramInstance,
mode,
initialize,
setMode,
onMouseMove,
onMouseClick,
geometry,
clearAll
} = useOdontogram();
const { renderCondition } = useToothRenderer();
function initializeGeometryFromConditions() {
if (odontogramInstance.value && store.conditions.length > 0) {
const newGeometry: Record<string, any[]> = {};
const teeth = odontogramInstance.value.teeth;
// Helper to get polygons for a given surface on a tooth
function getPolygonsForSurface(teeth: any, surfaceKey: string) {
const polygons = [];
for (const key in teeth) {
if (key === surfaceKey) {
const polygonOpt = {
fillStyle: "rgba(55, 55, 55, 0.2)"
};
const vertices = [];
for (const vertexKey in teeth[key]) {
vertices.push(teeth[key][vertexKey]);
}
const pol = new Polygon(vertices, polygonOpt);
pol.name = key;
polygons.push(pol);
}
}
return polygons;
}
// Build a map from tooth.num to toothKey for quick lookup
const toothNumToKeyMap = new Map<string, string>();
Object.keys(teeth).forEach((key) => {
const tooth = teeth[key];
if (tooth && tooth.num) {
toothNumToKeyMap.set(tooth.num, key);
}
});
for (const condition of store.conditions) {
if (!teeth) continue;
const toothKey = toothNumToKeyMap.get(String(condition.toothNumber));
if (!toothKey) {
console.warn(
`No tooth key found for toothNumber: ${condition.toothNumber}`
);
continue;
}
// Map short surface codes to full property names in teeth object
const surfaceMap: Record<string, string> = {
T: "top",
R: "right",
B: "bottom",
L: "left",
M: "middle"
};
let surfaceKey = "middle";
if (
condition.surface &&
["T", "R", "B", "L", "M"].includes(condition.surface)
) {
surfaceKey = surfaceMap[condition.surface];
} else if (condition.position && typeof condition.position === "string") {
// Try to infer surface from position string (pos)
const parts = condition.position.split("-");
if (parts.length > 1) {
const surf = parts[1].toUpperCase();
if (["T", "R", "B", "L", "M"].includes(surf)) {
surfaceKey = surfaceMap[surf];
}
}
}
// For AMF, COF, FIS, CARIES modes, use polygons for surface
if (
condition.mode === 1 || // AMF
condition.mode === 2 || // COF
condition.mode === 3 || // FIS
condition.mode === 10 // CARIES
) {
const polygons = getPolygonsForSurface(teeth, surfaceKey);
if (polygons.length > 0) {
if (!newGeometry[toothKey]) {
newGeometry[toothKey] = [];
}
for (const pol of polygons) {
pol.pos =
condition.toothNumber + "-" + pol.name.charAt(0).toUpperCase();
const geom = convertGeom(pol, condition.mode);
newGeometry[toothKey].push(geom);
}
continue;
}
}
// Fallback: use tooth rectangle vertices and pos as tooth number
const coord = odontogramInstance.value.teeth[toothKey];
if (!newGeometry[toothKey]) {
newGeometry[toothKey] = [];
}
if (coord) {
// Append start/finish suffix to pos for bridge mode
let posValue = condition.toothNumber;
if (
condition.mode === 18 &&
condition.position &&
typeof condition.position === "string"
) {
if (condition.position.toLowerCase().includes("start")) {
posValue = condition.toothNumber + "-start";
} else if (condition.position.toLowerCase().includes("finish")) {
posValue = condition.toothNumber + "-finish";
}
}
const geom = convertGeom(
{
vertices: [
{ x: coord.x1, y: coord.y1 },
{ x: coord.x2, y: coord.y2 }
],
pos: posValue
},
condition.mode
);
newGeometry[toothKey].push(geom);
}
}
odontogramInstance.value.geometry = newGeometry;
odontogramInstance.value.redraw();
console.log("Geometry initialized and canvas redrawn.");
} else {
console.log("No conditions found or odontogram instance missing.");
}
console.log("Odontogram instance initialized:", odontogramInstance.value);
}
onMounted(() => {
if (canvas.value) {
console.log(
"Initializing odontogram canvas with width:",
width,
"height:",
height
);
initialize(canvas.value, width, height);
// Load data synchronously
store.loadStoredData();
console.log("Data loaded from store:", store.conditions);
// Set mode from store or default
// if (store.currentMode) {
// setMode(store.currentMode);
// } else {
// setMode(mode.value);
// }
// Initialize geometry from conditions
// initializeGeometryFromConditions();
} else {
console.warn("Canvas ref is null on mounted");
}
});
// watch(
// () => store.conditions,
// () => {
// initializeGeometryFromConditions();
// }
// );
// Watch store currentMode and update local mode and odontogram instance
watch(
() => store.currentMode,
(newMode) => {
if (newMode !== mode.value) {
mode.value = newMode;
odontogramInstance.value?.setMode(newMode);
}
}
);
// Keep existing watch on local mode to update odontogram instance and store mode
watch(mode, (newMode) => {
console.log("Mode changed to:", newMode);
odontogramInstance.value?.setMode(newMode);
store.setMode(newMode);
});
</script>
<style scoped>
.odontogram-container {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
overflow-x: auto;
}
.odontogram-canvas {
border: 1px solid #a9a9a9;
margin-top: 15px;
max-width: 100%;
height: auto;
}
@media (max-width: 900px) {
.odontogram-canvas {
max-width: auto;
height: auto;
}
}
</style>