258 lines
7.0 KiB
Vue
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>
|