├── 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 | 
8 | 
9 | 
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 |
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);
--------------------------------------------------------------------------------