├── 1.gif ├── 2.gif ├── 3.gif ├── README.md ├── index.html └── jquery.odontogram.js /1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Adhiana46/jquery-odontogram/6d35bed7b22a899179896e4237e3e8c9f8e88b45/1.gif -------------------------------------------------------------------------------- /2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Adhiana46/jquery-odontogram/6d35bed7b22a899179896e4237e3e8c9f8e88b45/2.gif -------------------------------------------------------------------------------- /3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Adhiana46/jquery-odontogram/6d35bed7b22a899179896e4237e3e8c9f8e88b45/3.gif -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jquery-odontogram plugin 2 | 3 | Easy to use jQuery plugin for creating Odontogram using HTML5 Canvas. 4 | 5 | Example 6 | ----- 7 | ![Example 1](1.gif) 8 | ![Example 2](2.gif) 9 | ![Example 3](3.gif) 10 | 11 | Usage 12 | ----- 13 | Initialize Odontogram, make sure the element is a Canvas. 14 | ```js 15 | $("#odontogram-canvas").odontogram({ 16 | width: "900px", 17 | height: "420px" 18 | }); 19 | ``` 20 | 21 | Set to Default mode. 22 | ```js 23 | $("#odontogram-canvas").odontogram('setMode', ODONTOGRAM_MODE_DEFAULT); 24 | ``` 25 | 26 | Set to Delete mode. 27 | ```js 28 | $("#odontogram-canvas").odontogram('setMode', ODONTOGRAM_MODE_HAPUS); 29 | ``` 30 | 31 | Set to AMF mode. 32 | ```js 33 | $("#odontogram-canvas").odontogram('setMode', ODONTOGRAM_MODE_AMF); 34 | ``` 35 | 36 | Set to AMF mode. 37 | ```js 38 | $("#odontogram-canvas").odontogram('setMode', ODONTOGRAM_MODE_AMF); 39 | ``` 40 | 41 | Set to COF mode. 42 | ```js 43 | $("#odontogram-canvas").odontogram('setMode', ODONTOGRAM_MODE_COF); 44 | ``` 45 | 46 | Set to FIS mode. 47 | ```js 48 | $("#odontogram-canvas").odontogram('setMode', ODONTOGRAM_MODE_FIS); 49 | ``` 50 | 51 | Set to NVT mode. 52 | ```js 53 | $("#odontogram-canvas").odontogram('setMode', ODONTOGRAM_MODE_NVT); 54 | ``` 55 | 56 | Set to RCT mode. 57 | ```js 58 | $("#odontogram-canvas").odontogram('setMode', ODONTOGRAM_MODE_RCT); 59 | ``` 60 | 61 | Set to NON mode. 62 | ```js 63 | $("#odontogram-canvas").odontogram('setMode', ODONTOGRAM_MODE_NON); 64 | ``` 65 | 66 | Set to UNE mode. 67 | ```js 68 | $("#odontogram-canvas").odontogram('setMode', ODONTOGRAM_MODE_UNE); 69 | ``` 70 | 71 | Set to PRE mode. 72 | ```js 73 | $("#odontogram-canvas").odontogram('setMode', ODONTOGRAM_MODE_PRE); 74 | ``` 75 | 76 | Set to ANO mode. 77 | ```js 78 | $("#odontogram-canvas").odontogram('setMode', ODONTOGRAM_MODE_ANO); 79 | ``` 80 | 81 | Set to CARIES mode. 82 | ```js 83 | $("#odontogram-canvas").odontogram('setMode', ODONTOGRAM_MODE_CARIES); 84 | ``` 85 | 86 | Set to CFR mode. 87 | ```js 88 | $("#odontogram-canvas").odontogram('setMode', ODONTOGRAM_MODE_CFR); 89 | ``` 90 | 91 | Set to FMC mode. 92 | ```js 93 | $("#odontogram-canvas").odontogram('setMode', ODONTOGRAM_MODE_FMC); 94 | ``` 95 | 96 | Set to POC mode. 97 | ```js 98 | $("#odontogram-canvas").odontogram('setMode', ODONTOGRAM_MODE_POC); 99 | ``` 100 | 101 | Set to RRX mode. 102 | ```js 103 | $("#odontogram-canvas").odontogram('setMode', ODONTOGRAM_MODE_RRX); 104 | ``` 105 | 106 | Set to MIS mode. 107 | ```js 108 | $("#odontogram-canvas").odontogram('setMode', ODONTOGRAM_MODE_MIS); 109 | ``` 110 | 111 | Set to IPX mode. 112 | ```js 113 | $("#odontogram-canvas").odontogram('setMode', ODONTOGRAM_MODE_IPX); 114 | ``` 115 | 116 | Set to FRM_ACR mode. 117 | ```js 118 | $("#odontogram-canvas").odontogram('setMode', ODONTOGRAM_MODE_FRM_ACR); 119 | ``` 120 | 121 | Set to BRIDGE mode. 122 | ```js 123 | $("#odontogram-canvas").odontogram('setMode', ODONTOGRAM_MODE_BRIDGE); 124 | ``` 125 | 126 | Set to ARROW_TOP_LEFT mode. 127 | ```js 128 | $("#odontogram-canvas").odontogram('setMode', ODONTOGRAM_MODE_ARROW_TOP_LEFT); 129 | ``` 130 | 131 | Set to ARROW_TOP_RIGHT mode. 132 | ```js 133 | $("#odontogram-canvas").odontogram('setMode', ODONTOGRAM_MODE_ARROW_TOP_RIGHT); 134 | ``` 135 | 136 | Set to ARROW_TOP_TURN_LEFT mode. 137 | ```js 138 | $("#odontogram-canvas").odontogram('setMode', ODONTOGRAM_MODE_ARROW_TOP_TURN_LEFT); 139 | ``` 140 | 141 | Set to ARROW_TOP_TURN_RIGHT mode. 142 | ```js 143 | $("#odontogram-canvas").odontogram('setMode', ODONTOGRAM_MODE_ARROW_TOP_TURN_RIGHT); 144 | ``` 145 | 146 | Set to ARROW_BOTTOM_LEFT mode. 147 | ```js 148 | $("#odontogram-canvas").odontogram('setMode', ODONTOGRAM_MODE_ARROW_BOTTOM_LEFT); 149 | ``` 150 | 151 | Set to ARROW_BOTTOM_RIGHT mode. 152 | ```js 153 | $("#odontogram-canvas").odontogram('setMode', ODONTOGRAM_MODE_ARROW_BOTTOM_RIGHT); 154 | ``` 155 | 156 | Set to ARROW_BOTTOM_TURN_LEFT mode. 157 | ```js 158 | $("#odontogram-canvas").odontogram('setMode', ODONTOGRAM_MODE_ARROW_BOTTOM_TURN_LEFT); 159 | ``` 160 | 161 | Set to ARROW_BOTTOM_TURN_RIGHT mode. 162 | ```js 163 | $("#odontogram-canvas").odontogram('setMode', ODONTOGRAM_MODE_ARROW_BOTTOM_TURN_RIGHT); 164 | ``` 165 | 166 | Event listener for get all the position with the procedure name (code) 167 | ```js 168 | $('#odontogram').on('change', function (_, geometry) { 169 | console.log(geometry) 170 | }) 171 | ``` 172 | 173 | Set data by Position and Procedure Name (code). 174 | ```js 175 | $("#odontogram-canvas").data('odontogram').setGeometryByPos([ 176 | { code: 'AMF', pos: '18-R' }, 177 | { code: 'AMF', pos: '18-L' }, 178 | { code: 'CARIES', pos: '83-M' }, 179 | { code: 'POC', pos: '84' }, 180 | ]); 181 | ``` 182 | or 183 | ```js 184 | var odontogram = $("#odontogram-canvas").odontogram('init', { 185 | width: "900px", 186 | height: "420px" 187 | }); 188 | odontogram.setGeometryByPos([ 189 | { code: 'AMF', pos: '18-R' }, 190 | { code: 'AMF', pos: '18-L' }, 191 | { code: 'CARIES', pos: '83-M' }, 192 | { code: 'POC', pos: '84' }, 193 | ]); 194 | ``` 195 | 196 | 197 | Contribute 198 | ---- 199 | If you like the project please support with your contribution. 200 | 201 | [Donate on Paypal](https://www.paypal.me/adhiana46) 202 | 203 | Thank you and Happy Coding :) -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Odontogram 6 | 7 | 8 | 13 | 14 | 15 | 16 | 17 | Browser anda tidak support canvas, silahkan update browser anda. 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /jquery.odontogram.js: -------------------------------------------------------------------------------- 1 | // CONSTANTA untuk MODE Odontogram 2 | var ODONTOGRAM_MODE_HAPUS = 100; // HAPUS 3 | var ODONTOGRAM_MODE_DEFAULT = 0; // Do Nothing 4 | var ODONTOGRAM_MODE_AMF = 1; // Hitam = TAMBALAN AMALGAM 5 | var ODONTOGRAM_MODE_COF = 2; // Hijau Diarsir = TAMBALAN COMPOSITE 6 | var ODONTOGRAM_MODE_FIS = 3; // UNGU = pit dan fissure sealant 7 | var ODONTOGRAM_MODE_NVT = 4; // SEGITIGA DIBAWAH (seperti Akar) = gigi non-vital 8 | var ODONTOGRAM_MODE_RCT = 5; // SEGITIGA DIBAWAH (seperti Akar) filled = Perawatan Saluran Akar 9 | var ODONTOGRAM_MODE_NON = 6; // gigi tidak ada, tidak diketahui ada atau tidak ada. (non) 10 | var ODONTOGRAM_MODE_UNE = 7; // Un-Erupted (une) 11 | var ODONTOGRAM_MODE_PRE = 8; // Partial-Erupt (pre) 12 | var ODONTOGRAM_MODE_ANO = 9; // Anomali (ano), Pegshaped, micro, fusi, etc 13 | var ODONTOGRAM_MODE_CARIES = 10; // Caries = Tambalan sementara (car) 14 | var ODONTOGRAM_MODE_CFR = 11; // fracture (cfr) (Tanda '#' di tengah" gigi) 15 | var ODONTOGRAM_MODE_FMC = 12; // Full metal crown pada gigi vital (fmc) 16 | var ODONTOGRAM_MODE_POC = 13; // Porcelain crown pada gigi vital (poc) 17 | var ODONTOGRAM_MODE_RRX = 14; // Sisa Akar (rrx) 18 | var ODONTOGRAM_MODE_MIS = 15; // Gigi hilang (mis) 19 | var ODONTOGRAM_MODE_IPX = 16; // Implant + Porcelain crown (ipx - poc) 20 | var ODONTOGRAM_MODE_FRM_ACR = 17; // Partial Denture/ Full Denture 21 | var ODONTOGRAM_MODE_BRIDGE = 18; // BRIDGE 22 | var ODONTOGRAM_MODE_ARROW_TOP_LEFT = 19; // TOP-LEFT ARROW 23 | var ODONTOGRAM_MODE_ARROW_TOP_RIGHT = 20; // TOP-RIGHT ARROW 24 | var ODONTOGRAM_MODE_ARROW_TOP_TURN_LEFT = 21; // TOP-TURN-LEFT ARROW 25 | var ODONTOGRAM_MODE_ARROW_TOP_TURN_RIGHT = 22; // TOP-TURN-RIGHT ARROW 26 | var ODONTOGRAM_MODE_ARROW_BOTTOM_LEFT = 23; // BOTTOM-LEFT ARROW 27 | var ODONTOGRAM_MODE_ARROW_BOTTOM_RIGHT = 24; // BOTTOM-RIGHT ARROW 28 | var ODONTOGRAM_MODE_ARROW_BOTTOM_TURN_LEFT = 25; // BOTTOM-TURN-LEFT ARROW 29 | var ODONTOGRAM_MODE_ARROW_BOTTOM_TURN_RIGHT = 26; // BOTTOM-TURN-RIGHT ARROW 30 | 31 | 32 | 33 | // Create closure. 34 | (function ($) { 35 | // Class Polygon 36 | function Polygon(vertices, options) { 37 | this.name = 'Polygon'; 38 | this.vertices = vertices; 39 | this.options = options; 40 | return this; 41 | } 42 | Polygon.prototype.render = function (ctx) { 43 | if (this.vertices.length <= 0) return; 44 | ctx.fillStyle = this.options.fillStyle; 45 | ctx.beginPath(); 46 | 47 | var vertices = this.vertices.concat([]); 48 | var fpos = vertices.shift(); 49 | ctx.moveTo(fpos.x, fpos.y); 50 | 51 | var pos; 52 | while (vertices.length > 0) { 53 | pos = vertices.shift(); 54 | if (pos) { 55 | ctx.lineTo(pos.x, pos.y); 56 | } 57 | } 58 | ctx.lineTo(fpos.x, fpos.y); 59 | ctx.closePath(); 60 | ctx.fill(); 61 | }; 62 | // Class AMF = Tambalan Amalgam 63 | function AMF(vertices, options) { 64 | this.name = 'AMF'; 65 | this.vertices = vertices; 66 | this.options = $.extend({ fillStyle: '#222' }, options); 67 | return this; 68 | } 69 | AMF.prototype.render = function (ctx) { 70 | ctx.fillStyle = this.options.fillStyle; 71 | ctx.beginPath(); 72 | 73 | var vertices = this.vertices.concat([]); 74 | var fpos = vertices.shift(); 75 | ctx.moveTo(fpos.x + 1, fpos.y + 1); 76 | 77 | var pos; 78 | while (vertices.length > 0) { 79 | pos = vertices.shift(); 80 | if (pos) { 81 | ctx.lineTo(pos.x + 1, pos.y + 1); 82 | } 83 | } 84 | ctx.lineTo(fpos.x + 1, fpos.y + 1); 85 | ctx.closePath(); 86 | ctx.fill(); 87 | } 88 | // Class COF = TAMBALAN COMPOSITE 89 | function COF(vertices, options) { 90 | this.name = 'COF'; 91 | this.vertices = vertices; 92 | this.options = $.extend({ fillStyle: '#29b522' }, options); 93 | return this; 94 | } 95 | COF.prototype.render = function (ctx) { 96 | ctx.fillStyle = this.options.fillStyle; 97 | ctx.beginPath(); 98 | 99 | var vertices = this.vertices.concat([]); 100 | var fpos = vertices.shift(); 101 | ctx.moveTo(fpos.x + 1, fpos.y + 1); 102 | 103 | var top_left, top_right, 104 | bottom_left, bottom_right; 105 | var xcount = [], ycount = []; 106 | 107 | var pos; 108 | var i = 0; 109 | while (vertices.length > 0) { 110 | pos = vertices.shift(); 111 | if (pos) { 112 | ctx.lineTo(pos.x + 1, pos.y + 1); 113 | } 114 | 115 | if (i == 0) { // TOP LEFT 116 | top_left = { x: pos.x, y: pos.y }; 117 | } else if (i == 1) { // TOP RIGHT 118 | top_right = { x: pos.x, y: pos.y }; 119 | } else if (i == 2) { // BOTTOM RIGHT 120 | bottom_right = { x: pos.x, y: pos.y }; 121 | } else if (i == 3) { // BOTTOM LEFT 122 | bottom_left = { x: pos.x, y: pos.y }; 123 | } 124 | 125 | if (xcount.indexOf(pos.x) == -1) { 126 | xcount.push(pos.x); 127 | } 128 | if (ycount.indexOf(pos.y) == -1) { 129 | ycount.push(pos.y); 130 | } 131 | 132 | i++; 133 | } 134 | ctx.lineTo(fpos.x + 1, fpos.y + 1); 135 | ctx.closePath(); 136 | ctx.fill(); 137 | // console.log("COUNT", xcount.length, ycount.length); 138 | // TODO: Mengarsir 139 | // ctx.beginPath(); 140 | // ctx.moveTo(xpos, ypos + bigBoxSize); 141 | // ctx.lineTo(xpos + smallBoxSize/2, ypos + bigBoxSize - smallBoxSize/2); 142 | // ctx.stroke(); 143 | } 144 | // Class FIS = pit dan fissure sealant 145 | function FIS(vertices, options) { 146 | this.name = 'FIS'; 147 | this.vertices = vertices; 148 | this.options = $.extend({ fillStyle: '#ed3bed' }, options); 149 | return this; 150 | } 151 | FIS.prototype.render = function (ctx) { 152 | ctx.fillStyle = this.options.fillStyle; 153 | ctx.beginPath(); 154 | 155 | var vertices = this.vertices.concat([]); 156 | var fpos = vertices.shift(); 157 | ctx.moveTo(fpos.x + 1, fpos.y + 1); 158 | 159 | var pos; 160 | while (vertices.length > 0) { 161 | pos = vertices.shift(); 162 | if (pos) { 163 | ctx.lineTo(pos.x + 1, pos.y + 1); 164 | } 165 | } 166 | ctx.lineTo(fpos.x + 1, fpos.y + 1); 167 | ctx.closePath(); 168 | ctx.fill(); 169 | } 170 | // Class NVT = SEGITIGA DIBAWAH (seperti Akar) = gigi non-vital 171 | function NVT(vertices, options) { 172 | this.name = 'NVT'; 173 | this.vertices = vertices; 174 | this.options = $.extend({ strokeStyle: '#333', height: 25 }, options); 175 | return this; 176 | } 177 | NVT.prototype.render = function (ctx) { 178 | var x1 = parseFloat(this.vertices[0].x) + 1; 179 | var x2 = parseFloat(this.vertices[1].x) + 1; 180 | var y1 = parseFloat(this.vertices[0].y) + 1; 181 | var y2 = parseFloat(this.vertices[1].y) + 1; 182 | var size = x2 - x1; 183 | var height = parseFloat(this.options.height); 184 | 185 | ctx.strokeStyle = this.options.strokeStyle; 186 | ctx.beginPath(); 187 | ctx.moveTo(x1 + size / 4, y2); 188 | ctx.lineTo(x1 + size / 2, y2 + height); 189 | ctx.lineTo(x2 - size / 4, y2); 190 | ctx.closePath(); 191 | ctx.stroke(); 192 | } 193 | // Class RCT = SEGITIGA DIBAWAH (seperti Akar) filled = Perawatan Saluran Akar 194 | function RCT(vertices, options) { 195 | this.name = 'RCT'; 196 | this.vertices = vertices; 197 | this.options = $.extend({ strokeStyle: '#333', fillStyle: '#333', height: 25 }, options); 198 | return this; 199 | } 200 | RCT.prototype.render = function (ctx) { 201 | var x1 = parseFloat(this.vertices[0].x) + 1; 202 | var x2 = parseFloat(this.vertices[1].x) + 1; 203 | var y1 = parseFloat(this.vertices[0].y) + 1; 204 | var y2 = parseFloat(this.vertices[1].y) + 1; 205 | var size = x2 - x1; 206 | var height = parseFloat(this.options.height); 207 | 208 | ctx.strokeStyle = this.options.strokeStyle; 209 | ctx.fillStyle = this.options.fillStyle; 210 | ctx.beginPath(); 211 | ctx.moveTo(x1 + size / 4, y2); 212 | ctx.lineTo(x1 + size / 2, y2 + height); 213 | ctx.lineTo(x2 - size / 4, y2); 214 | ctx.closePath(); 215 | ctx.fill(); 216 | ctx.stroke(); 217 | } 218 | // Class NON = gigi tidak ada, tidak diketahui ada atau tidak ada. (non) 219 | function NON(vertices, options) { 220 | this.name = 'NON'; 221 | this.vertices = vertices; 222 | this.options = $.extend({ fillStyle: '#555', fontsize: 12 }, options); 223 | return this; 224 | } 225 | NON.prototype.render = function (ctx) { 226 | var x = parseFloat(this.vertices[0].x); 227 | var y = parseFloat(this.vertices[0].y); 228 | var fontsize = parseInt(this.options.fontsize); 229 | 230 | ctx.fillStyle = '#000'; 231 | ctx.font = "bold " + fontsize + "px Algerian"; 232 | ctx.textBaseline = "bottom"; 233 | ctx.textAlign = "left"; 234 | ctx.fillText(' NON', x, y); 235 | } 236 | // Class UNE = Un-Erupted (une) 237 | function UNE(vertices, options) { 238 | this.name = 'UNE'; 239 | this.vertices = vertices; 240 | this.options = $.extend({ fillStyle: '#555', fontsize: 12 }, options); 241 | return this; 242 | } 243 | UNE.prototype.render = function (ctx) { 244 | var x = parseFloat(this.vertices[0].x); 245 | var y = parseFloat(this.vertices[0].y); 246 | var fontsize = parseInt(this.options.fontsize); 247 | 248 | ctx.fillStyle = '#000'; 249 | ctx.font = "bold " + fontsize + "px Algerian"; 250 | ctx.textBaseline = "bottom"; 251 | ctx.textAlign = "left"; 252 | ctx.fillText(' UNE', x, y); 253 | } 254 | // Class PRE = Partial-Erupt (pre) 255 | function PRE(vertices, options) { 256 | this.name = 'PRE'; 257 | this.vertices = vertices; 258 | this.options = $.extend({ fillStyle: '#555', fontsize: 12 }, options); 259 | return this; 260 | } 261 | PRE.prototype.render = function (ctx) { 262 | var x = parseFloat(this.vertices[0].x); 263 | var y = parseFloat(this.vertices[0].y); 264 | var fontsize = parseInt(this.options.fontsize); 265 | 266 | ctx.fillStyle = '#000'; 267 | ctx.font = "bold " + fontsize + "px Algerian"; 268 | ctx.textBaseline = "bottom"; 269 | ctx.textAlign = "left"; 270 | ctx.fillText(' PRE', x, y); 271 | } 272 | // Class ANO = Anomali (ano), Pegshaped, micro, fusi, etc 273 | function ANO(vertices, options) { 274 | this.name = 'ANO'; 275 | this.vertices = vertices; 276 | this.options = $.extend({ fillStyle: '#555', fontsize: 12 }, options); 277 | return this; 278 | } 279 | ANO.prototype.render = function (ctx) { 280 | var x = parseFloat(this.vertices[0].x); 281 | var y = parseFloat(this.vertices[0].y); 282 | var fontsize = parseInt(this.options.fontsize); 283 | 284 | ctx.fillStyle = '#000'; 285 | ctx.font = "bold " + fontsize + "px Algerian"; 286 | ctx.textBaseline = "bottom"; 287 | ctx.textAlign = "left"; 288 | ctx.fillText(' ANO', x, y); 289 | } 290 | // Class CARIES = Caries = Tambalan sementara (car) 291 | function CARIES(vertices, options) { 292 | this.name = 'CARIES'; 293 | this.vertices = vertices; 294 | this.options = $.extend({ strokeStyle: '#333' }, options); 295 | return this; 296 | } 297 | CARIES.prototype.render = function (ctx) { 298 | ctx.strokeStyle = this.options.strokeStyle; 299 | ctx.lineWidth = 4; 300 | ctx.beginPath(); 301 | 302 | var vertices = this.vertices.concat([]); 303 | var fpos = vertices.shift(); 304 | ctx.moveTo(fpos.x, fpos.y); 305 | 306 | var pos; 307 | while (vertices.length > 0) { 308 | pos = vertices.shift(); 309 | if (pos) { 310 | ctx.lineTo(pos.x, pos.y); 311 | } 312 | } 313 | ctx.lineTo(fpos.x, fpos.y); 314 | ctx.closePath(); 315 | ctx.stroke(); 316 | } 317 | // Class CFR = fracture (cfr) (Tanda '#' di tengah" gigi) 318 | function CFR(vertices, options) { 319 | this.name = 'CFR'; 320 | this.vertices = vertices; 321 | this.options = $.extend({ fillStyle: '#555' }, options); 322 | return this; 323 | } 324 | CFR.prototype.render = function (ctx) { 325 | var x1 = parseFloat(this.vertices[0].x); 326 | var y1 = parseFloat(this.vertices[0].y); 327 | var x2 = parseFloat(this.vertices[1].x); 328 | var y2 = parseFloat(this.vertices[1].y); 329 | var boxSize = x2 - x1; 330 | var fontsize = parseInt(boxSize); 331 | 332 | var x = x1 + boxSize / 2; 333 | var y = y1 + boxSize / 2; 334 | 335 | ctx.fillStyle = '#000'; 336 | ctx.font = "bold " + fontsize + "px Algerian"; 337 | ctx.textBaseline = "middle"; 338 | ctx.textAlign = "center"; 339 | ctx.fillText('#', x, y); 340 | } 341 | // Class FMC = Full metal crown pada gigi vital (fmc) 342 | function FMC(vertices, options) { 343 | this.name = 'FMC'; 344 | this.vertices = vertices; 345 | this.options = $.extend({ strokeStyle: '#333' }, options); 346 | return this; 347 | } 348 | FMC.prototype.render = function (ctx) { 349 | var x1 = parseFloat(this.vertices[0].x) + 1; 350 | var y1 = parseFloat(this.vertices[0].y) + 1; 351 | var x2 = parseFloat(this.vertices[1].x) + 1; 352 | var y2 = parseFloat(this.vertices[1].y) + 1; 353 | var vertices = [ 354 | { x: x1, y: y1 }, 355 | { x: x2, y: y1 }, 356 | { x: x2, y: y2 }, 357 | { x: x1, y: y2 } 358 | ]; 359 | 360 | ctx.strokeStyle = this.options.strokeStyle; 361 | ctx.lineWidth = 6; 362 | ctx.beginPath(); 363 | 364 | var fpos = vertices.shift(); 365 | ctx.moveTo(fpos.x, fpos.y); 366 | 367 | var pos; 368 | while (vertices.length > 0) { 369 | pos = vertices.shift(); 370 | if (pos) { 371 | ctx.lineTo(pos.x, pos.y); 372 | } 373 | } 374 | ctx.lineTo(fpos.x, fpos.y); 375 | ctx.closePath(); 376 | ctx.stroke(); 377 | } 378 | // Class POC = Porcelain crown pada gigi vital (poc) 379 | function POC(vertices, options) { 380 | this.name = 'POC'; 381 | this.vertices = vertices; 382 | this.options = $.extend({ strokeStyle: '#333' }, options); 383 | return this; 384 | } 385 | POC.prototype.render = function (ctx) { 386 | var x1 = parseFloat(this.vertices[0].x) + 1; 387 | var y1 = parseFloat(this.vertices[0].y) + 1; 388 | var x2 = parseFloat(this.vertices[1].x) + 1; 389 | var y2 = parseFloat(this.vertices[1].y) + 1; 390 | var vertices = [ 391 | { x: x1, y: y1 }, 392 | { x: x2, y: y1 }, 393 | { x: x2, y: y2 }, 394 | { x: x1, y: y2 } 395 | ]; 396 | 397 | ctx.strokeStyle = this.options.strokeStyle; 398 | ctx.lineWidth = 6; 399 | ctx.beginPath(); 400 | 401 | var fpos = vertices.shift(); 402 | ctx.moveTo(fpos.x, fpos.y); 403 | 404 | var pos; 405 | while (vertices.length > 0) { 406 | pos = vertices.shift(); 407 | if (pos) { 408 | ctx.lineTo(pos.x, pos.y); 409 | } 410 | } 411 | ctx.lineTo(fpos.x, fpos.y); 412 | ctx.closePath(); 413 | ctx.stroke(); 414 | 415 | // Draw Lines 416 | ctx.lineWidth = 1; 417 | for (var xpos = x1; xpos < x2; xpos += ((x2 - x1) / 15)) { 418 | xpos = Math.min(xpos, x2); 419 | 420 | ctx.beginPath(); 421 | ctx.moveTo(xpos, y1); 422 | ctx.lineTo(xpos, y2); 423 | ctx.stroke(); 424 | } 425 | } 426 | // Class RRX = Sisa Akar (rrx) 427 | function RRX(vertices, options) { 428 | this.name = 'RRX'; 429 | this.vertices = vertices; 430 | this.options = $.extend({ strokeStyle: '#333' }, options); 431 | return this; 432 | } 433 | RRX.prototype.render = function (ctx) { 434 | var x1 = parseFloat(this.vertices[0].x) + 1; 435 | var y1 = parseFloat(this.vertices[0].y) + 1; 436 | var x2 = parseFloat(this.vertices[1].x) + 1; 437 | var y2 = parseFloat(this.vertices[1].y) + 1; 438 | var bigBoxSize = x2 - x1; 439 | var smallBoxSize = bigBoxSize / 2; 440 | var lines = [ 441 | { 442 | x1: x1 + smallBoxSize / 3, y1: y1 - smallBoxSize / 2, 443 | x2: x1 + smallBoxSize, y2: y2 + smallBoxSize / 2 444 | }, 445 | { 446 | x1: x1 + smallBoxSize, y1: y2 + smallBoxSize / 2, 447 | x2: x1 + smallBoxSize * 2, y2: y1 - smallBoxSize 448 | } 449 | ]; 450 | 451 | ctx.strokeStyle = this.options.strokeStyle; 452 | ctx.lineWidth = 4; 453 | var line; 454 | for (var i = 0; i < lines.length; i++) { 455 | line = lines[i]; 456 | ctx.beginPath(); 457 | ctx.moveTo(line.x1, line.y1); 458 | ctx.lineTo(line.x2, line.y2); 459 | ctx.stroke(); 460 | } 461 | } 462 | // Class MIS = Gigi hilang (mis) 463 | function MIS(vertices, options) { 464 | this.name = 'MIS'; 465 | this.vertices = vertices; 466 | this.options = $.extend({ strokeStyle: '#333' }, options); 467 | return this; 468 | } 469 | MIS.prototype.render = function (ctx) { 470 | var x1 = parseFloat(this.vertices[0].x) + 1; 471 | var y1 = parseFloat(this.vertices[0].y) + 1; 472 | var x2 = parseFloat(this.vertices[1].x) + 1; 473 | var y2 = parseFloat(this.vertices[1].y) + 1; 474 | var bigBoxSize = x2 - x1; 475 | var smallBoxSize = bigBoxSize / 2; 476 | var lines = [ 477 | { 478 | x1: x1 + smallBoxSize * .5, y1: y1 - smallBoxSize / 2, 479 | x2: x1 + smallBoxSize * 1.5, y2: y2 + smallBoxSize / 2 480 | }, 481 | { 482 | x1: x1 + smallBoxSize * 1.5, y1: y1 - smallBoxSize / 2, 483 | x2: x1 + smallBoxSize * .5, y2: y2 + smallBoxSize / 2 484 | } 485 | ]; 486 | 487 | ctx.strokeStyle = this.options.strokeStyle; 488 | ctx.lineWidth = 4; 489 | var line; 490 | for (var i = 0; i < lines.length; i++) { 491 | line = lines[i]; 492 | ctx.beginPath(); 493 | ctx.moveTo(line.x1, line.y1); 494 | ctx.lineTo(line.x2, line.y2); 495 | ctx.stroke(); 496 | } 497 | } 498 | // Class IPX = Implant + Porcelain crown (ipx - poc) 499 | function IPX(vertices, options) { 500 | this.name = 'IPX'; 501 | this.vertices = vertices; 502 | this.options = $.extend({ fillStyle: '#555', fontsize: 12 }, options); 503 | return this; 504 | } 505 | IPX.prototype.render = function (ctx) { 506 | var x = parseFloat(this.vertices[0].x); 507 | var y = parseFloat(this.vertices[1].y); 508 | var fontsize = parseInt(this.options.fontsize); 509 | 510 | ctx.fillStyle = '#000'; 511 | ctx.font = "bold " + fontsize + "px Algerian"; 512 | ctx.textBaseline = "top"; 513 | ctx.textAlign = "left"; 514 | ctx.fillText(' IPX', x, y); 515 | } 516 | // Class FRM_ACR = Partial Denture/ Full Denture 517 | function FRM_ACR(vertices, options) { 518 | this.name = 'FRM_ACR'; 519 | this.vertices = vertices; 520 | this.options = $.extend({ fillStyle: '#555', fontsize: 12 }, options); 521 | return this; 522 | } 523 | FRM_ACR.prototype.render = function (ctx) { 524 | var x = parseFloat(this.vertices[0].x) + (parseFloat(this.vertices[1].x) - parseFloat(this.vertices[0].x)) / 2; 525 | var y = parseFloat(this.vertices[1].y) + (parseFloat(this.vertices[1].x) - parseFloat(this.vertices[0].x)) / 2 - (parseFloat(this.vertices[1].x) - parseFloat(this.vertices[0].x)) / 8; 526 | var fontsize = parseInt(this.options.fontsize); 527 | 528 | ctx.fillStyle = '#000'; 529 | ctx.font = "bold " + fontsize + "px Algerian"; 530 | ctx.textBaseline = "top"; 531 | ctx.textAlign = "center"; 532 | ctx.fillText(' PRD/FLD', x, y); 533 | } 534 | // Class Bridge = BRIDGE 535 | function BRIDGE(startVert, endVert, options) { 536 | this.name = 'BRIDGE'; 537 | this.startVert = startVert; 538 | this.endVert = endVert; 539 | this.options = $.extend({ strokeStyle: '#555' }, options); 540 | return this; 541 | } 542 | BRIDGE.prototype.render = function (ctx) { 543 | var vert0, vert1; 544 | if (this.startVert) { 545 | vert0 = { 546 | x1: parseFloat(this.startVert[0].x), 547 | y1: parseFloat(this.startVert[0].y), 548 | x2: parseFloat(this.startVert[1].x), 549 | y2: parseFloat(this.startVert[1].y), 550 | }; 551 | vert0.size = vert0.x2 - vert0.x1; 552 | vert0.cx = vert0.x2 - vert0.size / 2; 553 | vert0.cy = vert0.y2 - vert0.size / 2; 554 | } 555 | 556 | if (this.endVert) { 557 | vert1 = { 558 | x1: parseFloat(this.endVert[0].x), 559 | y1: parseFloat(this.endVert[0].y), 560 | x2: parseFloat(this.endVert[1].x), 561 | y2: parseFloat(this.endVert[1].y) 562 | }; 563 | vert1.size = vert1.x2 - vert1.x1; 564 | vert1.cx = vert1.x2 - vert1.size / 2; 565 | vert1.cy = vert1.y2 - vert1.size / 2; 566 | } 567 | 568 | // Draw Bridge in vert0 | 569 | if (vert0) { 570 | ctx.strokeStyle = this.options.strokeStyle; 571 | ctx.lineWidth = 6; 572 | ctx.beginPath(); 573 | ctx.moveTo(vert0.cx, vert0.y1); 574 | ctx.lineTo(vert0.cx, vert0.y1 - vert0.size / 4); 575 | ctx.stroke(); 576 | } 577 | 578 | // Draw Bridge in vert1 | 579 | if (vert1) { 580 | ctx.strokeStyle = this.options.strokeStyle; 581 | ctx.lineWidth = 6; 582 | ctx.beginPath(); 583 | ctx.moveTo(vert1.cx, vert1.y1); 584 | ctx.lineTo(vert1.cx, vert1.y1 - vert1.size / 4); 585 | ctx.stroke(); 586 | } 587 | 588 | // JOIN BRIDGE 589 | if (vert0 && vert1) { 590 | var lor = (vert1.cx - vert0.cx) > 0 ? 1 : -1 591 | ctx.strokeStyle = this.options.strokeStyle; 592 | ctx.lineWidth = 6; 593 | ctx.beginPath(); 594 | ctx.moveTo(vert0.cx - 3 * lor, vert0.y1 - vert0.size / 4); 595 | ctx.lineTo(vert1.cx + 3 * lor, vert1.y1 - vert1.size / 4); 596 | ctx.stroke(); 597 | } 598 | } 599 | // Class HAPUS 600 | function HAPUS(vertices, options) { 601 | this.name = 'HAPUS'; 602 | this.vertices = vertices; 603 | this.options = $.extend({ fillStyle: 'rgba(200, 200, 200, 0.8)' }, options); 604 | return this; 605 | } 606 | HAPUS.prototype.render = function (ctx) { 607 | var x1 = parseFloat(this.vertices[0].x) + 1; 608 | var y1 = parseFloat(this.vertices[0].y) + 1; 609 | var x2 = parseFloat(this.vertices[1].x) + 1; 610 | var y2 = parseFloat(this.vertices[1].y) + 1; 611 | var x = x1; 612 | var y = y1; 613 | var size = x2 - x1; 614 | 615 | ctx.beginPath(); 616 | ctx.fillStyle = this.options.fillStyle; 617 | ctx.rect(x, y, size, size); 618 | ctx.fill(); 619 | } 620 | // ARROWS 621 | // Class ARROW_TOP_LEFT 622 | function ARROW_TOP_LEFT(vertices, options) { 623 | this.name = 'ARROW_TOP_LEFT'; 624 | this.vertices = vertices; 625 | this.options = $.extend({}, options); 626 | return this; 627 | } 628 | ARROW_TOP_LEFT.prototype.render = function (ctx) { 629 | var x1 = parseFloat(this.vertices[0].x); 630 | var y1 = parseFloat(this.vertices[0].y); 631 | var x2 = parseFloat(this.vertices[1].x); 632 | var y2 = parseFloat(this.vertices[1].y); 633 | 634 | var fromx, fromy, tox, toy; 635 | fromx = x2 - (x2 - x1) / 4; 636 | fromy = y1 - 10; 637 | tox = fromx - 25; 638 | toy = fromy; 639 | 640 | // fromx = 50; 641 | // fromy = 20; 642 | // tox = 25; 643 | // toy = 20; 644 | 645 | var headlen = 10; 646 | var lineWidth = 2; 647 | 648 | var angle = Math.atan2(toy - fromy, tox - fromx); 649 | 650 | ctx.beginPath(); 651 | ctx.moveTo(fromx, fromy); 652 | ctx.lineTo(tox, toy); 653 | ctx.strokeStyle = "#000000"; 654 | ctx.lineWidth = lineWidth; 655 | ctx.stroke(); 656 | 657 | //starting a new path from the head of the arrow to one of the sides of the point 658 | ctx.beginPath(); 659 | ctx.moveTo(tox, toy); 660 | ctx.lineTo(tox - headlen * Math.cos(angle - Math.PI / 7), toy - headlen * Math.sin(angle - Math.PI / 7)); 661 | 662 | //path from the side point of the arrow, to the other side point 663 | ctx.lineTo(tox - headlen * Math.cos(angle + Math.PI / 7), toy - headlen * Math.sin(angle + Math.PI / 7)); 664 | 665 | //path from the side point back to the tip of the arrow, and then again to the opposite side point 666 | ctx.lineTo(tox, toy); 667 | ctx.lineTo(tox - headlen * Math.cos(angle - Math.PI / 7), toy - headlen * Math.sin(angle - Math.PI / 7)); 668 | 669 | //draws the paths created above 670 | ctx.strokeStyle = "#000000"; 671 | ctx.fillStyle = "#000000"; 672 | ctx.lineWidth = lineWidth; 673 | ctx.stroke(); 674 | ctx.fill(); 675 | } 676 | 677 | // Class ARROW_TOP_RIGHT 678 | function ARROW_TOP_RIGHT(vertices, options) { 679 | this.name = 'ARROW_TOP_RIGHT'; 680 | this.vertices = vertices; 681 | this.options = $.extend({}, options); 682 | return this; 683 | } 684 | ARROW_TOP_RIGHT.prototype.render = function (ctx) { 685 | var x1 = parseFloat(this.vertices[0].x); 686 | var y1 = parseFloat(this.vertices[0].y); 687 | var x2 = parseFloat(this.vertices[1].x); 688 | var y2 = parseFloat(this.vertices[1].y); 689 | 690 | var fromx, fromy, tox, toy; 691 | fromx = x1 + (x2 - x1) / 4; 692 | fromy = y1 - 10; 693 | tox = fromx + 25; 694 | toy = fromy; 695 | 696 | // fromx = 60; 697 | // fromy = 20; 698 | // tox = 90; 699 | // toy = 20; 700 | 701 | var headlen = 10; 702 | var lineWidth = 2; 703 | 704 | var angle = Math.atan2(toy - fromy, tox - fromx); 705 | 706 | ctx.beginPath(); 707 | ctx.moveTo(fromx, fromy); 708 | ctx.lineTo(tox, toy); 709 | ctx.strokeStyle = "#000000"; 710 | ctx.lineWidth = lineWidth; 711 | ctx.stroke(); 712 | 713 | //starting a new path from the head of the arrow to one of the sides of the point 714 | ctx.beginPath(); 715 | ctx.moveTo(tox, toy); 716 | ctx.lineTo(tox - headlen * Math.cos(angle - Math.PI / 7), toy - headlen * Math.sin(angle - Math.PI / 7)); 717 | 718 | //path from the side point of the arrow, to the other side point 719 | ctx.lineTo(tox - headlen * Math.cos(angle + Math.PI / 7), toy - headlen * Math.sin(angle + Math.PI / 7)); 720 | 721 | //path from the side point back to the tip of the arrow, and then again to the opposite side point 722 | ctx.lineTo(tox, toy); 723 | ctx.lineTo(tox - headlen * Math.cos(angle - Math.PI / 7), toy - headlen * Math.sin(angle - Math.PI / 7)); 724 | 725 | //draws the paths created above 726 | ctx.strokeStyle = "#000000"; 727 | ctx.fillStyle = "#000000"; 728 | ctx.lineWidth = lineWidth; 729 | ctx.stroke(); 730 | ctx.fill(); 731 | } 732 | 733 | // Class ARROW_TOP_TURN_LEFT 734 | function ARROW_TOP_TURN_LEFT(vertices, options) { 735 | this.name = 'ARROW_TOP_TURN_LEFT'; 736 | this.vertices = vertices; 737 | this.options = $.extend({}, options); 738 | return this; 739 | } 740 | ARROW_TOP_TURN_LEFT.prototype.render = function (ctx) { 741 | var x1 = parseFloat(this.vertices[0].x); 742 | var y1 = parseFloat(this.vertices[0].y); 743 | var x2 = parseFloat(this.vertices[1].x); 744 | var y2 = parseFloat(this.vertices[1].y); 745 | 746 | var fromx, fromy, tox, toy; 747 | fromx = x1 + (x2 - x1) / 4; 748 | fromy = y1 - 10; 749 | tox = fromx + 10; 750 | toy = fromy; 751 | 752 | // fromx = 180; 753 | // fromy = 20; 754 | // tox = 190; 755 | // toy = 20; 756 | 757 | var headlen = 10; 758 | var lineWidth = 2; 759 | 760 | ctx.strokeStyle = "#000000"; 761 | ctx.fillStyle = "#000000"; 762 | ctx.lineWidth = lineWidth; 763 | 764 | ctx.beginPath(); 765 | ctx.arc(tox, fromy, 5, 1.5 * Math.PI, 0.5 * Math.PI); 766 | ctx.stroke(); 767 | 768 | ctx.beginPath(); 769 | ctx.moveTo(tox, fromy - 5); 770 | ctx.lineTo(fromx, fromy - 5); 771 | ctx.stroke(); 772 | 773 | var angle = Math.atan2(toy - fromy, tox - fromx); 774 | 775 | toy = toy - 5; 776 | fromx = fromx - 5; 777 | ctx.beginPath(); 778 | ctx.moveTo(fromx, toy); 779 | ctx.lineTo(fromx + headlen * Math.cos(angle + Math.PI / 7), toy + headlen * Math.sin(angle + Math.PI / 7)); 780 | ctx.lineTo(fromx + headlen * Math.cos(angle - Math.PI / 7), toy + headlen * Math.sin(angle - Math.PI / 7)); 781 | ctx.lineTo(fromx, toy); 782 | ctx.lineTo(fromx + headlen * Math.cos(angle + Math.PI / 7), toy + headlen * Math.sin(angle - Math.PI / 7)); 783 | ctx.stroke(); 784 | ctx.fill(); 785 | } 786 | 787 | // Class ARROW_TOP_TURN_RIGHT 788 | function ARROW_TOP_TURN_RIGHT(vertices, options) { 789 | this.name = 'ARROW_TOP_TURN_RIGHT'; 790 | this.vertices = vertices; 791 | this.options = $.extend({}, options); 792 | return this; 793 | } 794 | ARROW_TOP_TURN_RIGHT.prototype.render = function (ctx) { 795 | var x1 = parseFloat(this.vertices[0].x); 796 | var y1 = parseFloat(this.vertices[0].y); 797 | var x2 = parseFloat(this.vertices[1].x); 798 | var y2 = parseFloat(this.vertices[1].y); 799 | 800 | var fromx, fromy, tox, toy; 801 | fromx = x2 - (x2 - x1) / 2; 802 | fromy = y1 - 10; 803 | tox = fromx + 10; 804 | toy = fromy; 805 | 806 | var headlen = 10; 807 | var lineWidth = 2; 808 | 809 | ctx.strokeStyle = "#000000"; 810 | ctx.fillStyle = "#000000"; 811 | ctx.lineWidth = lineWidth; 812 | 813 | ctx.beginPath(); 814 | ctx.arc(fromx, fromy, 5, 0.38 * Math.PI, 1.5 * Math.PI); 815 | ctx.stroke(); 816 | 817 | ctx.beginPath(); 818 | ctx.moveTo(fromx, fromy - 5); 819 | ctx.lineTo(tox, fromy - 5); 820 | ctx.stroke(); 821 | 822 | var angle = Math.atan2(toy - fromy, tox - fromx); 823 | 824 | toy = toy - 5; 825 | tox = tox + 5; 826 | ctx.beginPath(); 827 | ctx.moveTo(tox, toy); 828 | ctx.lineTo(tox - headlen * Math.cos(angle - Math.PI / 7), toy - headlen * Math.sin(angle - Math.PI / 7)); 829 | ctx.lineTo(tox - headlen * Math.cos(angle + Math.PI / 7), toy - headlen * Math.sin(angle + Math.PI / 7)); 830 | ctx.lineTo(tox, toy); 831 | ctx.lineTo(tox - headlen * Math.cos(angle - Math.PI / 7), toy - headlen * Math.sin(angle - Math.PI / 7)); 832 | ctx.stroke(); 833 | ctx.fill(); 834 | } 835 | 836 | // Class ARROW_BOTTOM_LEFT 837 | function ARROW_BOTTOM_LEFT(vertices, options) { 838 | this.name = 'ARROW_BOTTOM_LEFT'; 839 | this.vertices = vertices; 840 | this.options = $.extend({}, options); 841 | return this; 842 | } 843 | ARROW_BOTTOM_LEFT.prototype.render = function (ctx) { 844 | var x1 = parseFloat(this.vertices[0].x); 845 | var y1 = parseFloat(this.vertices[0].y); 846 | var x2 = parseFloat(this.vertices[1].x); 847 | var y2 = parseFloat(this.vertices[1].y); 848 | 849 | var fromx, fromy, tox, toy; 850 | fromx = x2 - (x2 - x1) / 4; 851 | fromy = y2 + 10; 852 | tox = fromx - 25; 853 | toy = fromy; 854 | 855 | // fromx = 50; 856 | // fromy = 20; 857 | // tox = 25; 858 | // toy = 20; 859 | 860 | var headlen = 10; 861 | var lineWidth = 2; 862 | 863 | var angle = Math.atan2(toy - fromy, tox - fromx); 864 | 865 | ctx.beginPath(); 866 | ctx.moveTo(fromx, fromy); 867 | ctx.lineTo(tox, toy); 868 | ctx.strokeStyle = "#000000"; 869 | ctx.lineWidth = lineWidth; 870 | ctx.stroke(); 871 | 872 | //starting a new path from the head of the arrow to one of the sides of the point 873 | ctx.beginPath(); 874 | ctx.moveTo(tox, toy); 875 | ctx.lineTo(tox - headlen * Math.cos(angle - Math.PI / 7), toy - headlen * Math.sin(angle - Math.PI / 7)); 876 | 877 | //path from the side point of the arrow, to the other side point 878 | ctx.lineTo(tox - headlen * Math.cos(angle + Math.PI / 7), toy - headlen * Math.sin(angle + Math.PI / 7)); 879 | 880 | //path from the side point back to the tip of the arrow, and then again to the opposite side point 881 | ctx.lineTo(tox, toy); 882 | ctx.lineTo(tox - headlen * Math.cos(angle - Math.PI / 7), toy - headlen * Math.sin(angle - Math.PI / 7)); 883 | 884 | //draws the paths created above 885 | ctx.strokeStyle = "#000000"; 886 | ctx.fillStyle = "#000000"; 887 | ctx.lineWidth = lineWidth; 888 | ctx.stroke(); 889 | ctx.fill(); 890 | } 891 | 892 | // Class ARROW_BOTTOM_RIGHT 893 | function ARROW_BOTTOM_RIGHT(vertices, options) { 894 | this.name = 'ARROW_BOTTOM_RIGHT'; 895 | this.vertices = vertices; 896 | this.options = $.extend({}, options); 897 | return this; 898 | } 899 | ARROW_BOTTOM_RIGHT.prototype.render = function (ctx) { 900 | var x1 = parseFloat(this.vertices[0].x); 901 | var y1 = parseFloat(this.vertices[0].y); 902 | var x2 = parseFloat(this.vertices[1].x); 903 | var y2 = parseFloat(this.vertices[1].y); 904 | 905 | var fromx, fromy, tox, toy; 906 | fromx = x1 + (x2 - x1) / 4; 907 | fromy = y2 + 10; 908 | tox = fromx + 25; 909 | toy = fromy; 910 | 911 | // fromx = 60; 912 | // fromy = 20; 913 | // tox = 90; 914 | // toy = 20; 915 | 916 | var headlen = 10; 917 | var lineWidth = 2; 918 | 919 | var angle = Math.atan2(toy - fromy, tox - fromx); 920 | 921 | ctx.beginPath(); 922 | ctx.moveTo(fromx, fromy); 923 | ctx.lineTo(tox, toy); 924 | ctx.strokeStyle = "#000000"; 925 | ctx.lineWidth = lineWidth; 926 | ctx.stroke(); 927 | 928 | //starting a new path from the head of the arrow to one of the sides of the point 929 | ctx.beginPath(); 930 | ctx.moveTo(tox, toy); 931 | ctx.lineTo(tox - headlen * Math.cos(angle - Math.PI / 7), toy - headlen * Math.sin(angle - Math.PI / 7)); 932 | 933 | //path from the side point of the arrow, to the other side point 934 | ctx.lineTo(tox - headlen * Math.cos(angle + Math.PI / 7), toy - headlen * Math.sin(angle + Math.PI / 7)); 935 | 936 | //path from the side point back to the tip of the arrow, and then again to the opposite side point 937 | ctx.lineTo(tox, toy); 938 | ctx.lineTo(tox - headlen * Math.cos(angle - Math.PI / 7), toy - headlen * Math.sin(angle - Math.PI / 7)); 939 | 940 | //draws the paths created above 941 | ctx.strokeStyle = "#000000"; 942 | ctx.fillStyle = "#000000"; 943 | ctx.lineWidth = lineWidth; 944 | ctx.stroke(); 945 | ctx.fill(); 946 | } 947 | 948 | // Class ARROW_BOTTOM_TURN_LEFT 949 | function ARROW_BOTTOM_TURN_LEFT(vertices, options) { 950 | this.name = 'ARROW_BOTTOM_TURN_LEFT'; 951 | this.vertices = vertices; 952 | this.options = $.extend({}, options); 953 | return this; 954 | } 955 | ARROW_BOTTOM_TURN_LEFT.prototype.render = function (ctx) { 956 | var x1 = parseFloat(this.vertices[0].x); 957 | var y1 = parseFloat(this.vertices[0].y); 958 | var x2 = parseFloat(this.vertices[1].x); 959 | var y2 = parseFloat(this.vertices[1].y); 960 | 961 | var fromx, fromy, tox, toy; 962 | fromx = x2 - (x2 - x1) / 2; 963 | fromy = y2 + 10; 964 | tox = fromx - 10; 965 | toy = fromy + 5; 966 | 967 | var headlen = 10; 968 | var lineWidth = 2; 969 | 970 | ctx.strokeStyle = "#000000"; 971 | ctx.fillStyle = "#000000"; 972 | ctx.lineWidth = lineWidth; 973 | 974 | ctx.beginPath(); 975 | ctx.arc(fromx, fromy, 5, 1.5 * Math.PI, 0.5 * Math.PI); 976 | ctx.stroke(); 977 | 978 | ctx.beginPath(); 979 | ctx.moveTo(tox, toy); 980 | ctx.lineTo(fromx, toy); 981 | ctx.stroke(); 982 | 983 | var angle = Math.atan2(toy - fromy, tox); 984 | 985 | toy = toy; 986 | tox = tox - 5; 987 | ctx.beginPath(); 988 | ctx.moveTo(tox, toy); 989 | ctx.lineTo(tox + headlen * Math.cos(angle + Math.PI / 7), toy - headlen * Math.sin(angle + Math.PI / 7)); 990 | ctx.lineTo(tox + headlen * Math.cos(angle + Math.PI / 7), toy - headlen * Math.sin(angle - Math.PI / 7)); 991 | ctx.lineTo(tox, toy); 992 | ctx.lineTo(tox + headlen * Math.cos(angle + Math.PI / 7), toy - headlen * Math.sin(angle + Math.PI / 7)); 993 | ctx.stroke(); 994 | ctx.fill(); 995 | } 996 | 997 | // Class ARROW_BOTTOM_TURN_RIGHT 998 | function ARROW_BOTTOM_TURN_RIGHT(vertices, options) { 999 | this.name = 'ARROW_BOTTOM_TURN_RIGHT'; 1000 | this.vertices = vertices; 1001 | this.options = $.extend({}, options); 1002 | return this; 1003 | } 1004 | ARROW_BOTTOM_TURN_RIGHT.prototype.render = function (ctx) { 1005 | var x1 = parseFloat(this.vertices[0].x); 1006 | var y1 = parseFloat(this.vertices[0].y); 1007 | var x2 = parseFloat(this.vertices[1].x); 1008 | var y2 = parseFloat(this.vertices[1].y); 1009 | 1010 | var fromx, fromy, tox, toy; 1011 | fromx = x2 - (x2 - x1) / 2; 1012 | fromy = y2 + 10; 1013 | tox = fromx + 10; 1014 | toy = fromy + 5; 1015 | 1016 | var headlen = 10; 1017 | var lineWidth = 2; 1018 | 1019 | ctx.strokeStyle = "#000000"; 1020 | ctx.fillStyle = "#000000"; 1021 | ctx.lineWidth = lineWidth; 1022 | 1023 | ctx.beginPath(); 1024 | ctx.arc(fromx, fromy, 5, 0.38 * Math.PI, 1.5 * Math.PI); 1025 | ctx.stroke(); 1026 | 1027 | ctx.beginPath(); 1028 | ctx.moveTo(fromx, toy); 1029 | ctx.lineTo(tox, toy); 1030 | ctx.stroke(); 1031 | 1032 | var angle = Math.atan2(toy - fromy, tox); 1033 | 1034 | toy = toy; 1035 | tox = tox + 5; 1036 | ctx.beginPath(); 1037 | ctx.moveTo(tox, toy); 1038 | ctx.lineTo(tox - headlen * Math.cos(angle - Math.PI / 7), toy - headlen * Math.sin(angle - Math.PI / 7)); 1039 | ctx.lineTo(tox - headlen * Math.cos(angle + Math.PI / 7), toy - headlen * Math.sin(angle + Math.PI / 7)); 1040 | ctx.lineTo(tox, toy); 1041 | ctx.lineTo(tox - headlen * Math.cos(angle - Math.PI / 7), toy - headlen * Math.sin(angle - Math.PI / 7)); 1042 | ctx.stroke(); 1043 | ctx.fill(); 1044 | } 1045 | 1046 | 1047 | // Class Odontogram 1048 | function Odontogram(jqEl, settings) { 1049 | this.jquery = jqEl; 1050 | this.canvas = jqEl.get(0); 1051 | this.context = jqEl.get(0).getContext('2d'); 1052 | this.settings = settings; 1053 | this.mode = ODONTOGRAM_MODE_DEFAULT; 1054 | this.hoverGeoms = []; 1055 | this.geometry = {}; 1056 | this.active_geometry = null; // Selected Geometry 1057 | 1058 | this.teeth = {}; // Menyimpan Coordinate Gigi dengan key: x1:y1;x2:y2;cx:cy 1059 | 1060 | this._drawBackground(); 1061 | return this; 1062 | } 1063 | 1064 | Odontogram.prototype.setMode = function (mode) { 1065 | this.mode = mode; 1066 | 1067 | return this; 1068 | }; 1069 | 1070 | Odontogram.prototype._sideTeeth = function (ctx, numbers, bigBoxSize, smallBoxSize, xpos, ypos) { 1071 | // Small Box 1072 | ctx.beginPath(); 1073 | ctx.lineWidth = "2"; 1074 | ctx.strokeStyle = "#555"; 1075 | ctx.rect(xpos + smallBoxSize / 2, ypos + smallBoxSize / 2, smallBoxSize, smallBoxSize); 1076 | ctx.stroke(); 1077 | 1078 | // Lines 1079 | //// Top Left 1080 | ctx.beginPath(); 1081 | ctx.moveTo(xpos, ypos); 1082 | ctx.lineTo(xpos + smallBoxSize / 2, ypos + smallBoxSize / 2); 1083 | ctx.stroke(); 1084 | //// Top Right 1085 | ctx.beginPath(); 1086 | ctx.moveTo(xpos + bigBoxSize, ypos); 1087 | ctx.lineTo(xpos + bigBoxSize - smallBoxSize / 2, ypos + smallBoxSize / 2); 1088 | ctx.stroke(); 1089 | //// Bottom Left 1090 | ctx.beginPath(); 1091 | ctx.moveTo(xpos, ypos + bigBoxSize); 1092 | ctx.lineTo(xpos + smallBoxSize / 2, ypos + bigBoxSize - smallBoxSize / 2); 1093 | ctx.stroke(); 1094 | //// Bottom Right 1095 | ctx.beginPath(); 1096 | ctx.moveTo(xpos + bigBoxSize, ypos + bigBoxSize); 1097 | ctx.lineTo(xpos + bigBoxSize - smallBoxSize / 2, ypos + bigBoxSize - smallBoxSize / 2); 1098 | ctx.stroke(); 1099 | 1100 | // Numbers 1101 | var num = numbers.shift(); 1102 | ctx.font = "12px Arial"; 1103 | ctx.textBaseline = "bottom"; 1104 | ctx.textAlign = "center"; 1105 | ctx.fillText(num, xpos + bigBoxSize / 2, ypos + bigBoxSize * 1.4); 1106 | 1107 | const x1 = xpos; const y1 = ypos; 1108 | const x2 = xpos + bigBoxSize; const y2 = ypos + bigBoxSize; 1109 | const cx = xpos + bigBoxSize / 2; const cy = ypos + bigBoxSize / 2; 1110 | const key = x1 + ':' + y1 + ';' + x2 + ':' + y2 + ';' + cx + ':' + cy; 1111 | this.teeth[key] = { 1112 | num: num, 1113 | bigBoxSize: bigBoxSize, 1114 | smallBoxSize: smallBoxSize, 1115 | x1: x1, 1116 | y1: y1, 1117 | x2: x2, 1118 | y2: y2, 1119 | cx: cx, 1120 | cy: cy, 1121 | // Coords shapes (top left, top right, bottom left, bottom right) 1122 | top: { 1123 | tl: { x: xpos, y: ypos }, 1124 | tr: { x: xpos + bigBoxSize, y: ypos }, 1125 | br: { x: xpos + bigBoxSize - smallBoxSize / 2, y: ypos + smallBoxSize / 2 }, 1126 | bl: { x: xpos + smallBoxSize / 2, y: ypos + smallBoxSize / 2 } 1127 | }, 1128 | right: { 1129 | tl: { x: xpos + bigBoxSize - smallBoxSize / 2, y: ypos + smallBoxSize / 2 }, 1130 | tr: { x: xpos + bigBoxSize, y: ypos }, 1131 | br: { x: xpos + bigBoxSize, y: ypos + bigBoxSize }, 1132 | bl: { x: xpos + bigBoxSize - smallBoxSize / 2, y: ypos + bigBoxSize - smallBoxSize / 2 } 1133 | }, 1134 | bottom: { 1135 | tl: { x: xpos + smallBoxSize / 2, y: ypos + bigBoxSize - smallBoxSize / 2 }, 1136 | tr: { x: xpos + bigBoxSize - smallBoxSize / 2, y: ypos + bigBoxSize - smallBoxSize / 2 }, 1137 | br: { x: xpos + bigBoxSize, y: ypos + bigBoxSize }, 1138 | bl: { x: xpos, y: ypos + bigBoxSize } 1139 | }, 1140 | left: { 1141 | tl: { x: xpos, y: ypos }, 1142 | tr: { x: xpos + smallBoxSize / 2, y: ypos + smallBoxSize / 2 }, 1143 | br: { x: xpos + smallBoxSize / 2, y: ypos + bigBoxSize - smallBoxSize / 2 }, 1144 | bl: { x: xpos, y: ypos + bigBoxSize } 1145 | }, 1146 | middle: { 1147 | tl: { x: xpos + smallBoxSize / 2, y: ypos + smallBoxSize / 2 }, 1148 | tr: { x: xpos + bigBoxSize - smallBoxSize / 2, y: ypos + smallBoxSize / 2 }, 1149 | br: { x: xpos + bigBoxSize - smallBoxSize / 2, y: ypos + bigBoxSize - smallBoxSize / 2 }, 1150 | bl: { x: xpos + smallBoxSize / 2, y: ypos + bigBoxSize - smallBoxSize / 2 } 1151 | } 1152 | } 1153 | } 1154 | 1155 | Odontogram.prototype._centerTeeth = function (ctx, numbers, bigBoxSize, smallBoxSize, xpos, ypos) { 1156 | // Small Box 1157 | ctx.beginPath(); 1158 | ctx.lineWidth = "2"; 1159 | ctx.strokeStyle = "#555"; 1160 | ctx.rect(xpos + smallBoxSize / 2 + 3, ypos + smallBoxSize - 3, smallBoxSize - 6, 0); 1161 | ctx.stroke(); 1162 | 1163 | // Lines 1164 | //// Top Left 1165 | ctx.beginPath(); 1166 | ctx.moveTo(xpos, ypos); 1167 | ctx.lineTo(xpos + smallBoxSize / 2 + 3, ypos + smallBoxSize - 3); 1168 | ctx.stroke(); 1169 | //// Top Right 1170 | ctx.beginPath(); 1171 | ctx.moveTo(xpos + bigBoxSize, ypos); 1172 | ctx.lineTo(xpos + bigBoxSize - smallBoxSize / 2 - 3, ypos + smallBoxSize - 3); 1173 | ctx.stroke(); 1174 | //// Bottom Left 1175 | ctx.beginPath(); 1176 | ctx.moveTo(xpos, ypos + bigBoxSize); 1177 | ctx.lineTo(xpos + smallBoxSize / 2 + 3, ypos + bigBoxSize - smallBoxSize - 3); 1178 | ctx.stroke(); 1179 | //// Bottom Right 1180 | ctx.beginPath(); 1181 | ctx.moveTo(xpos + bigBoxSize, ypos + bigBoxSize); 1182 | ctx.lineTo(xpos + bigBoxSize - smallBoxSize / 2 - 3, ypos + bigBoxSize - smallBoxSize - 3); 1183 | ctx.stroke(); 1184 | 1185 | // Numbers 1186 | var num = numbers.shift(); 1187 | ctx.font = "12px Arial"; 1188 | ctx.textBaseline = "bottom"; 1189 | ctx.textAlign = "center"; 1190 | ctx.fillText(num, xpos + bigBoxSize / 2, ypos + bigBoxSize * 1.4); 1191 | 1192 | const x1 = xpos; const y1 = ypos; 1193 | const x2 = xpos + bigBoxSize; const y2 = ypos + bigBoxSize; 1194 | const cx = xpos + bigBoxSize - 3; const cy = ypos + bigBoxSize - 3; 1195 | const key = x1 + ':' + y1 + ';' + x2 + ':' + y2 + ';' + cx + ':' + cy; 1196 | this.teeth[key] = { 1197 | num: num, 1198 | bigBoxSize: bigBoxSize, 1199 | smallBoxSize: smallBoxSize, 1200 | x1: x1, 1201 | y1: y1, 1202 | x2: x2, 1203 | y2: y2, 1204 | cx: cx, 1205 | cy: cy, 1206 | // Coords shapes (top left, top right, bottom left, bottom right) 1207 | top: { 1208 | tl: { x: xpos + 1, y: ypos }, 1209 | tr: { x: xpos + bigBoxSize, y: ypos }, 1210 | br: { x: xpos + bigBoxSize - smallBoxSize / 2 - 3, y: ypos + smallBoxSize - 3 }, 1211 | bl: { x: xpos + smallBoxSize / 2 + 3, y: ypos + smallBoxSize - 3 } 1212 | }, 1213 | right: { 1214 | tl: { x: xpos + bigBoxSize - smallBoxSize / 2 - 2, y: ypos + smallBoxSize - 3 }, 1215 | tr: { x: xpos + bigBoxSize, y: ypos }, 1216 | br: { x: xpos + bigBoxSize, y: ypos + bigBoxSize }, 1217 | bl: { x: xpos + bigBoxSize - smallBoxSize / 2 - 2, y: ypos + smallBoxSize - 3 } 1218 | }, 1219 | bottom: { 1220 | tl: { x: xpos + smallBoxSize / 2 + 3, y: ypos + bigBoxSize - smallBoxSize - 3 }, 1221 | tr: { x: xpos + bigBoxSize - smallBoxSize / 2 - 3, y: ypos + bigBoxSize - smallBoxSize - 3 }, 1222 | br: { x: xpos + bigBoxSize - 1, y: ypos + bigBoxSize }, 1223 | bl: { x: xpos + 1, y: ypos + bigBoxSize } 1224 | }, 1225 | left: { 1226 | tl: { x: xpos, y: ypos }, 1227 | tr: { x: xpos + smallBoxSize / 2 + 2, y: ypos + smallBoxSize - 3 }, 1228 | br: { x: xpos + smallBoxSize / 2 + 2, y: ypos + smallBoxSize - 3 }, 1229 | bl: { x: xpos, y: ypos + bigBoxSize } 1230 | }, 1231 | middle: { 1232 | tl: { x: 0, y: 0 }, 1233 | tr: { x: 0, y: 0 }, 1234 | br: { x: 0, y: 0 }, 1235 | bl: { x: 0, y: 0 } 1236 | } 1237 | } 1238 | } 1239 | 1240 | Odontogram.prototype._drawBackground = function () { 1241 | var canvas = this.canvas; 1242 | var ctx = this.context; 1243 | 1244 | ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); // Clears the canvas 1245 | 1246 | var width = ctx.canvas.width; 1247 | var height = ctx.canvas.height; 1248 | 1249 | var pl = 10, 1250 | pr = 10, 1251 | pt = 30, 1252 | pb = 10, 1253 | gap_per = 10, 1254 | gap_bag = 30; 1255 | 1256 | var bigBoxSize = (width - (pl + pt + (gap_per * 16) + gap_bag)) / 16; 1257 | var smallBoxSize = bigBoxSize / 2; 1258 | 1259 | // Numbers 1260 | var numbers = [ 1261 | '18', '17', '16', '15', '14', '13', '12', '11', '21', '22', '23', '24', '25', '26', '27', '28', 1262 | '55', '54', '53', '52', '51', '61', '62', '63', '64', '65', 1263 | '85', '84', '83', '82', '81', '71', '72', '73', '74', '75', 1264 | '48', '47', '46', '45', '44', '43', '42', '41', '31', '32', '33', '34', '35', '36', '37', '38' 1265 | ]; 1266 | 1267 | var xpos, ypos; 1268 | var sec = 0; 1269 | for (var y = 0; y < 4; y++) { 1270 | sec = 0; 1271 | for (var x = 0; x < 16; x++) { 1272 | if (x % 8 == 0 && x != 0) sec++; 1273 | // else sec = 0; 1274 | 1275 | if ((y % 3 != 0) && 1276 | (x < 8 ? (x % 8) - 2 <= 0 : (x % 8) >= 5)) continue; 1277 | 1278 | xpos = x * bigBoxSize + (pl) + x * gap_per + (sec * gap_bag); 1279 | ypos = y * bigBoxSize + pt + (pt * y); 1280 | 1281 | // Big Box 1282 | ctx.beginPath(); 1283 | ctx.lineWidth = "2"; 1284 | ctx.strokeStyle = "#555"; 1285 | ctx.rect(xpos, ypos, bigBoxSize, bigBoxSize); 1286 | ctx.stroke(); 1287 | 1288 | if (x >= 5 && x <= 11) this._centerTeeth(ctx, numbers, bigBoxSize, smallBoxSize, xpos, ypos); 1289 | else this._sideTeeth(ctx, numbers, bigBoxSize, smallBoxSize, xpos, ypos); 1290 | } 1291 | } 1292 | 1293 | var me = this; 1294 | var img = new Image(); 1295 | img.src = this.getDataURL(); 1296 | img.onload = function () { 1297 | me.background = { 1298 | image: img, 1299 | x: 1, 1300 | y: 1, 1301 | w: img.naturalWidth, 1302 | h: img.naturalHeight 1303 | }; 1304 | 1305 | me.redraw(); 1306 | } 1307 | } 1308 | 1309 | // TOP 1310 | function top_leftArrow(ctx, coord) { 1311 | var x1 = parseFloat(coord.x1); 1312 | var y1 = parseFloat(coord.y1); 1313 | var x2 = parseFloat(coord.x2); 1314 | var y2 = parseFloat(coord.y2); 1315 | 1316 | var fromx, fromy, tox, toy; 1317 | fromx = x2 - (x2 - x1) / 4; 1318 | fromy = y1 - 10; 1319 | tox = fromx - 25; 1320 | toy = fromy; 1321 | 1322 | // fromx = 50; 1323 | // fromy = 20; 1324 | // tox = 25; 1325 | // toy = 20; 1326 | 1327 | var headlen = 10; 1328 | var lineWidth = 2; 1329 | 1330 | var angle = Math.atan2(toy - fromy, tox - fromx); 1331 | 1332 | ctx.beginPath(); 1333 | ctx.moveTo(fromx, fromy); 1334 | ctx.lineTo(tox, toy); 1335 | ctx.strokeStyle = "#000000"; 1336 | ctx.lineWidth = lineWidth; 1337 | ctx.stroke(); 1338 | 1339 | //starting a new path from the head of the arrow to one of the sides of the point 1340 | ctx.beginPath(); 1341 | ctx.moveTo(tox, toy); 1342 | ctx.lineTo(tox - headlen * Math.cos(angle - Math.PI / 7), toy - headlen * Math.sin(angle - Math.PI / 7)); 1343 | 1344 | //path from the side point of the arrow, to the other side point 1345 | ctx.lineTo(tox - headlen * Math.cos(angle + Math.PI / 7), toy - headlen * Math.sin(angle + Math.PI / 7)); 1346 | 1347 | //path from the side point back to the tip of the arrow, and then again to the opposite side point 1348 | ctx.lineTo(tox, toy); 1349 | ctx.lineTo(tox - headlen * Math.cos(angle - Math.PI / 7), toy - headlen * Math.sin(angle - Math.PI / 7)); 1350 | 1351 | //draws the paths created above 1352 | ctx.strokeStyle = "#000000"; 1353 | ctx.fillStyle = "#000000"; 1354 | ctx.lineWidth = lineWidth; 1355 | ctx.stroke(); 1356 | ctx.fill(); 1357 | } 1358 | function top_rightArrow(ctx, coord) { 1359 | var x1 = parseFloat(coord.x1); 1360 | var y1 = parseFloat(coord.y1); 1361 | var x2 = parseFloat(coord.x2); 1362 | var y2 = parseFloat(coord.y2); 1363 | 1364 | var fromx, fromy, tox, toy; 1365 | fromx = x1 + (x2 - x1) / 4; 1366 | fromy = y1 - 10; 1367 | tox = fromx + 25; 1368 | toy = fromy; 1369 | 1370 | // fromx = 60; 1371 | // fromy = 20; 1372 | // tox = 90; 1373 | // toy = 20; 1374 | 1375 | var headlen = 10; 1376 | var lineWidth = 2; 1377 | 1378 | var angle = Math.atan2(toy - fromy, tox - fromx); 1379 | 1380 | ctx.beginPath(); 1381 | ctx.moveTo(fromx, fromy); 1382 | ctx.lineTo(tox, toy); 1383 | ctx.strokeStyle = "#000000"; 1384 | ctx.lineWidth = lineWidth; 1385 | ctx.stroke(); 1386 | 1387 | //starting a new path from the head of the arrow to one of the sides of the point 1388 | ctx.beginPath(); 1389 | ctx.moveTo(tox, toy); 1390 | ctx.lineTo(tox - headlen * Math.cos(angle - Math.PI / 7), toy - headlen * Math.sin(angle - Math.PI / 7)); 1391 | 1392 | //path from the side point of the arrow, to the other side point 1393 | ctx.lineTo(tox - headlen * Math.cos(angle + Math.PI / 7), toy - headlen * Math.sin(angle + Math.PI / 7)); 1394 | 1395 | //path from the side point back to the tip of the arrow, and then again to the opposite side point 1396 | ctx.lineTo(tox, toy); 1397 | ctx.lineTo(tox - headlen * Math.cos(angle - Math.PI / 7), toy - headlen * Math.sin(angle - Math.PI / 7)); 1398 | 1399 | //draws the paths created above 1400 | ctx.strokeStyle = "#000000"; 1401 | ctx.fillStyle = "#000000"; 1402 | ctx.lineWidth = lineWidth; 1403 | ctx.stroke(); 1404 | ctx.fill(); 1405 | } 1406 | function top_turnLeftArrow(ctx, coord) { 1407 | var x1 = parseFloat(coord.x1); 1408 | var y1 = parseFloat(coord.y1); 1409 | var x2 = parseFloat(coord.x2); 1410 | var y2 = parseFloat(coord.y2); 1411 | 1412 | var fromx, fromy, tox, toy; 1413 | fromx = x2 - (x2 - x1) / 2; 1414 | fromy = y1 - 10; 1415 | tox = fromx + 10; 1416 | toy = fromy; 1417 | 1418 | var headlen = 10; 1419 | var lineWidth = 2; 1420 | 1421 | ctx.strokeStyle = "#000000"; 1422 | ctx.fillStyle = "#000000"; 1423 | ctx.lineWidth = lineWidth; 1424 | 1425 | ctx.beginPath(); 1426 | ctx.arc(fromx, fromy, 5, 0.38 * Math.PI, 1.5 * Math.PI); 1427 | ctx.stroke(); 1428 | 1429 | ctx.beginPath(); 1430 | ctx.moveTo(fromx, fromy - 5); 1431 | ctx.lineTo(tox, fromy - 5); 1432 | ctx.stroke(); 1433 | 1434 | var angle = Math.atan2(toy - fromy, tox - fromx); 1435 | 1436 | toy = toy - 5; 1437 | tox = tox + 5; 1438 | ctx.beginPath(); 1439 | ctx.moveTo(tox, toy); 1440 | ctx.lineTo(tox - headlen * Math.cos(angle - Math.PI / 7), toy - headlen * Math.sin(angle - Math.PI / 7)); 1441 | ctx.lineTo(tox - headlen * Math.cos(angle + Math.PI / 7), toy - headlen * Math.sin(angle + Math.PI / 7)); 1442 | ctx.lineTo(tox, toy); 1443 | ctx.lineTo(tox - headlen * Math.cos(angle - Math.PI / 7), toy - headlen * Math.sin(angle - Math.PI / 7)); 1444 | ctx.stroke(); 1445 | ctx.fill(); 1446 | 1447 | //starting a new path from the head of the arrow to one of the sides of the point 1448 | // ctx.beginPath(); 1449 | // ctx.moveTo(tox, toy); 1450 | // ctx.lineTo(tox-headlen*Math.cos(angle-Math.PI/7),toy-headlen*Math.sin(angle-Math.PI/7)); 1451 | 1452 | //path from the side point of the arrow, to the other side point 1453 | // ctx.lineTo(tox-headlen*Math.cos(angle+Math.PI/7),toy-headlen*Math.sin(angle+Math.PI/7)); 1454 | 1455 | //path from the side point back to the tip of the arrow, and then again to the opposite side point 1456 | // ctx.lineTo(tox, toy); 1457 | // ctx.lineTo(tox-headlen*Math.cos(angle-Math.PI/7),toy-headlen*Math.sin(angle-Math.PI/7)); 1458 | 1459 | //draws the paths created above 1460 | // ctx.strokeStyle = "#000000"; 1461 | // ctx.lineWidth = lineWidth; 1462 | // ctx.stroke(); 1463 | } 1464 | function top_turnRightArrow(ctx, coord) { 1465 | var x1 = parseFloat(coord.x1); 1466 | var y1 = parseFloat(coord.y1); 1467 | var x2 = parseFloat(coord.x2); 1468 | var y2 = parseFloat(coord.y2); 1469 | 1470 | var fromx, fromy, tox, toy; 1471 | fromx = x1 + (x2 - x1) / 4; 1472 | fromy = y1 - 10; 1473 | tox = fromx + 10; 1474 | toy = fromy; 1475 | 1476 | // fromx = 180; 1477 | // fromy = 20; 1478 | // tox = 190; 1479 | // toy = 20; 1480 | 1481 | var headlen = 10; 1482 | var lineWidth = 2; 1483 | 1484 | ctx.strokeStyle = "#000000"; 1485 | ctx.fillStyle = "#000000"; 1486 | ctx.lineWidth = lineWidth; 1487 | 1488 | ctx.beginPath(); 1489 | ctx.arc(tox, fromy, 5, 1.5 * Math.PI, 0.5 * Math.PI); 1490 | ctx.stroke(); 1491 | 1492 | ctx.beginPath(); 1493 | ctx.moveTo(tox, fromy - 5); 1494 | ctx.lineTo(fromx, fromy - 5); 1495 | ctx.stroke(); 1496 | 1497 | var angle = Math.atan2(toy - fromy, tox - fromx); 1498 | 1499 | toy = toy - 5; 1500 | fromx = fromx - 5; 1501 | ctx.beginPath(); 1502 | ctx.moveTo(fromx, toy); 1503 | ctx.lineTo(fromx + headlen * Math.cos(angle + Math.PI / 7), toy + headlen * Math.sin(angle + Math.PI / 7)); 1504 | ctx.lineTo(fromx + headlen * Math.cos(angle - Math.PI / 7), toy + headlen * Math.sin(angle - Math.PI / 7)); 1505 | ctx.lineTo(fromx, toy); 1506 | ctx.lineTo(fromx + headlen * Math.cos(angle + Math.PI / 7), toy + headlen * Math.sin(angle - Math.PI / 7)); 1507 | ctx.stroke(); 1508 | ctx.fill(); 1509 | } 1510 | // BOTTOM 1511 | function bottom_leftArrow(ctx, coord) { 1512 | var x1 = parseFloat(coord.x1); 1513 | var y1 = parseFloat(coord.y1); 1514 | var x2 = parseFloat(coord.x2); 1515 | var y2 = parseFloat(coord.y2); 1516 | 1517 | var fromx, fromy, tox, toy; 1518 | fromx = x2 - (x2 - x1) / 4; 1519 | fromy = y2 + 10; 1520 | tox = fromx - 25; 1521 | toy = fromy; 1522 | 1523 | // fromx = 50; 1524 | // fromy = 20; 1525 | // tox = 25; 1526 | // toy = 20; 1527 | 1528 | var headlen = 10; 1529 | var lineWidth = 2; 1530 | 1531 | var angle = Math.atan2(toy - fromy, tox - fromx); 1532 | 1533 | ctx.beginPath(); 1534 | ctx.moveTo(fromx, fromy); 1535 | ctx.lineTo(tox, toy); 1536 | ctx.strokeStyle = "#000000"; 1537 | ctx.lineWidth = lineWidth; 1538 | ctx.stroke(); 1539 | 1540 | //starting a new path from the head of the arrow to one of the sides of the point 1541 | ctx.beginPath(); 1542 | ctx.moveTo(tox, toy); 1543 | ctx.lineTo(tox - headlen * Math.cos(angle - Math.PI / 7), toy - headlen * Math.sin(angle - Math.PI / 7)); 1544 | 1545 | //path from the side point of the arrow, to the other side point 1546 | ctx.lineTo(tox - headlen * Math.cos(angle + Math.PI / 7), toy - headlen * Math.sin(angle + Math.PI / 7)); 1547 | 1548 | //path from the side point back to the tip of the arrow, and then again to the opposite side point 1549 | ctx.lineTo(tox, toy); 1550 | ctx.lineTo(tox - headlen * Math.cos(angle - Math.PI / 7), toy - headlen * Math.sin(angle - Math.PI / 7)); 1551 | 1552 | //draws the paths created above 1553 | ctx.strokeStyle = "#000000"; 1554 | ctx.fillStyle = "#000000"; 1555 | ctx.lineWidth = lineWidth; 1556 | ctx.stroke(); 1557 | ctx.fill(); 1558 | } 1559 | 1560 | function bottom_rightArrow(ctx, coord) { 1561 | var x1 = parseFloat(coord.x1); 1562 | var y1 = parseFloat(coord.y1); 1563 | var x2 = parseFloat(coord.x2); 1564 | var y2 = parseFloat(coord.y2); 1565 | 1566 | var fromx, fromy, tox, toy; 1567 | fromx = x1 + (x2 - x1) / 4; 1568 | fromy = y2 + 10; 1569 | tox = fromx + 25; 1570 | toy = fromy; 1571 | 1572 | // fromx = 60; 1573 | // fromy = 20; 1574 | // tox = 90; 1575 | // toy = 20; 1576 | 1577 | var headlen = 10; 1578 | var lineWidth = 2; 1579 | 1580 | var angle = Math.atan2(toy - fromy, tox - fromx); 1581 | 1582 | ctx.beginPath(); 1583 | ctx.moveTo(fromx, fromy); 1584 | ctx.lineTo(tox, toy); 1585 | ctx.strokeStyle = "#000000"; 1586 | ctx.lineWidth = lineWidth; 1587 | ctx.stroke(); 1588 | 1589 | //starting a new path from the head of the arrow to one of the sides of the point 1590 | ctx.beginPath(); 1591 | ctx.moveTo(tox, toy); 1592 | ctx.lineTo(tox - headlen * Math.cos(angle - Math.PI / 7), toy - headlen * Math.sin(angle - Math.PI / 7)); 1593 | 1594 | //path from the side point of the arrow, to the other side point 1595 | ctx.lineTo(tox - headlen * Math.cos(angle + Math.PI / 7), toy - headlen * Math.sin(angle + Math.PI / 7)); 1596 | 1597 | //path from the side point back to the tip of the arrow, and then again to the opposite side point 1598 | ctx.lineTo(tox, toy); 1599 | ctx.lineTo(tox - headlen * Math.cos(angle - Math.PI / 7), toy - headlen * Math.sin(angle - Math.PI / 7)); 1600 | 1601 | //draws the paths created above 1602 | ctx.strokeStyle = "#000000"; 1603 | ctx.fillStyle = "#000000"; 1604 | ctx.lineWidth = lineWidth; 1605 | ctx.stroke(); 1606 | ctx.fill(); 1607 | } 1608 | 1609 | function bottom_turnLeftArrow(ctx, coord) { 1610 | var x1 = parseFloat(coord.x1); 1611 | var y1 = parseFloat(coord.y1); 1612 | var x2 = parseFloat(coord.x2); 1613 | var y2 = parseFloat(coord.y2); 1614 | 1615 | var fromx, fromy, tox, toy; 1616 | fromx = x2 - (x2 - x1) / 2; 1617 | fromy = y2 + 10; 1618 | tox = fromx + 10; 1619 | toy = fromy + 5; 1620 | 1621 | var headlen = 10; 1622 | var lineWidth = 2; 1623 | 1624 | ctx.strokeStyle = "#000000"; 1625 | ctx.fillStyle = "#000000"; 1626 | ctx.lineWidth = lineWidth; 1627 | 1628 | ctx.beginPath(); 1629 | ctx.arc(fromx, fromy, 5, 0.38 * Math.PI, 1.5 * Math.PI); 1630 | ctx.stroke(); 1631 | 1632 | ctx.beginPath(); 1633 | ctx.moveTo(fromx, toy); 1634 | ctx.lineTo(tox, toy); 1635 | ctx.stroke(); 1636 | 1637 | var angle = Math.atan2(toy - fromy, tox); 1638 | 1639 | toy = toy; 1640 | tox = tox + 5; 1641 | ctx.beginPath(); 1642 | ctx.moveTo(tox, toy); 1643 | ctx.lineTo(tox - headlen * Math.cos(angle - Math.PI / 7), toy - headlen * Math.sin(angle - Math.PI / 7)); 1644 | ctx.lineTo(tox - headlen * Math.cos(angle + Math.PI / 7), toy - headlen * Math.sin(angle + Math.PI / 7)); 1645 | ctx.lineTo(tox, toy); 1646 | ctx.lineTo(tox - headlen * Math.cos(angle - Math.PI / 7), toy - headlen * Math.sin(angle - Math.PI / 7)); 1647 | ctx.stroke(); 1648 | ctx.fill(); 1649 | } 1650 | 1651 | function bottom_turnRightArrow(ctx, coord) { 1652 | var x1 = parseFloat(coord.x1); 1653 | var y1 = parseFloat(coord.y1); 1654 | var x2 = parseFloat(coord.x2); 1655 | var y2 = parseFloat(coord.y2); 1656 | 1657 | var fromx, fromy, tox, toy; 1658 | fromx = x2 - (x2 - x1) / 2; 1659 | fromy = y2 + 10; 1660 | tox = fromx - 10; 1661 | toy = fromy + 5; 1662 | 1663 | var headlen = 10; 1664 | var lineWidth = 2; 1665 | 1666 | ctx.strokeStyle = "#000000"; 1667 | ctx.fillStyle = "#000000"; 1668 | ctx.lineWidth = lineWidth; 1669 | 1670 | ctx.beginPath(); 1671 | ctx.arc(fromx, fromy, 5, 1.5 * Math.PI, 0.5 * Math.PI); 1672 | ctx.stroke(); 1673 | 1674 | ctx.beginPath(); 1675 | ctx.moveTo(tox, toy); 1676 | ctx.lineTo(fromx, toy); 1677 | ctx.stroke(); 1678 | 1679 | var angle = Math.atan2(toy - fromy, tox); 1680 | 1681 | toy = toy; 1682 | tox = tox - 5; 1683 | ctx.beginPath(); 1684 | ctx.moveTo(tox, toy); 1685 | ctx.lineTo(tox + headlen * Math.cos(angle + Math.PI / 7), toy - headlen * Math.sin(angle + Math.PI / 7)); 1686 | ctx.lineTo(tox + headlen * Math.cos(angle + Math.PI / 7), toy - headlen * Math.sin(angle - Math.PI / 7)); 1687 | ctx.lineTo(tox, toy); 1688 | ctx.lineTo(tox + headlen * Math.cos(angle + Math.PI / 7), toy - headlen * Math.sin(angle + Math.PI / 7)); 1689 | ctx.stroke(); 1690 | ctx.fill(); 1691 | } 1692 | 1693 | Odontogram.prototype.redraw = function () { 1694 | var canvas = this.canvas; 1695 | var ctx = this.context; 1696 | 1697 | ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); // Clears the canvas 1698 | 1699 | if (this.background) { // Background 1700 | ctx.drawImage(this.background.image, this.background.x, this.background.y, ctx.canvas.width, ctx.canvas.height); 1701 | } 1702 | 1703 | // Draw Hover Geom 1704 | var hoverGeoms; 1705 | for (var i = 0; i < this.hoverGeoms.length; i++) { 1706 | hoverGeoms = convertGeom(this.hoverGeoms[i], this.mode); 1707 | hoverGeoms.render(ctx); 1708 | } 1709 | 1710 | 1711 | // Draw Geometry 1712 | var geoms; 1713 | for (var keyCoord in this.geometry) { 1714 | geoms = this.geometry[keyCoord]; 1715 | for (var x in geoms) { 1716 | if (geoms[x].render) geoms[x].render(ctx); 1717 | } 1718 | } 1719 | 1720 | if (this.active_geometry) { 1721 | this.active_geometry.render(ctx); 1722 | } 1723 | 1724 | return this; 1725 | }; 1726 | 1727 | Odontogram.prototype.setGeometry = function (geometry) { 1728 | for (var keyCoord in geometry) { 1729 | for (var i = 0; i < geometry[keyCoord].length; i++) { 1730 | geometry[keyCoord][i] = convertGeomFromObject(geometry[keyCoord][i]); 1731 | } 1732 | } 1733 | 1734 | this.geometry = geometry; 1735 | 1736 | this.redraw(); 1737 | } 1738 | 1739 | Odontogram.prototype.search = function (key, value) { 1740 | const obj = this.teeth 1741 | for (let k in obj) { 1742 | if (obj[k][key] == value) { 1743 | return [k, obj[k]]; 1744 | } 1745 | } 1746 | return null; 1747 | } 1748 | 1749 | Odontogram.prototype.setGeometryByPos = function (data) { 1750 | let geometry = {}; 1751 | for (d of data) { 1752 | if (!d.code || !d.pos) continue; 1753 | if (d.pos.includes('-')) { 1754 | [pos, sub] = d.pos.split('-'); 1755 | const t = this.search('num', pos); 1756 | let s; 1757 | if (sub == 'L') s = t[1].left 1758 | else if (sub == 'R') s = t[1].right 1759 | else if (sub == 'B') s = t[1].bottom 1760 | else if (sub == 'T') s = t[1].top 1761 | else if (sub == 'M') s = t[1].middle 1762 | 1763 | if (['1', '2', '3'].some(a => pos.endsWith(a)) && sub == 'M') continue 1764 | 1765 | if (!geometry[t[0]]) geometry[t[0]] = [] 1766 | 1767 | geometry[t[0]].push({ 1768 | name: d.code, 1769 | pos: d.pos, 1770 | vertices: [s.bl, s.br, s.tr, s.tl] 1771 | }) 1772 | } else { 1773 | const t = this.search('num', d.pos); 1774 | if (!geometry[t[0]]) geometry[t[0]] = [] 1775 | geometry[t[0]].push({ 1776 | name: d.code, 1777 | pos: d.pos, 1778 | vertices: [ 1779 | { x: t[1].x1, y: t[1].y1 }, 1780 | { x: t[1].x2, y: t[1].y2 } 1781 | ] 1782 | }) 1783 | } 1784 | } 1785 | this.setGeometry(geometry); 1786 | return geometry; 1787 | } 1788 | 1789 | Odontogram.prototype.getDataURL = function () { 1790 | return this.canvas.toDataURL(); 1791 | } 1792 | 1793 | $.fn.odontogram = function (mode, arg1, arg2, arg3, arg4) { 1794 | var instance = this.data('odontogram'); 1795 | switch (mode) { 1796 | case 'init': // Arg1 is options 1797 | if (this.prop('nodeName') !== "CANVAS") { 1798 | throw Error('Odontogram must be valid `CANVAS`.'); 1799 | } 1800 | 1801 | if (instance != null) { 1802 | throw Error("can't reinitialize odontogram."); 1803 | } 1804 | 1805 | return initialize(this, arg1); 1806 | case 'setMode': 1807 | instance.active_geometry = null; 1808 | checkOdontogram(this, mode); 1809 | setMode(this, arg1); 1810 | break; 1811 | case 'redraw': 1812 | checkOdontogram(this, mode); 1813 | redraw(this); 1814 | break; 1815 | case 'getDataURL': 1816 | checkOdontogram(this, mode); 1817 | return instance.getDataURL(); 1818 | case 'setGeometry': 1819 | checkOdontogram(this, mode); 1820 | instance.setGeometry(arg1); 1821 | break; 1822 | case 'setGeometryByPos': 1823 | checkOdontogram(this, mode); 1824 | instance.setGeometryByPos(arg1); 1825 | break; 1826 | // DLL 1827 | } 1828 | 1829 | return this; 1830 | } 1831 | 1832 | function initialize($this, options) { 1833 | var settings = $.extend({}, $.fn.odontogram.defaults, options); 1834 | 1835 | var canvas = $this.get(0); 1836 | 1837 | $this.prop('height', settings.height) 1838 | .prop('width', settings.width) 1839 | .css('width', settings.width) 1840 | .css('height', settings.height); 1841 | 1842 | canvas.width = parseFloat(settings.width); 1843 | canvas.height = parseFloat(settings.height); 1844 | 1845 | var instance = new Odontogram($this, settings); 1846 | 1847 | $this.data('odontogram', instance); 1848 | 1849 | $this 1850 | .on('mousemove', _on_mouse_move) 1851 | .on('click', _on_mouse_click); 1852 | 1853 | return instance; 1854 | } 1855 | 1856 | function setMode($this, mode) { 1857 | // TODO 1858 | // switch (mode) { 1859 | // default: 1860 | // throw Error("Odontogram invalid mode `" + mode + "`"); 1861 | // } 1862 | var instance = $this.data('odontogram'); 1863 | instance.setMode(mode); 1864 | } 1865 | 1866 | function redraw($this) { 1867 | var instance = $this.data('odontogram'); 1868 | instance.redraw(); 1869 | } 1870 | 1871 | function checkOdontogram($this, mode) { 1872 | if ($this.data('odontogram') == null || !($this.data('odontogram') instanceof Odontogram)) { 1873 | throw Error('`' + mode + '` must be valid Odontogram object.'); 1874 | } 1875 | } 1876 | 1877 | // HELPERS 1878 | // Convert Geometry to Specific Mode (geometry = Polygon) 1879 | function convertGeom(geometry, mode) { 1880 | var newGeometry; 1881 | switch (mode) { 1882 | case ODONTOGRAM_MODE_AMF: 1883 | newGeometry = new AMF(geometry.vertices); 1884 | break; 1885 | case ODONTOGRAM_MODE_COF: 1886 | newGeometry = new COF(geometry.vertices); 1887 | break; 1888 | case ODONTOGRAM_MODE_FIS: 1889 | newGeometry = new FIS(geometry.vertices); 1890 | break; 1891 | case ODONTOGRAM_MODE_NVT: 1892 | newGeometry = new NVT(geometry.vertices); 1893 | break; 1894 | case ODONTOGRAM_MODE_RCT: 1895 | newGeometry = new RCT(geometry.vertices); 1896 | break; 1897 | case ODONTOGRAM_MODE_NON: 1898 | newGeometry = new NON(geometry.vertices); 1899 | break; 1900 | case ODONTOGRAM_MODE_UNE: 1901 | newGeometry = new UNE(geometry.vertices); 1902 | break; 1903 | case ODONTOGRAM_MODE_PRE: 1904 | newGeometry = new PRE(geometry.vertices); 1905 | break; 1906 | case ODONTOGRAM_MODE_ANO: 1907 | newGeometry = new ANO(geometry.vertices); 1908 | break; 1909 | case ODONTOGRAM_MODE_CARIES: 1910 | newGeometry = new CARIES(geometry.vertices); 1911 | break; 1912 | case ODONTOGRAM_MODE_CFR: 1913 | newGeometry = new CFR(geometry.vertices); 1914 | break; 1915 | case ODONTOGRAM_MODE_FMC: 1916 | newGeometry = new FMC(geometry.vertices); 1917 | break; 1918 | case ODONTOGRAM_MODE_POC: 1919 | newGeometry = new POC(geometry.vertices); 1920 | break; 1921 | case ODONTOGRAM_MODE_RRX: 1922 | newGeometry = new RRX(geometry.vertices); 1923 | break; 1924 | case ODONTOGRAM_MODE_MIS: 1925 | newGeometry = new MIS(geometry.vertices); 1926 | break; 1927 | case ODONTOGRAM_MODE_IPX: 1928 | newGeometry = new IPX(geometry.vertices); 1929 | break; 1930 | case ODONTOGRAM_MODE_FRM_ACR: 1931 | newGeometry = new FRM_ACR(geometry.vertices); 1932 | break; 1933 | case ODONTOGRAM_MODE_BRIDGE: 1934 | newGeometry = new BRIDGE(geometry[0], geometry[1]); 1935 | break; 1936 | case ODONTOGRAM_MODE_HAPUS: 1937 | newGeometry = new HAPUS(geometry.vertices); 1938 | break; 1939 | case ODONTOGRAM_MODE_ARROW_TOP_LEFT: 1940 | newGeometry = new ARROW_TOP_LEFT(geometry.vertices); 1941 | break; 1942 | case ODONTOGRAM_MODE_ARROW_TOP_RIGHT: 1943 | newGeometry = new ARROW_TOP_RIGHT(geometry.vertices); 1944 | break; 1945 | case ODONTOGRAM_MODE_ARROW_TOP_TURN_LEFT: 1946 | newGeometry = new ARROW_TOP_TURN_LEFT(geometry.vertices); 1947 | break; 1948 | case ODONTOGRAM_MODE_ARROW_TOP_TURN_RIGHT: 1949 | newGeometry = new ARROW_TOP_TURN_RIGHT(geometry.vertices); 1950 | break; 1951 | case ODONTOGRAM_MODE_ARROW_BOTTOM_LEFT: 1952 | newGeometry = new ARROW_BOTTOM_LEFT(geometry.vertices); 1953 | break; 1954 | case ODONTOGRAM_MODE_ARROW_BOTTOM_RIGHT: 1955 | newGeometry = new ARROW_BOTTOM_RIGHT(geometry.vertices); 1956 | break; 1957 | case ODONTOGRAM_MODE_ARROW_BOTTOM_TURN_LEFT: 1958 | newGeometry = new ARROW_BOTTOM_TURN_LEFT(geometry.vertices); 1959 | break; 1960 | case ODONTOGRAM_MODE_ARROW_BOTTOM_TURN_RIGHT: 1961 | newGeometry = new ARROW_BOTTOM_TURN_RIGHT(geometry.vertices); 1962 | break; 1963 | default: 1964 | newGeometry = geometry; 1965 | break; 1966 | } 1967 | newGeometry.pos = geometry.pos; 1968 | 1969 | return newGeometry; 1970 | } 1971 | 1972 | function convertGeomFromObject(geometry) { 1973 | var newGeom = null; 1974 | switch (geometry.name) { 1975 | case 'Polygon': 1976 | newGeom = new Polygon(geometry.vertices, geometry.options); 1977 | break; 1978 | case 'AMF': 1979 | newGeom = new AMF(geometry.vertices, geometry.options); 1980 | break; 1981 | case 'COF': 1982 | newGeom = new COF(geometry.vertices, geometry.options); 1983 | break; 1984 | case 'FIS': 1985 | newGeom = new FIS(geometry.vertices, geometry.options); 1986 | break; 1987 | case 'NVT': 1988 | newGeom = new NVT(geometry.vertices, geometry.options); 1989 | break; 1990 | case 'RCT': 1991 | newGeom = new RCT(geometry.vertices, geometry.options); 1992 | break; 1993 | case 'NON': 1994 | newGeom = new NON(geometry.vertices, geometry.options); 1995 | break; 1996 | case 'UNE': 1997 | newGeom = new UNE(geometry.vertices, geometry.options); 1998 | break; 1999 | case 'PRE': 2000 | newGeom = new PRE(geometry.vertices, geometry.options); 2001 | break; 2002 | case 'ANO': 2003 | newGeom = new ANO(geometry.vertices, geometry.options); 2004 | break; 2005 | case 'CARIES': 2006 | newGeom = new CARIES(geometry.vertices, geometry.options); 2007 | break; 2008 | case 'CFR': 2009 | newGeom = new CFR(geometry.vertices, geometry.options); 2010 | break; 2011 | case 'FMC': 2012 | newGeom = new FMC(geometry.vertices, geometry.options); 2013 | break; 2014 | case 'POC': 2015 | newGeom = new POC(geometry.vertices, geometry.options); 2016 | break; 2017 | case 'RRX': 2018 | newGeom = new RRX(geometry.vertices, geometry.options); 2019 | break; 2020 | case 'MIS': 2021 | newGeom = new MIS(geometry.vertices, geometry.options); 2022 | break; 2023 | case 'IPX': 2024 | newGeom = new IPX(geometry.vertices, geometry.options); 2025 | break; 2026 | case 'FRM_ACR': 2027 | newGeom = new FRM_ACR(geometry.vertices, geometry.options); 2028 | break; 2029 | case 'BRIDGE': 2030 | newGeom = new BRIDGE(geometry.startVert, geometry.endVert, geometry.options); 2031 | break; 2032 | case 'ARROW_TOP_LEFT': 2033 | newGeom = new ARROW_TOP_LEFT(geometry.vertices, geometry.options); 2034 | break; 2035 | case 'ARROW_TOP_RIGHT': 2036 | newGeom = new ARROW_TOP_RIGHT(geometry.vertices, geometry.options); 2037 | break; 2038 | case 'ARROW_TOP_TURN_LEFT': 2039 | newGeom = new ARROW_TOP_TURN_LEFT(geometry.vertices, geometry.options); 2040 | break; 2041 | case 'ARROW_TOP_TURN_RIGHT': 2042 | newGeom = new ARROW_TOP_TURN_RIGHT(geometry.vertices, geometry.options); 2043 | break; 2044 | case 'ARROW_BOTTOM_LEFT': 2045 | newGeom = new ARROW_BOTTOM_LEFT(geometry.vertices, geometry.options); 2046 | break; 2047 | case 'ARROW_BOTTOM_RIGHT': 2048 | newGeom = new ARROW_BOTTOM_RIGHT(geometry.vertices, geometry.options); 2049 | break; 2050 | case 'ARROW_BOTTOM_TURN_LEFT': 2051 | newGeom = new ARROW_BOTTOM_TURN_LEFT(geometry.vertices, geometry.options); 2052 | break; 2053 | case 'ARROW_BOTTOM_TURN_RIGHT': 2054 | newGeom = new ARROW_BOTTOM_TURN_RIGHT(geometry.vertices, geometry.options); 2055 | break; 2056 | case 'HAPUS': 2057 | newGeom = new HAPUS(geometry.vertices, geometry.options); 2058 | break; 2059 | } 2060 | 2061 | return newGeom; 2062 | } 2063 | 2064 | //// Check Hover On Teeth (return Geom) 2065 | function getHoverShapeOnTeeth(mouse, teeth) { 2066 | var geoms = []; 2067 | for (var key in teeth) { 2068 | switch (key) { 2069 | case 'middle': 2070 | case 'top': 2071 | case 'bottom': 2072 | case 'left': 2073 | case 'right': 2074 | if (isPolyIntersect(teeth[key], mouse)) { 2075 | geoms.push({ name: key, coord: teeth[key] }); 2076 | } 2077 | break; 2078 | } 2079 | } 2080 | 2081 | var polygonOpt = { 2082 | fillStyle: 'rgba(55, 55, 55, 0.2)' 2083 | }; 2084 | var polygons = []; 2085 | var vertices; 2086 | for (var i = 0; i < geoms.length; i++) { 2087 | vertices = []; 2088 | for (var key in geoms[i].coord) { 2089 | vertices.push(geoms[i].coord[key]); 2090 | } 2091 | const pol = new Polygon(vertices, polygonOpt); 2092 | pol.name = geoms[i].name; 2093 | polygons.push(pol); 2094 | } 2095 | 2096 | return polygons; 2097 | } 2098 | 2099 | function isRectIntersect(rectA, rectB) { 2100 | return rectA.x1 < rectB.x2 && rectA.x2 > rectB.x1 && 2101 | rectA.y1 < rectB.y2 && rectA.y2 > rectB.y1; 2102 | } 2103 | 2104 | function isPolyIntersect(polygon, point) { 2105 | let { x, y } = point; // Koordinat titik 2106 | let vertices = Object.values(polygon); // Array titik poligon 2107 | let intersectCount = 0; // Jumlah interseksi sinar dengan sisi poligon 2108 | 2109 | for (let i = 0; i < vertices.length; i++) { 2110 | let v1 = vertices[i]; 2111 | let v2 = vertices[(i + 1) % vertices.length]; 2112 | 2113 | // Cek apakah garis dari y=mouseY melintasi sisi v1->v2 2114 | if ((v1.y > y) !== (v2.y > y)) { 2115 | let intersectionX = v1.x + ((y - v1.y) * (v2.x - v1.x)) / (v2.y - v1.y); 2116 | if (x < intersectionX) { 2117 | intersectCount++; 2118 | } 2119 | } 2120 | } 2121 | 2122 | // Jika jumlah interseksi ganjil, maka titik berada di dalam poligon 2123 | return intersectCount % 2 !== 0; 2124 | } 2125 | 2126 | function parseKeyCoord(key) { 2127 | var x1, x2, y1, y2, cx, cy; 2128 | var keyChunks, temp; 2129 | 2130 | keyChunks = key.split(';'); 2131 | for (var i = 0; i < 3; i++) { 2132 | temp = keyChunks[i].split(':'); 2133 | if (i == 0) { 2134 | x1 = temp[0]; 2135 | y1 = temp[1]; 2136 | } else if (i == 1) { 2137 | x2 = temp[0]; 2138 | y2 = temp[1]; 2139 | } else { 2140 | cx = temp[0]; 2141 | cy = temp[1]; 2142 | } 2143 | } 2144 | 2145 | return { 2146 | x1: x1, y1: y1, 2147 | x2: x2, y2: y2, 2148 | cx: cx, cy: cy 2149 | }; 2150 | } 2151 | 2152 | // Menggabungkan, jika ada bentuk yang tidak sesuai maka akan dihapus atau diganti. 2153 | function joinShapeTeeth(geoms1, geoms2) { 2154 | var geometry = $.extend(true, {}, geoms1); 2155 | var geom1, geom2; 2156 | for (var keyCoord in geoms2) { 2157 | geom1 = geoms1[keyCoord]; 2158 | geom2 = geoms2[keyCoord]; 2159 | if (geom1 == null) { 2160 | geometry[keyCoord] = geom2; 2161 | } else { 2162 | geometry[keyCoord] = _joinShapeTeeth(geom1, geom2); 2163 | } 2164 | } 2165 | 2166 | return geometry; 2167 | } 2168 | 2169 | // Rules :..( 2170 | function _joinShapeTeeth(geoms1, geoms2) { 2171 | var geom1, geom2; 2172 | var geometry = []; 2173 | for (var y = 0; y < geoms2.length; y++) { 2174 | geom2 = geoms2[y]; 2175 | geometry = [geom2]; 2176 | for (var x = 0; x < geoms1.length; x++) { 2177 | geom1 = geoms1[x]; 2178 | switch (true) { 2179 | case geom2 instanceof AMF: 2180 | switch (true) { 2181 | case geom1 instanceof AMF: 2182 | case geom1 instanceof RCT: 2183 | geometry.push(geom1); 2184 | break; 2185 | } 2186 | break; 2187 | case geom2 instanceof COF: 2188 | switch (true) { 2189 | case geom1 instanceof COF: 2190 | case geom1 instanceof RCT: 2191 | geometry.push(geom1); 2192 | break; 2193 | } 2194 | break; 2195 | case geom2 instanceof FIS: 2196 | switch (true) { 2197 | case geom1 instanceof FIS: 2198 | geometry.push(geom1); 2199 | break; 2200 | } 2201 | break; 2202 | case geom2 instanceof NVT: 2203 | switch (true) { 2204 | case geom1 instanceof NVT: 2205 | geometry.push(geom1); 2206 | break; 2207 | } 2208 | break; 2209 | case geom2 instanceof RCT: 2210 | switch (true) { 2211 | case geom1 instanceof AMF: 2212 | case geom1 instanceof COF: 2213 | case geom1 instanceof POC: 2214 | case geom1 instanceof FMC: 2215 | case geom1 instanceof BRIDGE: 2216 | geometry.push(geom1); 2217 | break; 2218 | } 2219 | break; 2220 | break; 2221 | case geom2 instanceof NON: 2222 | switch (true) { 2223 | case geom1 instanceof NON: 2224 | geometry.push(geom1); 2225 | break; 2226 | } 2227 | break; 2228 | case geom2 instanceof UNE: 2229 | switch (true) { 2230 | case geom1 instanceof UNE: 2231 | geometry.push(geom1); 2232 | break; 2233 | } 2234 | break; 2235 | case geom2 instanceof PRE: 2236 | switch (true) { 2237 | case geom1 instanceof PRE: 2238 | geometry.push(geom1); 2239 | break; 2240 | } 2241 | break; 2242 | case geom2 instanceof ANO: 2243 | switch (true) { 2244 | case geom1 instanceof ANO: 2245 | geometry.push(geom1); 2246 | break; 2247 | } 2248 | break; 2249 | case geom2 instanceof CARIES: 2250 | switch (true) { 2251 | case geom1 instanceof CARIES: 2252 | geometry.push(geom1); 2253 | break; 2254 | } 2255 | break; 2256 | case geom2 instanceof CFR: 2257 | // 2258 | break; 2259 | case geom2 instanceof FMC: 2260 | switch (true) { 2261 | case geom1 instanceof RCT: 2262 | case geom1 instanceof MIS: 2263 | case geom1 instanceof BRIDGE: 2264 | geometry.push(geom1); 2265 | break; 2266 | } 2267 | break; 2268 | case geom2 instanceof POC: 2269 | switch (true) { 2270 | case geom1 instanceof POC: 2271 | case geom1 instanceof IPX: 2272 | case geom1 instanceof RCT: 2273 | case geom1 instanceof MIS: 2274 | case geom1 instanceof BRIDGE: 2275 | geometry.push(geom1); 2276 | break; 2277 | } 2278 | break; 2279 | case geom2 instanceof RRX: 2280 | // 2281 | break; 2282 | case geom2 instanceof MIS: 2283 | switch (true) { 2284 | case geom1 instanceof POC: 2285 | case geom1 instanceof FMC: 2286 | case geom1 instanceof FRM_ACR: 2287 | case geom1 instanceof BRIDGE: 2288 | geometry.push(geom1); 2289 | break; 2290 | } 2291 | break; 2292 | case geom2 instanceof IPX: 2293 | switch (true) { 2294 | case geom1 instanceof POC: 2295 | case geom1 instanceof BRIDGE: 2296 | geometry.push(geom1); 2297 | break; 2298 | } 2299 | break; 2300 | case geom2 instanceof FRM_ACR: 2301 | switch (true) { 2302 | case geom1 instanceof MIS: 2303 | case geom1 instanceof BRIDGE: 2304 | geometry.push(geom1); 2305 | break; 2306 | } 2307 | break; 2308 | case geom2 instanceof BRIDGE: 2309 | switch (true) { 2310 | case geom1 instanceof POC: 2311 | case geom1 instanceof FMC: 2312 | case geom1 instanceof FRM_ACR: 2313 | case geom1 instanceof RCT: 2314 | case geom1 instanceof MIS: 2315 | case geom1 instanceof IPX: 2316 | geometry.push(geom1); 2317 | break; 2318 | } 2319 | break; 2320 | default: 2321 | console.log("DEFAULT[POLYGON]"); 2322 | break; 2323 | } 2324 | } 2325 | } 2326 | 2327 | return geometry; 2328 | } 2329 | 2330 | function getMouse(evt) { 2331 | var offsetX, offsetY; 2332 | if (typeof evt.offsetX != 'undefined') { 2333 | offsetX = evt.offsetX; 2334 | offsetY = evt.offsetY; 2335 | } else if (typeof evt.layerX != 'undefined') { 2336 | offsetX = evt.layerX; 2337 | offsetY = evt.layerY; 2338 | } 2339 | 2340 | return { 'x': offsetX, 'y': offsetY }; 2341 | } 2342 | 2343 | 2344 | // Handlers 2345 | function _on_mouse_move(e) { 2346 | var mouse = getMouse(e); 2347 | var $this = $(e.target); 2348 | var instance = $this.data('odontogram'); 2349 | 2350 | instance.hoverGeoms = []; 2351 | 2352 | var teeth, coord, hoverGeoms; 2353 | for (var keyCoord in instance.teeth) { 2354 | teeth = instance.teeth[keyCoord]; 2355 | coord = parseKeyCoord(keyCoord); 2356 | 2357 | switch (instance.mode) { 2358 | case ODONTOGRAM_MODE_NVT: // Kotak 2359 | case ODONTOGRAM_MODE_RCT: 2360 | case ODONTOGRAM_MODE_NON: 2361 | case ODONTOGRAM_MODE_UNE: 2362 | case ODONTOGRAM_MODE_PRE: 2363 | case ODONTOGRAM_MODE_ANO: 2364 | case ODONTOGRAM_MODE_CFR: 2365 | case ODONTOGRAM_MODE_FMC: 2366 | case ODONTOGRAM_MODE_POC: 2367 | case ODONTOGRAM_MODE_RRX: 2368 | case ODONTOGRAM_MODE_MIS: 2369 | case ODONTOGRAM_MODE_IPX: 2370 | case ODONTOGRAM_MODE_FRM_ACR: 2371 | case ODONTOGRAM_MODE_HAPUS: 2372 | case ODONTOGRAM_MODE_ARROW_TOP_LEFT: 2373 | case ODONTOGRAM_MODE_ARROW_TOP_RIGHT: 2374 | case ODONTOGRAM_MODE_ARROW_TOP_TURN_LEFT: 2375 | case ODONTOGRAM_MODE_ARROW_TOP_TURN_RIGHT: 2376 | case ODONTOGRAM_MODE_ARROW_BOTTOM_LEFT: 2377 | case ODONTOGRAM_MODE_ARROW_BOTTOM_RIGHT: 2378 | case ODONTOGRAM_MODE_ARROW_BOTTOM_TURN_LEFT: 2379 | case ODONTOGRAM_MODE_ARROW_BOTTOM_TURN_RIGHT: 2380 | if (isRectIntersect(coord, { x1: mouse.x, y1: mouse.y, x2: mouse.x, y2: mouse.y })) { 2381 | hoverGeoms = [{ 2382 | vertices: [ 2383 | { x: coord.x1, y: coord.y1 }, 2384 | { x: coord.x2, y: coord.y2 } 2385 | ] 2386 | }]; 2387 | 2388 | instance.hoverGeoms = instance.hoverGeoms.concat(hoverGeoms); 2389 | } 2390 | break; 2391 | case ODONTOGRAM_MODE_BRIDGE: 2392 | if (isRectIntersect(coord, { x1: mouse.x, y1: mouse.y, x2: mouse.x, y2: mouse.y })) { 2393 | hoverGeoms = [ 2394 | { x: coord.x1, y: coord.y1 }, 2395 | { x: coord.x2, y: coord.y2 } 2396 | ]; 2397 | 2398 | if (instance.active_geometry) { 2399 | instance.hoverGeoms = [ 2400 | [instance.active_geometry.startVert, hoverGeoms] 2401 | ]; 2402 | } else { 2403 | instance.hoverGeoms = [ 2404 | [hoverGeoms, null] 2405 | ]; 2406 | } 2407 | } 2408 | break; 2409 | default: // Setiap Bagian 2410 | if (isRectIntersect(coord, { x1: mouse.x, y1: mouse.y, x2: mouse.x, y2: mouse.y })) { 2411 | hoverGeoms = getHoverShapeOnTeeth(mouse, teeth); 2412 | 2413 | instance.hoverGeoms = instance.hoverGeoms.concat(hoverGeoms); 2414 | } 2415 | break; 2416 | } 2417 | 2418 | } 2419 | 2420 | if (instance.hoverGeoms.length > 0) { 2421 | $this.css('cursor', 'pointer'); 2422 | if (instance.mode == ODONTOGRAM_MODE_HAPUS) { 2423 | $this.css('cursor', 'move'); 2424 | } 2425 | } else { 2426 | $this.css('cursor', 'default'); 2427 | } 2428 | 2429 | instance.redraw(); 2430 | } 2431 | 2432 | function _on_mouse_click(e) { 2433 | var mouse = getMouse(e); 2434 | var $this = $(e.target); 2435 | var instance = $this.data('odontogram'); 2436 | 2437 | if (instance.mode == ODONTOGRAM_MODE_DEFAULT) return; 2438 | 2439 | var teeth, coord; 2440 | var tempGeoms = {}; 2441 | var temp; 2442 | for (var keyCoord in instance.teeth) { 2443 | teeth = instance.teeth[keyCoord]; 2444 | coord = parseKeyCoord(keyCoord); 2445 | 2446 | switch (instance.mode) { 2447 | case ODONTOGRAM_MODE_NVT: // Kotak 2448 | case ODONTOGRAM_MODE_RCT: 2449 | case ODONTOGRAM_MODE_NON: 2450 | case ODONTOGRAM_MODE_UNE: 2451 | case ODONTOGRAM_MODE_PRE: 2452 | case ODONTOGRAM_MODE_ANO: 2453 | case ODONTOGRAM_MODE_CFR: 2454 | case ODONTOGRAM_MODE_FMC: 2455 | case ODONTOGRAM_MODE_POC: 2456 | case ODONTOGRAM_MODE_RRX: 2457 | case ODONTOGRAM_MODE_MIS: 2458 | case ODONTOGRAM_MODE_IPX: 2459 | case ODONTOGRAM_MODE_FRM_ACR: 2460 | case ODONTOGRAM_MODE_HAPUS: 2461 | case ODONTOGRAM_MODE_ARROW_TOP_LEFT: 2462 | case ODONTOGRAM_MODE_ARROW_TOP_RIGHT: 2463 | case ODONTOGRAM_MODE_ARROW_TOP_TURN_LEFT: 2464 | case ODONTOGRAM_MODE_ARROW_TOP_TURN_RIGHT: 2465 | case ODONTOGRAM_MODE_ARROW_BOTTOM_LEFT: 2466 | case ODONTOGRAM_MODE_ARROW_BOTTOM_RIGHT: 2467 | case ODONTOGRAM_MODE_ARROW_BOTTOM_TURN_LEFT: 2468 | case ODONTOGRAM_MODE_ARROW_BOTTOM_TURN_RIGHT: 2469 | if (isRectIntersect(coord, { x1: mouse.x, y1: mouse.y, x2: mouse.x, y2: mouse.y })) { 2470 | tempGeoms[keyCoord] = [convertGeom({ 2471 | vertices: [ 2472 | { x: coord.x1, y: coord.y1 }, 2473 | { x: coord.x2, y: coord.y2 } 2474 | ], 2475 | pos: teeth.num 2476 | }, instance.mode)]; 2477 | } 2478 | break; 2479 | case ODONTOGRAM_MODE_BRIDGE: 2480 | if (isRectIntersect(coord, { x1: mouse.x, y1: mouse.y, x2: mouse.x, y2: mouse.y })) { 2481 | tempGeoms[keyCoord] = []; 2482 | if (instance.active_geometry) { 2483 | instance.active_geometry = convertGeom([ 2484 | instance.active_geometry.startVert, 2485 | [ 2486 | { x: coord.x1, y: coord.y1 }, 2487 | { x: coord.x2, y: coord.y2 } 2488 | ] 2489 | ], instance.mode); 2490 | 2491 | tempGeoms[keyCoord].push(instance.active_geometry); 2492 | instance.active_geometry = null; 2493 | } else { 2494 | instance.active_geometry = convertGeom([ 2495 | [ 2496 | { x: coord.x1, y: coord.y1 }, 2497 | { x: coord.x2, y: coord.y2 } 2498 | ], 2499 | null 2500 | ], instance.mode); 2501 | } 2502 | } 2503 | break; 2504 | default: // Setiap Bagian 2505 | if (isRectIntersect(coord, { x1: mouse.x, y1: mouse.y, x2: mouse.x, y2: mouse.y })) { 2506 | tempGeoms[keyCoord] = []; 2507 | temp = getHoverShapeOnTeeth(mouse, teeth); 2508 | for (var i = 0; i < temp.length; i++) { 2509 | temp[i].pos = teeth.num + '-' + temp[i].name.charAt(0).toUpperCase(); 2510 | tempGeoms[keyCoord].push(convertGeom(temp[i], instance.mode)); 2511 | } 2512 | } 2513 | break; 2514 | } 2515 | 2516 | } 2517 | 2518 | if (instance.mode == ODONTOGRAM_MODE_HAPUS) { 2519 | for (var keyCoord in tempGeoms) { 2520 | instance.geometry[keyCoord] = []; 2521 | } 2522 | } else { 2523 | instance.geometry = joinShapeTeeth(instance.geometry, tempGeoms); 2524 | } 2525 | 2526 | $this.trigger('change', [instance.geometry]); 2527 | instance.redraw(); 2528 | } 2529 | 2530 | $.fn.odontogram.defaults = { 2531 | width: "800px", 2532 | height: "480px" 2533 | } 2534 | 2535 | })(jQuery); --------------------------------------------------------------------------------