{"id":31318,"date":"2025-11-26T16:10:26","date_gmt":"2025-11-26T08:10:26","guid":{"rendered":"https:\/\/www.hkmu.edu.hk\/oetools\/?page_id=31318"},"modified":"2025-11-26T16:39:21","modified_gmt":"2025-11-26T08:39:21","slug":"resource-3-digestive-system-visualisation","status":"publish","type":"page","link":"https:\/\/www.hkmu.edu.hk\/oetools\/resource-3-digestive-system-visualisation\/","title":{"rendered":"Resource 3 Digestive System Visualisation"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"31318\" class=\"elementor elementor-31318\" data-elementor-settings=\"[]\">\n\t\t\t\t\t\t\t<div class=\"elementor-section-wrap\">\n\t\t\t\t\t\t\t<section class=\"has_eae_slider wavo-column-gap-default elementor-section elementor-top-section elementor-element elementor-element-18bf372 elementor-section-boxed elementor-section-height-default elementor-section-height-default\" data-id=\"18bf372\" data-element_type=\"section\" data-settings=\"{&quot;jet_parallax_layout_list&quot;:[]}\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"has_eae_slider elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-bd18fd4\" data-id=\"bd18fd4\" data-element_type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t\t\t<div class=\"elementor-element elementor-element-1245ad1 elementor-widget elementor-widget-html\" data-id=\"1245ad1\" data-element_type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t<!DOCTYPE html>\r\n<html lang=\"en\">\r\n    <head>\r\n        <meta charset=\"UTF-8\">\r\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n        <title>Interactive Digestive System<\/title>\r\n        <style>\r\n        :root {\r\n            --primary-color: #32744C;\r\n            --primary-light: #508E42;\r\n            --primary-dark: #4C4F26;\r\n            --accent-green: #508E42;\r\n            --medium-gray: #e0e0e0;\r\n            --dark-gray: #707070;\r\n            --warning-color: #3A7CA5;\r\n            --dark-text: #333;\r\n            --light-text: #666;\r\n            --card-bg: rgba(255, 255, 255, 0.95);\r\n            --bg-light: linear-gradient(135deg, #CFE2CF, #8DBEA2);\r\n            --shadow: 0 15px 40px rgba(0, 0, 0, 0.2);\r\n        }\r\n        * {\r\n            margin: 0;\r\n            padding: 0;\r\n            box-sizing: border-box;\r\n            font-family: 'Microsoft YaHei', '\u9ed1\u9ad4', 'SimHei', Arial, sans-serif;\r\n        }\r\n        \r\n        body {\r\n            background: var(--bg-light);\r\n            color: var(--dark-text);\r\n            overflow-x: hidden;\r\n        }\r\n        \r\n        .container {\r\n            display: flex;\r\n            max-width: 1800px;\r\n            height: 100vh;\r\n            padding: 20px;\r\n            gap: 20px;\r\n        }\r\n        \r\n        .header {\r\n            background: linear-gradient(45deg, var(--primary-color), var(--primary-light));\r\n            color: #ffffff;\r\n            padding: 25px;\r\n            text-align: center;\r\n        }\r\n        \r\n        .header h1 {\r\n            font-size: 2.5rem;\r\n            font-weight: 700;\r\n            margin-bottom: 10px;\r\n        }\r\n        \r\n        .header p {\r\n            font-size: 18px;\r\n            opacity: 0.9;\r\n        }\r\n        \r\n        .left-panel {\r\n            flex: 0 0 280px;\r\n            background: var(--card-bg);\r\n            border-radius: 20px;\r\n            padding: 20px;\r\n            box-shadow: var(--shadow);\r\n            overflow-y: auto;\r\n        }\r\n        \r\n        .center-panel {\r\n            flex: 1;\r\n            background: var(--card-bg);\r\n            border-radius: 20px;\r\n            padding: 30px;\r\n            box-shadow: var(--shadow);\r\n            position: relative;\r\n            overflow: hidden;\r\n        }\r\n        \r\n        .right-panel {\r\n            flex: 0 0 300px;\r\n            background: var(--card-bg);\r\n            border-radius: 20px;\r\n            padding: 20px;\r\n            box-shadow: var(--shadow);\r\n            overflow-y: auto;\r\n        }\r\n        \r\n        h2 {\r\n            color: var(--primary-color);\r\n            margin-bottom: 15px;\r\n            font-size: 20px;\r\n        }\r\n        \r\n        h3 {\r\n            color: var(--primary-color);\r\n            margin-bottom: 10px;\r\n            font-size: 16px;\r\n        }\r\n        \r\n        .food-item {\r\n            display: flex;\r\n            align-items: center;\r\n            gap: 10px;\r\n            padding: 12px;\r\n            margin: 8px 0;\r\n            background: #f8f9fa;\r\n            border-radius: 12px;\r\n            cursor: pointer;\r\n            transition: all 0.3s;\r\n            border: 2px solid transparent;\r\n        }\r\n        \r\n        .food-item:hover {\r\n            background: #e9ecef;\r\n            transform: translateX(5px);\r\n        }\r\n        \r\n        .food-item.selected {\r\n            background: rgba(80, 142, 66, 0.12);\r\n            border-color: var(--accent-green);\r\n        }\r\n        \r\n        .food-icon {\r\n            width: 40px;\r\n            height: 40px;\r\n            border-radius: 8px;\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: center;\r\n            font-size: 24px;\r\n        }\r\n        \r\n        .food-info {\r\n            flex: 1;\r\n        }\r\n        \r\n        .food-name {\r\n            font-weight: bold;\r\n            color: var(--dark-text);\r\n        }\r\n        \r\n        .food-nutrient {\r\n            font-size: 12px;\r\n            color: var(--light-text);\r\n        }\r\n        \r\n        .btn {\r\n            width: 100%;\r\n            padding: 12px;\r\n            margin: 8px 0;\r\n            border: none;\r\n            border-radius: 10px;\r\n            font-size: 16px;\r\n            font-weight: bold;\r\n            cursor: pointer;\r\n            transition: all 0.3s;\r\n        }\r\n        \r\n        .btn-primary {\r\n            background: var(--primary-color);\r\n            color: white;\r\n        }\r\n        \r\n        .btn-primary:hover:not(:disabled) {\r\n            transform: translateY(-2px);\r\n            box-shadow: 0 5px 15px rgba(50, 116, 76, 0.35);\r\n        }\r\n        \r\n        .btn-primary:disabled {\r\n            opacity: 0.5;\r\n            cursor: not-allowed;\r\n            background: var(--dark-gray);\r\n        }\r\n        \r\n        .btn-secondary {\r\n            background: #ffffff;\r\n            color: var(--primary-color);\r\n            border: 2px solid var(--primary-color);\r\n        }\r\n        \r\n        .btn-secondary:hover:not(:disabled) {\r\n            background: rgba(50, 116, 76, 0.08);\r\n        }\r\n        \r\n        .canvas-container {\r\n            width: 100%;\r\n            height: calc(100% - 80px);\r\n            position: relative;\r\n        }\r\n        \r\n        #digestiveCanvas {\r\n            width: 100%;\r\n            height: 100%;\r\n            cursor: pointer;\r\n        }\r\n        \r\n        .info-box {\r\n            background: rgba(255, 255, 255, 0.98);\r\n            padding: 20px;\r\n            border-radius: 15px;\r\n            box-shadow: 0 8px 25px rgba(0,0,0,0.25);\r\n            display: none;\r\n            border: 3px solid var(--primary-color);\r\n            overflow-y: auto;\r\n            margin-top: 10px;\r\n        }\r\n        \r\n        .info-box.visible {\r\n            display: block;\r\n            animation: slideIn 0.3s ease-out;\r\n        }\r\n        \r\n        @keyframes slideIn {\r\n            from {\r\n                opacity: 0;\r\n                transform: translateX(20px);\r\n            }\r\n            to {\r\n                opacity: 1;\r\n                transform: translateX(0);\r\n            }\r\n        }\r\n        \r\n        .progress-bar {\r\n            width: 100%;\r\n            height: 30px;\r\n            background: var(--medium-gray);\r\n            border-radius: 15px;\r\n            overflow: hidden;\r\n            margin: 10px 0;\r\n        }\r\n        \r\n        .progress-fill {\r\n            height: 100%;\r\n            background: var(--primary-color);\r\n            transition: width 0.3s;\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: center;\r\n            color: white;\r\n            font-weight: bold;\r\n            font-size: 12px;\r\n            width: 0%;\r\n        }\r\n        \r\n        .enzyme-tag {\r\n            background: rgba(58, 124, 165, 0.12);\r\n            padding: 8px 12px;\r\n            margin: 5px 0;\r\n            border-radius: 8px;\r\n            border-left: 4px solid var(--warning-color);\r\n            font-size: 13px;\r\n        }\r\n        \r\n        .enzyme-name {\r\n            font-weight: bold;\r\n            color: var(--warning-color);\r\n        }\r\n        \r\n        .quiz-section {\r\n            margin-top: 20px;\r\n        }\r\n        \r\n        .question {\r\n            background: #f8f9fa;\r\n            padding: 15px;\r\n            border-radius: 12px;\r\n            margin: 10px 0;\r\n            cursor: pointer;\r\n            transition: all 0.3s;\r\n        }\r\n        \r\n        .question:hover {\r\n            background: #e9ecef;\r\n        }\r\n        \r\n        .question.revealed {\r\n            background: rgba(58, 124, 165, 0.12);\r\n            border-left: 4px solid var(--warning-color);\r\n        }\r\n        \r\n        .answer {\r\n            margin-top: 10px;\r\n            padding: 10px;\r\n            background: white;\r\n            border-radius: 8px;\r\n            display: none;\r\n        }\r\n        \r\n        .answer.visible {\r\n            display: block;\r\n        }\r\n        \r\n        .control-panel {\r\n            display: flex;\r\n            gap: 10px;\r\n            margin-top: 20px;\r\n        }\r\n        \r\n        .control-panel .btn {\r\n            flex: 1;\r\n            padding: 10px;\r\n            font-size: 14px;\r\n        }\r\n        \r\n        .status-message {\r\n            background: rgba(80, 142, 66, 0.12);\r\n            border: 2px solid var(--accent-green);\r\n            padding: 12px;\r\n            border-radius: 10px;\r\n            margin: 10px 0;\r\n            font-weight: bold;\r\n            color: var(--primary-dark);\r\n        }\r\n        \r\n        ::-webkit-scrollbar {\r\n            width: 8px;\r\n        }\r\n        \r\n        ::-webkit-scrollbar-track {\r\n            background: var(--medium-gray);\r\n            border-radius: 10px;\r\n        }\r\n        \r\n        ::-webkit-scrollbar-thumb {\r\n            background: var(--primary-color);\r\n            border-radius: 10px;\r\n        }\r\n\r\n        .loading-message {\r\n            text-align: center;\r\n            padding: 20px;\r\n            color: var(--primary-color);\r\n            font-weight: bold;\r\n        }\r\n        <\/style>\r\n<\/head>\r\n<body>\r\n    <div class=\"header\">\r\n        <h1>Digestive System Visualisation<\/h1>\r\n        <p>Visualize the human digestive process with interactive organ models and animated food breakdown.<\/p>\r\n    <\/div>\r\n    <div class=\"container\">\r\n        <!-- Left Panel: Food Selection -->\r\n        <div class=\"left-panel\">\r\n            <h2>Select Food<\/h2>\r\n            <div id=\"foodList\"><\/div>\r\n            <button class=\"btn btn-primary\" id=\"startBtn\" disabled>Start Digestion<\/button>\r\n            <div class=\"control-panel\">\r\n                <button class=\"btn btn-secondary\" id=\"pauseBtn\" disabled>\u23f8\ufe0f Pause<\/button>\r\n                <button class=\"btn btn-secondary\" id=\"speedBtn\" disabled>\u25b6\ufe0f Normal<\/button>\r\n            <\/div>\r\n            <button class=\"btn btn-secondary\" id=\"resetBtn\">\ud83d\udd04 Reset<\/button>\r\n            <!-- <button class=\"btn btn-secondary\" id=\"alignBtn\"> Align Organs<\/button> -->\r\n            <div id=\"alignControls\" style=\"display:none; margin-top:10px;\">\r\n                <h3>Alignment<\/h3>\r\n                <select id=\"organSelect\" class=\"btn btn-secondary\" style=\"width:100%;\"><\/select>\r\n                <select id=\"alignTypeSelect\" class=\"btn btn-secondary\" style=\"width:100%; margin-top:8px;\">\r\n                    <option value=\"box\">Align Box<\/option>\r\n                    <option value=\"label\">Align Label<\/option>\r\n                <\/select>\r\n                <div style=\"display:grid; grid-template-columns:1fr 1fr; gap:8px; margin-top:8px;\">\r\n                    <input id=\"inputX\" type=\"number\" placeholder=\"x\" class=\"btn btn-secondary\" \/>\r\n                    <input id=\"inputY\" type=\"number\" placeholder=\"y\" class=\"btn btn-secondary\" \/>\r\n                    <input id=\"inputW\" type=\"number\" placeholder=\"w\" class=\"btn btn-secondary\" \/>\r\n                    <input id=\"inputH\" type=\"number\" placeholder=\"h\" class=\"btn btn-secondary\" \/>\r\n                    <input id=\"inputLabelX\" type=\"number\" placeholder=\"label x\" class=\"btn btn-secondary\" style=\"display:none;\"\/>\r\n                    <input id=\"inputLabelY\" type=\"number\" placeholder=\"label y\" class=\"btn btn-secondary\" style=\"display:none;\"\/>\r\n                <\/div>\r\n                <div class=\"control-panel\">\r\n                    <button class=\"btn btn-primary\" id=\"saveLayoutBtn\">Save Layout<\/button>\r\n                    <button class=\"btn btn-secondary\" id=\"copyLayoutBtn\">Copy JSON<\/button>\r\n                <\/div>\r\n                <textarea id=\"layoutJson\" placeholder=\"Paste layout JSON here\" class=\"btn btn-secondary\" style=\"width:100%; height:100px; margin-top:8px;\"><\/textarea>\r\n                <div class=\"control-panel\">\r\n                    <button class=\"btn btn-secondary\" id=\"applyLayoutBtn\">Apply JSON<\/button>\r\n                <\/div>\r\n            <\/div>\r\n        <\/div>\r\n        \r\n        <!-- Center Panel: 3D Model -->\r\n        <div class=\"center-panel\">\r\n            <h2>Human Digestive System<\/h2>\r\n            <div class=\"canvas-container\">\r\n                <canvas id=\"digestiveCanvas\"><\/canvas>\r\n            <\/div>\r\n            <div class=\"progress-bar\">\r\n                <div class=\"progress-fill\" id=\"progressBar\">0%<\/div>\r\n            <\/div>\r\n        <\/div>\r\n        \r\n        <!-- Right Panel: Information & Quiz -->\r\n        <div class=\"right-panel\">\r\n            <h2>Digestion Process<\/h2>\r\n            <div id=\"statusPanel\">\r\n                <p>Select food items and click \"Start Digestion\" to begin!<\/p>\r\n            <\/div>\r\n            <div class=\"info-box\" id=\"infoBox\">\r\n                <h3 id=\"organName\"><\/h3>\r\n                <p id=\"organFunction\"><\/p>\r\n                <div id=\"enzymeInfo\"><\/div>\r\n            <\/div>\r\n            \r\n            <div class=\"quiz-section\" id=\"quizSection\" style=\"display: none;\">\r\n                <h2>Quiz<\/h2>\r\n                <div id=\"quizQuestions\"><\/div>\r\n            <\/div>\r\n        <\/div>\r\n    <\/div>\r\n\r\n    <script>\r\n        const canvas = document.getElementById('digestiveCanvas');\r\n        const ctx = canvas.getContext('2d');\r\n        const infoBox = document.getElementById('infoBox');\r\n        const statusPanel = document.getElementById('statusPanel');\r\n        const quizSection = document.getElementById('quizSection');\r\n        const progressBar = document.getElementById('progressBar');\r\n        \r\n        \/\/ Polyfill for roundRect\r\n        if (!CanvasRenderingContext2D.prototype.roundRect) {\r\n            CanvasRenderingContext2D.prototype.roundRect = function(x, y, w, h, r) {\r\n                if (w < 2 * r) r = w \/ 2;\r\n                if (h < 2 * r) r = h \/ 2;\r\n                this.beginPath();\r\n                this.moveTo(x + r, y);\r\n                this.arcTo(x + w, y, x + w, y + h, r);\r\n                this.arcTo(x + w, y + h, x, y + h, r);\r\n                this.arcTo(x, y + h, x, y, r);\r\n                this.arcTo(x, y, x + w, y, r);\r\n                this.closePath();\r\n                return this;\r\n            };\r\n        }\r\n        \r\n        \/\/ Food data\r\n        const foods = [\r\n            { id: 'rice', name: 'Rice', icon: '\ud83c\udf5a', color: '#FFF8DC', nutrient: 'Starch (Carbohydrate)', particleColor: '#F4E4C1' },\r\n            { id: 'beef', name: 'Beef', icon: '\ud83e\udd69', color: '#8B4513', nutrient: 'Protein', particleColor: '#CD853F' },\r\n            { id: 'oil', name: 'Vegetable Oil', icon: '\ud83e\uddf4', color: '#FFD700', nutrient: 'Fat (Lipid)', particleColor: '#FFD700' },\r\n            { id: 'vegetables', name: 'Vegetables', icon: '\ud83e\udd6c', color: '#90EE90', nutrient: 'Fiber', particleColor: '#90EE90' }\r\n        ];\r\n        \r\n        \/\/ Organ data with anatomical positions\r\n        const organs = {\r\n            mouth: { \r\n                x: 560, y: 112, w: 100, h: 80,\r\n                colors: { base: '#FFB6C1', highlight: '#FFD1DC', shadow: '#FF8FA3' },\r\n                labelOffset: { x: 0, y: -25 },\r\n                name: 'Mouth',\r\n                function: 'Mechanical breakdown by chewing and initial chemical digestion',\r\n                enzymes: [\r\n                    { name: 'Salivary Amylase', function: 'Breaks down starch into maltose', affects: ['rice'] },\r\n                    { name: 'Lingual Lipase', function: 'Begins fat digestion', affects: ['oil'] }\r\n                ]\r\n            },\r\n            esophagus: { \r\n                x: 584, y: 186, w: 50, h: 110,\r\n                colors: { base: '#DDA0DD', highlight: '#F0B0F0', shadow: '#C080C0' },\r\n                labelOffset: { x: 70, y: 50 },\r\n                name: 'Esophagus',\r\n                function: 'Transports food from mouth to stomach via peristalsis',\r\n                enzymes: []\r\n            },\r\n            stomach: { \r\n                x: 552, y: 380, w: 160, h: 140,\r\n                colors: { base: '#FFB6C1', highlight: '#FFD1DC', shadow: '#FF8FA3' },\r\n                labelOffset: { x: 0, y: 155 },\r\n                name: 'Stomach',\r\n                function: 'Churns and mixes food with gastric juice',\r\n                enzymes: [\r\n                    { name: 'Pepsin', function: 'Breaks down proteins into peptides', affects: ['beef'] },\r\n                    { name: 'Gastric Lipase', function: 'Continues fat digestion', affects: ['oil'] }\r\n                ]\r\n            },\r\n            liver: { \r\n                x: 512, y: 422, w: 100, h: 80,\r\n                colors: { base: '#8B4513', highlight: '#A0522D', shadow: '#654321' },\r\n                labelOffset: { x: 0, y: -25 },\r\n                name: 'Liver',\r\n                function: 'Produces bile to emulsify fats',\r\n                enzymes: [\r\n                    { name: 'Bile', function: 'Emulsifies fats into droplets', affects: ['oil'] }\r\n                ]\r\n            },\r\n            pancreas: { \r\n                x: 557, y: 536, w: 120, h: 60,\r\n                colors: { base: '#F0E68C', highlight: '#FFFFE0', shadow: '#D4BE78' },\r\n                labelOffset: { x: -90, y: 25 },\r\n                name: 'Pancreas',\r\n                function: 'Secretes digestive enzymes',\r\n                enzymes: [\r\n                    { name: 'Pancreatic Amylase', function: 'Completes starch digestion', affects: ['rice'] },\r\n                    { name: 'Pancreatic Lipase', function: 'Breaks fats into fatty acids', affects: ['oil'] },\r\n                    { name: 'Trypsin', function: 'Digests proteins', affects: ['beef'] }\r\n                ]\r\n            },\r\n            smallIntestine: { \r\n                x: 474, y: 515, w: 280, h: 200,\r\n                colors: { base: '#FFE4B5', highlight: '#FFF8DC', shadow: '#DEB887' },\r\n                labelOffset: { x: 0, y: 0 },\r\n                labelSide: 'right',\r\n                labelMargin: 10,\r\n                name: 'Small Intestine',\r\n                function: 'Main site of digestion and nutrient absorption',\r\n                enzymes: [\r\n                    { name: 'Maltase', function: 'Converts maltose to glucose', affects: ['rice'] },\r\n                    { name: 'Peptidases', function: 'Complete protein digestion', affects: ['beef'] }\r\n                ],\r\n                absorption: [\r\n                    { nutrient: 'Glucose', from: ['rice'] },\r\n                    { nutrient: 'Amino Acids', from: ['beef'] },\r\n                    { nutrient: 'Fatty Acids', from: ['oil'] },\r\n                    { nutrient: 'Vitamins', from: ['vegetables'] }\r\n                ]\r\n            }\r\n        };\r\n        \r\n        const defaultPixelLayout = {\r\n            mouth: { x: 560, y: 112, w: 100, h: 80, labelX: 643, labelY: 140 },\r\n            esophagus: { x: 584, y: 186, w: 50, h: 110, labelX: 638, labelY: 224 },\r\n            stomach: { x: 567, y: 386, w: 160, h: 140, labelX: 644, labelY: 450 },\r\n            liver: { x: 512, y: 422, w: 100, h: 80, labelX: 535, labelY: 426 },\r\n            pancreas: { x: 570, y: 479, w: 120, h: 60, labelX: 490, labelY: 493 },\r\n            smallIntestine: { x: 479, y: 535, w: 280, h: 200, labelX: 651, labelY: 576 }\r\n        };\r\n\r\n        const defaultNormalizedLayout = {\r\n            mouth: { nx: 0.445873, ny: 0.151556, nw: 0.135318, nh: 0.108254, nlx: 0.558187, nly: 0.189445 },\r\n            esophagus: { nx: 0.478349, ny: 0.251691, nw: 0.067659, nh: 0.14885, nlx: 0.551421, nly: 0.303112 },\r\n            stomach: { nx: 0.455345, ny: 0.522327, nw: 0.216509, nh: 0.189445, nlx: 0.55954, nly: 0.608931 },\r\n            liver: { nx: 0.38092, ny: 0.571042, nw: 0.135318, nh: 0.108254, nlx: 0.412043, nly: 0.576455 },\r\n            pancreas: { nx: 0.459404, ny: 0.648173, nw: 0.162382, nh: 0.081191, nlx: 0.35115, nly: 0.667118 },\r\n            smallIntestine: { nx: 0.336265, ny: 0.723952, nw: 0.37889, nh: 0.270636, nlx: 0.569012, nly: 0.779432 }\r\n        };\r\n\r\n        const digestiveImage = new Image();\r\n        \r\n        let imagesLoaded = 0;\r\n        const totalImages = 1;\r\n        let allImagesLoaded = false;\r\n        \r\n        digestiveImage.src = 'https:\/\/raw.githubusercontent.com\/852hk\/hkmu\/refs\/heads\/main\/digestive-system-photo.jpg';\r\n        \r\n        function checkAllImagesLoaded() {\r\n            imagesLoaded++;\r\n            if (imagesLoaded === totalImages) {\r\n                allImagesLoaded = true;\r\n                drawOrgans();\r\n            }\r\n        }\r\n        \r\n        digestiveImage.onload = checkAllImagesLoaded;\r\n        digestiveImage.onerror = function() {\r\n            console.error('Failed to load image:', this.src);\r\n            checkAllImagesLoaded();\r\n        };\r\n        \r\n        \/\/ State\r\n        let selectedFoods = new Set();\r\n        let isRunning = false;\r\n        let isPaused = false;\r\n        let speed = 1;\r\n        let particles = [];\r\n        let currentStep = 0;\r\n        let animationFrame = null;\r\n        let alignMode = false;\r\n        let selectedOrganKey = 'mouth';\r\n        let isDragging = false;\r\n        let dragOffsetX = 0;\r\n        let dragOffsetY = 0;\r\n        let alignType = 'box';\r\n        let infoSequence = ['mouth','esophagus','stomach','liver','pancreas','smallIntestine'];\r\n        let segmentDuration = 5000;\r\n        let infoStartTime = 0;\r\n        let currentInfoIndex = -1;\r\n        let progressTotalDuration = segmentDuration * infoSequence.length;\r\n        \r\n        const steps = ['mouth', 'esophagus', 'stomach', 'smallIntestine'];\r\n        \r\n        function getImageDrawRect() {\r\n            const imgW = digestiveImage.naturalWidth;\r\n            const imgH = digestiveImage.naturalHeight;\r\n            const scale = Math.min(canvas.width \/ imgW, canvas.height \/ imgH);\r\n            const drawW = imgW * scale;\r\n            const drawH = imgH * scale;\r\n            const drawX = (canvas.width - drawW) \/ 2;\r\n            const drawY = (canvas.height - drawH) \/ 2;\r\n            return { drawX, drawY, drawW, drawH, scale };\r\n        }\r\n        \r\n        const organLayout = {};\r\n        const labelLayout = {};\r\n        let layoutInitialized = false;\r\n        \r\n        function initNormalizedLayout() {\r\n            if (layoutInitialized || !allImagesLoaded) return;\r\n            const { drawX, drawY, drawW, drawH } = getImageDrawRect();\r\n            if (Object.keys(organLayout).length === 0) {\r\n                if (defaultNormalizedLayout && Object.keys(defaultNormalizedLayout).length > 0) {\r\n                    Object.keys(defaultNormalizedLayout).forEach(k => {\r\n                        const s = defaultNormalizedLayout[k];\r\n                        organLayout[k] = { x: s.nx, y: s.ny, w: s.nw, h: s.nh };\r\n                        labelLayout[k] = { x: s.nlx, y: s.nly };\r\n                    });\r\n                } else if (defaultPixelLayout) {\r\n                    Object.keys(defaultPixelLayout).forEach(k => {\r\n                        const s = defaultPixelLayout[k];\r\n                        organLayout[k] = {\r\n                            x: (s.x - drawX) \/ drawW,\r\n                            y: (s.y - drawY) \/ drawH,\r\n                            w: s.w \/ drawW,\r\n                            h: s.h \/ drawH\r\n                        };\r\n                        if (typeof s.labelX === 'number' && typeof s.labelY === 'number') {\r\n                            labelLayout[k] = {\r\n                                x: (s.labelX - drawX) \/ drawW,\r\n                                y: (s.labelY - drawY) \/ drawH\r\n                            };\r\n                        }\r\n                    });\r\n                } else {\r\n                    Object.entries(organs).forEach(([key, organ]) => {\r\n                        const nx = (organ.x - drawX) \/ drawW;\r\n                        const ny = (organ.y - drawY) \/ drawH;\r\n                        const nw = organ.w \/ drawW;\r\n                        const nh = organ.h \/ drawH;\r\n                        organLayout[key] = {\r\n                            x: Math.max(0, Math.min(1, nx)),\r\n                            y: Math.max(0, Math.min(1, ny)),\r\n                            w: Math.max(0, Math.min(1, nw)),\r\n                            h: Math.max(0, Math.min(1, nh))\r\n                        };\r\n                    });\r\n                }\r\n            }\r\n            layoutInitialized = true;\r\n        }\r\n        \r\n        function getOrganRect(key) {\r\n            const { drawX, drawY, drawW, drawH } = getImageDrawRect();\r\n            const pos = organLayout[key];\r\n            const x = drawX + pos.x * drawW;\r\n            const y = drawY + pos.y * drawH;\r\n            const w = pos.w * drawW;\r\n            const h = pos.h * drawH;\r\n            return { x, y, w, h };\r\n        }\r\n        \r\n        \/\/ Helper: Create gradient\r\n        function createOrganGradient(organ, x, y, w, h) {\r\n            const gradient = ctx.createRadialGradient(\r\n                x + w * 0.3, y + h * 0.3, 0,\r\n                x + w * 0.5, y + h * 0.5, Math.max(w, h) * 0.8\r\n            );\r\n            gradient.addColorStop(0, organ.colors.highlight);\r\n            gradient.addColorStop(0.5, organ.colors.base);\r\n            gradient.addColorStop(1, organ.colors.shadow);\r\n            return gradient;\r\n        }\r\n        \r\n        \/\/ Resize canvas\r\n        function resizeCanvas() {\r\n            canvas.width = canvas.offsetWidth;\r\n            canvas.height = canvas.offsetHeight;\r\n            if (allImagesLoaded) {\r\n                drawOrgans();\r\n            }\r\n        }\r\n        resizeCanvas();\r\n        window.addEventListener('resize', resizeCanvas);\r\n        \r\n        \/\/ Initialize food list\r\n        function initFoodList() {\r\n            const foodList = document.getElementById('foodList');\r\n            foods.forEach(food => {\r\n                const item = document.createElement('div');\r\n                item.className = 'food-item';\r\n                item.innerHTML = `\r\n                    <div class=\"food-icon\" style=\"background: ${food.color}\">${food.icon}<\/div>\r\n                    <div class=\"food-info\">\r\n                        <div class=\"food-name\">${food.name}<\/div>\r\n                        <div class=\"food-nutrient\">${food.nutrient}<\/div>\r\n                    <\/div>\r\n                `;\r\n                item.addEventListener('click', () => toggleFood(food.id, item));\r\n                foodList.appendChild(item);\r\n            });\r\n        }\r\n        \r\n        function toggleFood(id, element) {\r\n            if (isRunning) return;\r\n            \r\n            if (selectedFoods.has(id)) {\r\n                selectedFoods.delete(id);\r\n                element.classList.remove('selected');\r\n            } else {\r\n                selectedFoods.add(id);\r\n                element.classList.add('selected');\r\n            }\r\n            \r\n            document.getElementById('startBtn').disabled = selectedFoods.size === 0;\r\n        }\r\n        \r\n        \/\/ Draw organs\r\n        function drawOrgans() {\r\n            ctx.clearRect(0, 0, canvas.width, canvas.height);\r\n            \r\n            if (!allImagesLoaded) {\r\n                ctx.fillStyle = '#0079c1';\r\n                ctx.font = 'bold 16px \"Microsoft YaHei\",\"\u9ed1\u9ad4\",\"SimHei\", Arial, sans-serif';\r\n                ctx.textAlign = 'center';\r\n                ctx.fillText('Loading organ images...', canvas.width \/ 2, canvas.height \/ 2);\r\n                return;\r\n            }\r\n            \r\n            const { drawX, drawY, drawW, drawH } = getImageDrawRect();\r\n            ctx.drawImage(digestiveImage, drawX, drawY, drawW, drawH);\r\n            initNormalizedLayout();\r\n            \r\n            Object.entries(organs).forEach(([key, organ]) => {\r\n                const r = getOrganRect(key);\r\n                if (alignMode && alignType === 'box') {\r\n                    ctx.strokeStyle = key === selectedOrganKey ? '#ff4757' : 'rgba(102, 126, 234, 0.7)';\r\n                    ctx.lineWidth = key === selectedOrganKey ? 3 : 2;\r\n                    ctx.strokeRect(r.x, r.y, r.w, r.h);\r\n                }\r\n                \r\n                ctx.font = 'bold 13px \"Microsoft YaHei\",\"\u9ed1\u9ad4\",\"SimHei\", Arial, sans-serif';\r\n                ctx.textAlign = 'center';\r\n                ctx.textBaseline = 'middle';\r\n                const labelRect = getLabelRect(key, r, organ);\r\n                \r\n                ctx.fillStyle = 'rgba(255, 255, 255, 0.95)';\r\n                ctx.fillRect(labelRect.x, labelRect.y, labelRect.w, labelRect.h);\r\n                ctx.strokeStyle = 'rgba(0, 121, 193, 0.5)';\r\n                ctx.lineWidth = 1;\r\n                ctx.strokeRect(labelRect.x, labelRect.y, labelRect.w, labelRect.h);\r\n                \r\n                ctx.fillStyle = '#0079c1';\r\n                ctx.fillText(organ.name, labelRect.x + labelRect.w\/2, labelRect.y + labelRect.h\/2);\r\n\r\n                if (alignMode && alignType === 'label') {\r\n                    ctx.strokeStyle = key === selectedOrganKey ? '#ff4757' : 'rgba(102, 126, 234, 0.7)';\r\n                    ctx.lineWidth = key === selectedOrganKey ? 3 : 2;\r\n                    ctx.strokeRect(labelRect.x, labelRect.y, labelRect.w, labelRect.h);\r\n                }\r\n            });\r\n        }\r\n\r\n        function getLabelRect(key, organRect, organ) {\r\n            const { drawX, drawY, drawW, drawH } = getImageDrawRect();\r\n            const width = ctx.measureText(organ.name).width + 12;\r\n            const height = 20;\r\n            const saved = labelLayout[key];\r\n            if (saved) {\r\n                const x = drawX + saved.x * drawW;\r\n                const y = drawY + saved.y * drawH;\r\n                return { x, y, w: width, h: height };\r\n            }\r\n            let cx = organRect.x + organRect.w\/2 + (organ.labelOffset?.x || 0);\r\n            let cy = organRect.y + (organ.labelOffset?.y || 0);\r\n            if (organ.labelSide === 'right') {\r\n                cx = organRect.x + organRect.w + (organ.labelMargin || 10) + width\/2;\r\n                cy = organRect.y + organRect.h\/2 + (organ.labelOffset?.y || 0);\r\n            }\r\n            return { x: cx - width\/2, y: cy - 12, w: width, h: height };\r\n        }\r\n        \r\n        canvas.addEventListener('click', (e) => {\r\n            if (alignMode) return;\r\n            const rect = canvas.getBoundingClientRect();\r\n            const x = e.clientX - rect.left;\r\n            const y = e.clientY - rect.top;\r\n            for (const [key, organ] of Object.entries(organs)) {\r\n                const r = getOrganRect(key);\r\n                const lbl = getLabelRect(key, r, organ);\r\n                if (x >= lbl.x && x <= lbl.x + lbl.w && y >= lbl.y && y <= lbl.y + lbl.h) {\r\n                    showOrganInfo(key);\r\n                    speakOrgan(key);\r\n                    return;\r\n                }\r\n            }\r\n\r\n            Object.entries(organs).forEach(([key]) => {\r\n                const r = getOrganRect(key);\r\n                if (x >= r.x && x <= r.x + r.w &&\r\n                    y >= r.y && y <= r.y + r.h) {\r\n                    showOrganInfo(key);\r\n                }\r\n            });\r\n        });\r\n\r\n        canvas.addEventListener('mousedown', (e) => {\r\n            if (!alignMode) return;\r\n            const rect = canvas.getBoundingClientRect();\r\n            const x = e.clientX - rect.left;\r\n            const y = e.clientY - rect.top;\r\n            if (alignType === 'box') {\r\n                const r = getOrganRect(selectedOrganKey);\r\n                if (x >= r.x && x <= r.x + r.w && y >= r.y && y <= r.y + r.h) {\r\n                    isDragging = true;\r\n                    dragOffsetX = x - r.x;\r\n                    dragOffsetY = y - r.y;\r\n                }\r\n                return;\r\n            }\r\n            const rOrg = getOrganRect(selectedOrganKey);\r\n            const lbl = getLabelRect(selectedOrganKey, rOrg, organs[selectedOrganKey]);\r\n            if (x >= lbl.x && x <= lbl.x + lbl.w && y >= lbl.y && y <= lbl.y + lbl.h) {\r\n                isDragging = true;\r\n                dragOffsetX = x - lbl.x;\r\n                dragOffsetY = y - lbl.y;\r\n            }\r\n        });\r\n\r\n        canvas.addEventListener('mousemove', (e) => {\r\n            if (!alignMode || !isDragging) return;\r\n            const rect = canvas.getBoundingClientRect();\r\n            const x = e.clientX - rect.left;\r\n            const y = e.clientY - rect.top;\r\n            const { drawX, drawY, drawW, drawH } = getImageDrawRect();\r\n            const newX = x - dragOffsetX;\r\n            const newY = y - dragOffsetY;\r\n            if (alignType === 'box') {\r\n                organLayout[selectedOrganKey].x = (newX - drawX) \/ drawW;\r\n                organLayout[selectedOrganKey].y = (newY - drawY) \/ drawH;\r\n            } else {\r\n                labelLayout[selectedOrganKey] = labelLayout[selectedOrganKey] || { x: 0, y: 0 };\r\n                labelLayout[selectedOrganKey].x = (newX - drawX) \/ drawW;\r\n                labelLayout[selectedOrganKey].y = (newY - drawY) \/ drawH;\r\n            }\r\n            updateAlignInputs();\r\n            drawOrgans();\r\n        });\r\n\r\n        canvas.addEventListener('mouseup', () => {\r\n            if (!alignMode) return;\r\n            isDragging = false;\r\n        });\r\n        \r\n        function showOrganInfo(organKey) {\r\n            const organ = organs[organKey];\r\n            \r\n            document.getElementById('organName').textContent = organ.name;\r\n            document.getElementById('organFunction').textContent = organ.function;\r\n            \r\n            const enzymeInfo = document.getElementById('enzymeInfo');\r\n            enzymeInfo.innerHTML = '';\r\n            \r\n            if (organ.enzymes && organ.enzymes.length > 0) {\r\n                const title = document.createElement('h3');\r\n                title.textContent = 'Digestive Enzymes:';\r\n                enzymeInfo.appendChild(title);\r\n                \r\n                organ.enzymes.forEach(enzyme => {\r\n                    const tag = document.createElement('div');\r\n                    tag.className = 'enzyme-tag';\r\n                    tag.innerHTML = `<span class=\"enzyme-name\">${enzyme.name}:<\/span> ${enzyme.function}`;\r\n                    enzymeInfo.appendChild(tag);\r\n                });\r\n            }\r\n            \r\n            if (organ.absorption) {\r\n                const title = document.createElement('h3');\r\n                title.textContent = 'Nutrients Absorbed:';\r\n                title.style.marginTop = '10px';\r\n                enzymeInfo.appendChild(title);\r\n                \r\n                organ.absorption.forEach(item => {\r\n                    const tag = document.createElement('div');\r\n                    tag.className = 'enzyme-tag';\r\n                    tag.style.borderColor = '#28a745';\r\n                    tag.style.background = '#d4edda';\r\n                    tag.innerHTML = `<span class=\"enzyme-name\">${item.nutrient}<\/span> \u2192 Bloodstream`;\r\n                    enzymeInfo.appendChild(tag);\r\n                });\r\n            }\r\n            \r\n            infoBox.classList.add('visible');\r\n        }\r\n\r\n        function speakOrgan(organKey) {\r\n            const synth = window.speechSynthesis;\r\n            if (!synth) return;\r\n            synth.cancel();\r\n            const organ = organs[organKey];\r\n            if (!organ) return;\r\n            let text = organ.name + '. ' + organ.function;\r\n            if (organ.enzymes && organ.enzymes.length) {\r\n                const names = organ.enzymes.map(e => e.name).join(', ');\r\n                text += '. Enzymes: ' + names + '.';\r\n            }\r\n            if (organ.absorption && organ.absorption.length) {\r\n                const nutrients = organ.absorption.map(a => a.nutrient).join(', ');\r\n                text += ' Absorbs: ' + nutrients + '.';\r\n            }\r\n            const utter = new SpeechSynthesisUtterance(text);\r\n            utter.lang = 'en-US';\r\n            utter.rate = 1;\r\n            utter.pitch = 1;\r\n            synth.speak(utter);\r\n        }\r\n        \r\n        \/\/ Start digestion\r\n        document.getElementById('startBtn').addEventListener('click', startDigestion);\r\n        \r\n        function startDigestion() {\r\n            if (selectedFoods.size === 0) return;\r\n            \r\n            isRunning = true;\r\n            isPaused = false;\r\n            currentStep = 0;\r\n            particles = [];\r\n            \r\n            selectedFoods.forEach(foodId => {\r\n                const food = foods.find(f => f.id === foodId);\r\n                const rMouth = getOrganRect('mouth');\r\n                for (let i = 0; i < 15; i++) {\r\n                    particles.push({\r\n                        foodId: foodId,\r\n                        color: food.particleColor,\r\n                        x: rMouth.x + rMouth.w\/2 + (Math.random() - 0.5) * 40,\r\n                        y: rMouth.y + rMouth.h\/2,\r\n                        targetX: 0,\r\n                        targetY: 0,\r\n                        step: 0,\r\n                        absorbed: false,\r\n                        inBloodstream: false,\r\n                        siEnterTime: 0\r\n                    });\r\n                }\r\n            });\r\n            \r\n            document.getElementById('startBtn').disabled = true;\r\n            document.getElementById('pauseBtn').disabled = false;\r\n            document.getElementById('speedBtn').disabled = false;\r\n            infoStartTime = Date.now();\r\n            currentInfoIndex = 0;\r\n            progressTotalDuration = segmentDuration * infoSequence.length;\r\n            progressBar.style.width = '0%';\r\n            progressBar.textContent = '0%';\r\n            updateStatus('\ud83c\udf7d\ufe0f Food entering the mouth... Chewing begins!');\r\n            showOrganInfo(infoSequence[currentInfoIndex]);\r\n            \r\n            animate();\r\n        }\r\n        \r\n        function animate() {\r\n            if (!isRunning || isPaused) return;\r\n            \r\n            drawOrgans();\r\n            \r\n            let activeParticles = 0;\r\n            particles.forEach(particle => {\r\n                if (particle.absorbed) {\r\n                    return;\r\n                }\r\n                \r\n                activeParticles++;\r\n                \r\n                if (particle.step < steps.length) {\r\n                    const organKey = steps[particle.step];\r\n                    const r = getOrganRect(organKey);\r\n                    particle.targetX = r.x + r.w\/2 + (Math.random() - 0.5) * r.w * 0.8;\r\n                    particle.targetY = r.y + r.h\/2 + (Math.random() - 0.5) * r.h * 0.8;\r\n                }\r\n                \r\n                const dx = particle.targetX - particle.x;\r\n                const dy = particle.targetY - particle.y;\r\n                const dist = Math.sqrt(dx*dx + dy*dy);\r\n                \r\n                if (dist > 2) {\r\n                    particle.x += dx * 0.03 * speed;\r\n                    particle.y += dy * 0.03 * speed;\r\n                } else {\r\n                    particle.step++;\r\n                    if (particle.step >= steps.length) {\r\n                        particle.absorbed = true;\r\n                    }\r\n                }\r\n                \r\n                \/\/ Draw particle\r\n                ctx.fillStyle = particle.color;\r\n                ctx.beginPath();\r\n                ctx.arc(particle.x, particle.y, 4, 0, Math.PI * 2);\r\n                ctx.fill();\r\n                ctx.strokeStyle = '#333';\r\n                ctx.lineWidth = 1;\r\n                ctx.stroke();\r\n                \r\n                \/\/ Linger briefly in small intestine, then disappear\r\n                if (steps[particle.step] === 'smallIntestine') {\r\n                    if (!particle.siEnterTime) {\r\n                        particle.siEnterTime = Date.now();\r\n                    }\r\n                    const lingerMs = Math.max(1500, Math.round(3000 \/ speed));\r\n                    if (Date.now() - particle.siEnterTime >= lingerMs) {\r\n                        particle.absorbed = true;\r\n                    }\r\n                }\r\n            });\r\n            \r\n            const elapsed = Math.max(0, Date.now() - infoStartTime);\r\n            const progress = Math.min(100, Math.round((elapsed \/ progressTotalDuration) * 100));\r\n            progressBar.style.width = progress + '%';\r\n            progressBar.textContent = progress + '%';\r\n            const newInfoIndex = Math.min(infoSequence.length - 1, Math.floor(elapsed \/ segmentDuration));\r\n            if (newInfoIndex !== currentInfoIndex) {\r\n                currentInfoIndex = newInfoIndex;\r\n                showOrganInfo(infoSequence[currentInfoIndex]);\r\n            }\r\n            \r\n            \/\/ Check for step changes\r\n            const avgStep = particles.reduce((sum, p) => sum + Math.min(p.step, steps.length - 1), 0) \/ particles.length;\r\n            const newStep = Math.floor(avgStep);\r\n            \r\n            if (newStep !== currentStep) {\r\n                currentStep = newStep;\r\n                if (currentStep < steps.length) {\r\n                    showOrganInfo(steps[currentStep]);\r\n                    updateStatusForStep(currentStep);\r\n                }\r\n            }\r\n            \r\n            \/\/ Check if complete (based on timed progress)\r\n            if (progress >= 100) {\r\n                completeDigestion();\r\n                return;\r\n            }\r\n            \r\n            animationFrame = requestAnimationFrame(animate);\r\n        }\r\n        \r\n        function updateStatusForStep(step) {\r\n            const messages = [\r\n                'Mouth: Chewing breaks down food. Salivary amylase begins starch digestion!',\r\n                'Esophagus: Peristaltic waves push food downward to the stomach.',\r\n                'Stomach: Churning mixes food with gastric juice. Pepsin digests proteins!',\r\n                'Small Intestine: Most digestion and absorption happens here! Nutrients enter bloodstream.'\r\n            ];\r\n            \r\n            if (messages[step]) {\r\n                updateStatus(messages[step]);\r\n            }\r\n        }\r\n        \r\n        function updateStatus(message) {\r\n            statusPanel.innerHTML = `<div class=\"status-message\">${message}<\/div>`;\r\n        }\r\n        \r\n        \/\/ Control buttons\r\n        document.getElementById('pauseBtn').addEventListener('click', () => {\r\n            isPaused = !isPaused;\r\n            document.getElementById('pauseBtn').innerHTML = isPaused ? '\u25b6\ufe0f Resume' : '\u23f8\ufe0f Pause';\r\n            if (!isPaused) animate();\r\n        });\r\n        \r\n        document.getElementById('speedBtn').addEventListener('click', () => {\r\n            speed = speed === 1 ? 2 : 1;\r\n            document.getElementById('speedBtn').innerHTML = speed === 2 ? '\u23e9 Fast' : '\u25b6\ufe0f Normal';\r\n        });\r\n        \r\n        document.getElementById('resetBtn').addEventListener('click', () => {\r\n            isRunning = false;\r\n            isPaused = false;\r\n            speed = 1;\r\n            currentStep = 0;\r\n            particles = [];\r\n            selectedFoods.clear();\r\n            \r\n            if (animationFrame) {\r\n                cancelAnimationFrame(animationFrame);\r\n                animationFrame = null;\r\n            }\r\n            \r\n            document.querySelectorAll('.food-item').forEach(item => {\r\n                item.classList.remove('selected');\r\n            });\r\n            \r\n            document.getElementById('startBtn').disabled = true;\r\n            document.getElementById('pauseBtn').disabled = true;\r\n            document.getElementById('pauseBtn').innerHTML = '\u23f8\ufe0f Pause';\r\n            document.getElementById('speedBtn').disabled = true;\r\n            document.getElementById('speedBtn').innerHTML = '\u23e9 Fast';\r\n            alignMode = false;\r\n            document.getElementById('alignControls').style.display = 'none';\r\n            document.getElementById('alignBtn').innerHTML = '\ud83d\udcd0 Align Organs';\r\n            \r\n            progressBar.style.width = '0%';\r\n            progressBar.textContent = '0%';\r\n            \r\n            infoBox.classList.remove('visible');\r\n            quizSection.style.display = 'none';\r\n            \r\n            statusPanel.innerHTML = '<p>Select food items and click \"Start Digestion\" to begin!<\/p>';\r\n            \r\n            drawOrgans();\r\n        });\r\n        \r\n        function completeDigestion() {\r\n            isRunning = false;\r\n            updateStatus('\u2705 Digestion Complete! Nutrients absorbed into bloodstream.');\r\n            \r\n            document.getElementById('pauseBtn').disabled = true;\r\n            document.getElementById('speedBtn').disabled = true;\r\n            \r\n            setTimeout(showQuiz, 1000);\r\n        }\r\n        \r\n        function showQuiz() {\r\n            quizSection.style.display = 'block';\r\n            const quizQuestions = document.getElementById('quizQuestions');\r\n            quizQuestions.innerHTML = '';\r\n            \r\n            const questions = [\r\n                {\r\n                    q: '\u2753 Where does starch begin its digestion?',\r\n                    a: 'Starch digestion begins in the <strong>mouth<\/strong> where salivary amylase breaks it down into smaller carbohydrates like maltose. The digestion continues in the small intestine with pancreatic amylase, and is completed by maltase which converts maltose into glucose for absorption.'\r\n                },\r\n                {\r\n                    q: '\u2753 Which organ produces bile and what is its function?',\r\n                    a: 'The <strong>liver<\/strong> produces bile, which is stored in the gallbladder. Bile emulsifies fats, breaking them into smaller droplets so that lipase enzymes can digest them more efficiently. This is essential for proper fat digestion and absorption.'\r\n                },\r\n                {\r\n                    q: '\u2753 Where does most nutrient absorption occur?',\r\n                    a: 'The <strong>small intestine<\/strong> is where most nutrient absorption happens. Its inner surface has millions of tiny finger-like projections called villi and microvilli that increase surface area dramatically. Glucose, amino acids, fatty acids, vitamins, and minerals are absorbed through the intestinal wall into the bloodstream.'\r\n                }\r\n            ];\r\n            \r\n            questions.forEach((item, index) => {\r\n                const questionDiv = document.createElement('div');\r\n                questionDiv.className = 'question';\r\n                questionDiv.innerHTML = `\r\n                    <div><strong>${item.q}<\/strong><\/div>\r\n                    <div class=\"answer\">${item.a}<\/div>\r\n                `;\r\n                \r\n                questionDiv.addEventListener('click', function() {\r\n                    const answer = this.querySelector('.answer');\r\n                    const isVisible = answer.classList.contains('visible');\r\n                    \r\n                    document.querySelectorAll('.answer').forEach(a => a.classList.remove('visible'));\r\n                    document.querySelectorAll('.question').forEach(q => q.classList.remove('revealed'));\r\n                    \r\n                    if (!isVisible) {\r\n                        answer.classList.add('visible');\r\n                        this.classList.add('revealed');\r\n                    }\r\n                });\r\n                \r\n                quizQuestions.appendChild(questionDiv);\r\n            });\r\n        }\r\n        \r\n        \/\/ Initialize\r\n        initFoodList();\r\n        initAlignControls();\r\n        applySavedLayout();\r\n        drawOrgans();\r\n        if (document.fonts && document.fonts.ready) {\r\n            document.fonts.ready.then(() => { drawOrgans(); });\r\n        }\r\n\r\n        function initAlignControls() {\r\n            const organSelect = document.getElementById('organSelect');\r\n            Object.keys(organs).forEach(k => {\r\n                const opt = document.createElement('option');\r\n                opt.value = k;\r\n                opt.textContent = organs[k].name;\r\n                organSelect.appendChild(opt);\r\n            });\r\n            organSelect.value = selectedOrganKey;\r\n            document.getElementById('alignBtn').addEventListener('click', () => {\r\n                alignMode = !alignMode;\r\n                document.getElementById('alignControls').style.display = alignMode ? 'block' : 'none';\r\n                document.getElementById('startBtn').disabled = alignMode ? true : selectedFoods.size === 0;\r\n                document.getElementById('pauseBtn').disabled = alignMode ? true : !isRunning;\r\n                document.getElementById('speedBtn').disabled = alignMode ? true : !isRunning;\r\n                document.getElementById('alignBtn').innerHTML = alignMode ? '\u2705 Exit Align' : '\ud83d\udcd0 Align Organs';\r\n                updateAlignInputs();\r\n                drawOrgans();\r\n            });\r\n            document.getElementById('alignTypeSelect').addEventListener('change', (e) => {\r\n                alignType = e.target.value;\r\n                const showLabelInputs = alignType === 'label';\r\n                document.getElementById('inputLabelX').style.display = showLabelInputs ? 'block' : 'none';\r\n                document.getElementById('inputLabelY').style.display = showLabelInputs ? 'block' : 'none';\r\n                document.getElementById('inputW').style.display = showLabelInputs ? 'none' : 'block';\r\n                document.getElementById('inputH').style.display = showLabelInputs ? 'none' : 'block';\r\n                updateAlignInputs();\r\n                drawOrgans();\r\n            });\r\n            organSelect.addEventListener('change', (e) => {\r\n                selectedOrganKey = e.target.value;\r\n                updateAlignInputs();\r\n                drawOrgans();\r\n            });\r\n            ['inputX','inputY','inputW','inputH'].forEach(id => {\r\n                document.getElementById(id).addEventListener('input', () => {\r\n                    const { drawX, drawY, drawW, drawH } = getImageDrawRect();\r\n                    const px = parseFloat(document.getElementById('inputX').value);\r\n                    const py = parseFloat(document.getElementById('inputY').value);\r\n                    const pw = parseFloat(document.getElementById('inputW').value);\r\n                    const ph = parseFloat(document.getElementById('inputH').value);\r\n                    const pos = organLayout[selectedOrganKey];\r\n                    if (!isNaN(px)) pos.x = (px - drawX) \/ drawW;\r\n                    if (!isNaN(py)) pos.y = (py - drawY) \/ drawH;\r\n                    if (!isNaN(pw)) pos.w = pw \/ drawW;\r\n                    if (!isNaN(ph)) pos.h = ph \/ drawH;\r\n                    drawOrgans();\r\n                });\r\n            });\r\n            ['inputLabelX','inputLabelY'].forEach(id => {\r\n                document.getElementById(id).addEventListener('input', () => {\r\n                    const { drawX, drawY, drawW, drawH } = getImageDrawRect();\r\n                    const px = parseFloat(document.getElementById('inputLabelX').value);\r\n                    const py = parseFloat(document.getElementById('inputLabelY').value);\r\n                    labelLayout[selectedOrganKey] = labelLayout[selectedOrganKey] || { x: 0, y: 0 };\r\n                    if (!isNaN(px)) labelLayout[selectedOrganKey].x = (px - drawX) \/ drawW;\r\n                    if (!isNaN(py)) labelLayout[selectedOrganKey].y = (py - drawY) \/ drawH;\r\n                    drawOrgans();\r\n                });\r\n            });\r\n            document.getElementById('saveLayoutBtn').addEventListener('click', () => {\r\n                localStorage.setItem('organLayoutNormalized', JSON.stringify(organLayout));\r\n                localStorage.setItem('labelLayoutNormalized', JSON.stringify(labelLayout));\r\n                updateStatus('\ud83d\udcbe Layout saved');\r\n            });\r\n            document.getElementById('copyLayoutBtn').addEventListener('click', async () => {\r\n                const out = {};\r\n                const dr = getImageDrawRect();\r\n                out.__drawRect = { x: Math.round(dr.drawX), y: Math.round(dr.drawY), w: Math.round(dr.drawW), h: Math.round(dr.drawH) };\r\n                Object.keys(organs).forEach(k => {\r\n                    const r = getOrganRect(k);\r\n                    const lr = getLabelRect(k, r, organs[k]);\r\n                    const n = organLayout[k] || { x: 0, y: 0, w: 0, h: 0 };\r\n                    const nl = labelLayout[k] || { x: (lr.x - dr.drawX) \/ dr.drawW, y: (lr.y - dr.drawY) \/ dr.drawH };\r\n                    out[k] = {\r\n                        x: Math.round(r.x), y: Math.round(r.y), w: Math.round(r.w), h: Math.round(r.h),\r\n                        labelX: Math.round(lr.x), labelY: Math.round(lr.y),\r\n                        nx: +n.x.toFixed(6), ny: +n.y.toFixed(6), nw: +n.w.toFixed(6), nh: +n.h.toFixed(6),\r\n                        nlx: +nl.x.toFixed(6), nly: +nl.y.toFixed(6)\r\n                    };\r\n                });\r\n                const text = JSON.stringify(out, null, 2);\r\n                try { await navigator.clipboard.writeText(text); updateStatus('\ud83d\udccb Layout copied'); } catch(e) { updateStatus('\u26a0\ufe0f Copy failed'); }\r\n            });\r\n\r\n            function clamp01(v) { return Math.max(0, Math.min(1, v)); }\r\n            document.getElementById('applyLayoutBtn').addEventListener('click', () => {\r\n                try {\r\n                    const raw = document.getElementById('layoutJson').value.trim();\r\n                    if (!raw) return;\r\n                    const obj = JSON.parse(raw);\r\n                    let base = getImageDrawRect();\r\n                    if (obj.__drawRect && typeof obj.__drawRect.w === 'number' && typeof obj.__drawRect.h === 'number') {\r\n                        base = { drawX: obj.__drawRect.x || 0, drawY: obj.__drawRect.y || 0, drawW: obj.__drawRect.w, drawH: obj.__drawRect.h };\r\n                    }\r\n                    const { drawX, drawY, drawW, drawH } = base;\r\n                    Object.keys(obj).forEach(k => {\r\n                        const s = obj[k];\r\n                        if (!s) return;\r\n                        organLayout[k] = organLayout[k] || { x: 0, y: 0, w: 0, h: 0 };\r\n                        if (typeof s.nx === 'number' || typeof s.ny === 'number' || typeof s.nw === 'number' || typeof s.nh === 'number') {\r\n                            if (typeof s.nx === 'number') organLayout[k].x = clamp01(s.nx);\r\n                            if (typeof s.ny === 'number') organLayout[k].y = clamp01(s.ny);\r\n                            if (typeof s.nw === 'number') organLayout[k].w = clamp01(s.nw);\r\n                            if (typeof s.nh === 'number') organLayout[k].h = clamp01(s.nh);\r\n                        } else {\r\n                            if (typeof s.x === 'number') organLayout[k].x = clamp01((s.x - drawX) \/ drawW);\r\n                            if (typeof s.y === 'number') organLayout[k].y = clamp01((s.y - drawY) \/ drawH);\r\n                            if (typeof s.w === 'number') organLayout[k].w = clamp01(s.w \/ drawW);\r\n                            if (typeof s.h === 'number') organLayout[k].h = clamp01(s.h \/ drawH);\r\n                        }\r\n                        if (typeof s.labelX === 'number' || typeof s.labelY === 'number' || typeof s.nlx === 'number' || typeof s.nly === 'number') {\r\n                            labelLayout[k] = labelLayout[k] || { x: 0, y: 0 };\r\n                            if (typeof s.nlx === 'number') labelLayout[k].x = clamp01(s.nlx);\r\n                            else if (typeof s.labelX === 'number') labelLayout[k].x = clamp01((s.labelX - drawX) \/ drawW);\r\n                            if (typeof s.nly === 'number') labelLayout[k].y = clamp01(s.nly);\r\n                            else if (typeof s.labelY === 'number') labelLayout[k].y = clamp01((s.labelY - drawY) \/ drawH);\r\n                        }\r\n                    });\r\n                    updateStatus('\u2705 Layout applied');\r\n                    updateAlignInputs();\r\n                    drawOrgans();\r\n                } catch (e) {\r\n                    updateStatus('\u26a0\ufe0f Invalid JSON');\r\n                }\r\n            });\r\n            updateAlignInputs();\r\n        }\r\n\r\n        function updateAlignInputs() {\r\n            const r = getOrganRect(selectedOrganKey);\r\n            const lr = getLabelRect(selectedOrganKey, r, organs[selectedOrganKey]);\r\n            document.getElementById('inputX').value = Math.round(r.x);\r\n            document.getElementById('inputY').value = Math.round(r.y);\r\n            document.getElementById('inputW').value = Math.round(r.w);\r\n            document.getElementById('inputH').value = Math.round(r.h);\r\n            document.getElementById('inputLabelX').value = Math.round(lr.x);\r\n            document.getElementById('inputLabelY').value = Math.round(lr.y);\r\n        }\r\n\r\n        function applySavedLayout() {\r\n            try {\r\n                const savedNorm = localStorage.getItem('organLayoutNormalized');\r\n                if (savedNorm) {\r\n                    Object.assign(organLayout, JSON.parse(savedNorm));\r\n                    layoutInitialized = true;\r\n                }\r\n                const savedLabel = localStorage.getItem('labelLayoutNormalized');\r\n                if (savedLabel) {\r\n                    Object.assign(labelLayout, JSON.parse(savedLabel));\r\n                }\r\n                const savedPix = localStorage.getItem('organLayout');\r\n                if (savedPix) {\r\n                    const obj = JSON.parse(savedPix);\r\n                    const { drawX, drawY, drawW, drawH } = getImageDrawRect();\r\n                    Object.keys(obj).forEach(k => {\r\n                        if (obj[k]) {\r\n                            const s = obj[k];\r\n                            organLayout[k] = {\r\n                                x: (s.x - drawX) \/ drawW,\r\n                                y: (s.y - drawY) \/ drawH,\r\n                                w: s.w \/ drawW,\r\n                                h: s.h \/ drawH\r\n                            };\r\n                        }\r\n                    });\r\n                    layoutInitialized = true;\r\n                }\r\n            } catch {}\r\n        }\r\n    <\/script>\r\n<\/body>\r\n<\/html>\r\n\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>Interactive Digestive System Digestive System Visualisation Visualize the human digestive process with interactive organ models and animated food breakdown. Select Food Start Digestion \u23f8\ufe0f Pause \u25b6\ufe0f Normal \ud83d\udd04 Reset Alignment Align Box Align Label Save Layout Copy JSON Apply JSON Human Digestive System 0% Digestion Process Select food items and click \"Start Digestion\" to begin!...<\/p>\n","protected":false},"author":748,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"redux-templates_canvas","meta":{"_expiration-date-status":"","_expiration-date":0,"_expiration-date-type":"","_expiration-date-categories":[],"_expiration-date-options":[]},"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v17.3 (Yoast SEO v21.2) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Resource 3 Digestive System Visualisation - Hong Kong Metropolitan University<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.hkmu.edu.hk\/oetools\/resource-3-digestive-system-visualisation\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Resource 3 Digestive System Visualisation\" \/>\n<meta property=\"og:description\" content=\"Interactive Digestive System Digestive System Visualisation Visualize the human digestive process with interactive organ models and animated food\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.hkmu.edu.hk\/oetools\/resource-3-digestive-system-visualisation\/\" \/>\n<meta property=\"og:site_name\" content=\"Hong Kong Metropolitan University\" \/>\n<meta property=\"article:modified_time\" content=\"2025-11-26T08:39:21+00:00\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"1 minute\" \/>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Resource 3 Digestive System Visualisation - Hong Kong Metropolitan University","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.hkmu.edu.hk\/oetools\/resource-3-digestive-system-visualisation\/","og_locale":"en_US","og_type":"article","og_title":"Resource 3 Digestive System Visualisation","og_description":"Interactive Digestive System Digestive System Visualisation Visualize the human digestive process with interactive organ models and animated food","og_url":"https:\/\/www.hkmu.edu.hk\/oetools\/resource-3-digestive-system-visualisation\/","og_site_name":"Hong Kong Metropolitan University","article_modified_time":"2025-11-26T08:39:21+00:00","twitter_card":"summary_large_image","twitter_misc":{"Est. reading time":"1 minute"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.hkmu.edu.hk\/oetools\/resource-3-digestive-system-visualisation\/","url":"https:\/\/www.hkmu.edu.hk\/oetools\/resource-3-digestive-system-visualisation\/","name":"Resource 3 Digestive System Visualisation - Hong Kong Metropolitan University","isPartOf":{"@id":"https:\/\/www.hkmu.edu.hk\/oetools\/#website"},"datePublished":"2025-11-26T08:10:26+00:00","dateModified":"2025-11-26T08:39:21+00:00","breadcrumb":{"@id":"https:\/\/www.hkmu.edu.hk\/oetools\/resource-3-digestive-system-visualisation\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.hkmu.edu.hk\/oetools\/resource-3-digestive-system-visualisation\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.hkmu.edu.hk\/oetools\/resource-3-digestive-system-visualisation\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Open Educational Tools","item":"\/oetools\/"},{"@type":"ListItem","position":2,"name":"Resource 3 Digestive System Visualisation"}]},{"@type":"WebSite","@id":"https:\/\/www.hkmu.edu.hk\/oetools\/#website","url":"https:\/\/www.hkmu.edu.hk\/oetools\/","name":"Hong Kong Metropolitan University","description":"Open Educational Tools - Hong Kong Metropolitan University","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.hkmu.edu.hk\/oetools\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"}]}},"_links":{"self":[{"href":"https:\/\/www.hkmu.edu.hk\/oetools\/wp-json\/wp\/v2\/pages\/31318"}],"collection":[{"href":"https:\/\/www.hkmu.edu.hk\/oetools\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.hkmu.edu.hk\/oetools\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.hkmu.edu.hk\/oetools\/wp-json\/wp\/v2\/users\/748"}],"replies":[{"embeddable":true,"href":"https:\/\/www.hkmu.edu.hk\/oetools\/wp-json\/wp\/v2\/comments?post=31318"}],"version-history":[{"count":16,"href":"https:\/\/www.hkmu.edu.hk\/oetools\/wp-json\/wp\/v2\/pages\/31318\/revisions"}],"predecessor-version":[{"id":31335,"href":"https:\/\/www.hkmu.edu.hk\/oetools\/wp-json\/wp\/v2\/pages\/31318\/revisions\/31335"}],"wp:attachment":[{"href":"https:\/\/www.hkmu.edu.hk\/oetools\/wp-json\/wp\/v2\/media?parent=31318"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}