1const directionVerbose = false; 2const DIRECTION_TAG_NAME = 'direction-display'; 3/* 4* See custom properties in CSS. 5* ============================= 6* @see https://developer.mozilla.org/en-US/docs/Web/CSS/ 7* Relies on those elements: 8* 9.xxxxxxxx { 10 --bg-color: rgba(0, 0, 0, 0); 11 --digit-color: black; 12 --with-gradient: true; 13 --display-background-gradient-from: LightGrey; 14 --display-background-gradient-to: white; 15 --display-line-color: rgba(255, 255, 255, 0.5); 16 --label-fill-color: rgba(255, 255, 255, 0.5); 17 --with-display-shadow: false; 18 --shadow-color: rgba(0, 0, 0, 0.75); 19 --outline-color: DarkGrey; 20 --major-tick-color: black; 21 --minor-tick-color: black; 22 --value-color: grey; 23 --value-outline-color: black; 24 --value-nb-decimal: 1; 25 --hand-color: red; 26 --hand-outline-color: black; 27 --with-hand-shadow: true; 28 --knob-color: DarkGrey; 29 --knob-outline-color: black; 30 --font: Arial; 31} 32*/ 33 34/** 35 * Recurse from the top down, on styleSheets and cssRules 36 * 37 * document.styleSheets[0].cssRules[2].selectorText returns ".analogdisplay" 38 * document.styleSheets[0].cssRules[2].cssText returns ".analogdisplay { --hand-color: red; --face-color: white; }" 39 * document.styleSheets[0].cssRules[2].style.cssText returns "--hand-color: red; --face-color: white;" 40 * 41 * spine-case to camelCase 42 */ 43const defaultDirectionColorConfig = { 44 bgColor: 'rgba(0, 0, 0, 0)', /* transparent, 'white', */ 45 digitColor: 'black', 46 withGradient: true, 47 displayBackgroundGradient: { 48 from: 'LightGrey', 49 to: 'white' 50 }, 51 displayLineColor: 'rgba(0, 0, 0, 0.5)', 52 labelFillColor: 'rgba(255, 255, 255, 0.5)', 53 withDisplayShadow: true, 54 shadowColor: 'rgba(0, 0, 0, 0.75)', 55 outlineColor: 'DarkGrey', 56 majorTickColor: 'black', 57 minorTickColor: 'black', 58 valueColor: 'grey', 59 valueOutlineColor: 'black', 60 valueNbDecimal: 1, 61 handColor: 'red', // 'rgba(0, 0, 100, 0.25)', 62 handOutlineColor: 'black', 63 withHandShadow: true, 64 knobColor: 'DarkGrey', 65 knobOutlineColor: 'black', 66 font: 'Arial' /* 'Source Code Pro' */ 67}; 68 69// import * as Utilities from "../utilities/Utilities.js"; 70 71/* global HTMLElement */ 72class DirectionDisplay extends HTMLElement { 73 74 static get observedAttributes() { 75 return [ 76 "width", // Integer. Canvas width 77 "height", // Integer. Canvas height 78 "major-ticks", // Float. value between major ticks (those with labels) 79 "minor-ticks", // Float. value between minor ticks 80 "with-rose", // Boolean, draw the rose or not 81 "with-border", // Boolean 82 "label", // String 83 "hand", // String. 'regular' (default, needle) or 'wind', "boat", "cata", "tri", "plane", "arrow" 84 "value" // Float. Heading to display 85 ]; 86 } 87 88 constructor() { 89 super(); 90 this._shadowRoot = this.attachShadow({mode: 'open'}); // 'open' means it is accessible from external JavaScript. 91 // create and append a <canvas> 92 this.canvas = document.createElement("canvas"); 93 let fallbackElemt = document.createElement("h1"); 94 let content = document.createTextNode("This is a DirectionDisplay, on an HTML5 canvas"); 95 fallbackElemt.appendChild(content); 96 this.canvas.appendChild(fallbackElemt); 97 this.shadowRoot.appendChild(this.canvas); 98 99 // Default values 100 this._value = 0; 101 this._width = 150; 102 this._height = 150; 103 this._major_ticks = 45; 104 this._minor_ticks = 5; 105 this._with_rose = true; 106 this._with_border = true; 107 this._hand = 'regular'; 108 this._label = undefined; 109 110 this._previousClassName = ""; 111 this.directionColorConfig = defaultDirectionColorConfig; // Init 112 113 if (directionVerbose) { 114 console.log("Data in Constructor:", this._value); 115 } 116 } 117 118 // Called whenever the custom element is inserted into the DOM. 119 connectedCallback() { 120 if (directionVerbose) { 121 console.log("connectedCallback invoked, 'value' is [", this.value, "]"); 122 } 123 } 124 125 // Called whenever the custom element is removed from the DOM. 126 disconnectedCallback() { 127 if (directionVerbose) { 128 console.log("disconnectedCallback invoked"); 129 } 130 } 131 132 // Called whenever an attribute is added, removed or updated. 133 // Only attributes listed in the observedAttributes property are affected. 134 attributeChangedCallback(attrName, oldVal, newVal) { 135 if (directionVerbose) { 136 console.log("attributeChangedCallback invoked on " + attrName + " from " + oldVal + " to " + newVal); 137 } 138 switch (attrName) { 139 case "value": 140 this._value = parseFloat(newVal); 141 break; 142 case "width": 143 this._width = parseInt(newVal); 144 break; 145 case "height": 146 this._height = parseInt(newVal); 147 break; 148 case "major-ticks": 149 this._major_ticks = parseFloat(newVal); 150 break; 151 case "minor-ticks": 152 this._minor_ticks = parseFloat(newVal); 153 break; 154 case "with-rose": 155 this._with_rose = ("true" === newVal); 156 break; 157 case "with-border": 158 this._with_border = ("true" === newVal); 159 break; 160 case "label": 161 this._label = newVal; 162 break; 163 case "hand": 164 this._hand = (newVal ? newVal : 'regular'); 165 break; 166 default: 167 break; 168 } 169 this.repaint(); 170 } 171 172 // Called whenever the custom element has been moved into a new document. 173 adoptedCallback() { 174 if (directionVerbose) { 175 console.log("adoptedCallback invoked"); 176 } 177 } 178 179 set value(option) { 180 this.setAttribute("value", option); 181 if (directionVerbose) { 182 console.log(">> Value option:", option); 183 } 184// this.repaint(); 185 } 186 set width(val) { 187 this.setAttribute("width", val); 188 } 189 set height(val) { 190 this.setAttribute("height", val); 191 } 192 set majorTicks(val) { 193 this.setAttribute("major-ticks", val); 194 } 195 set minorTicks(val) { 196 this.setAttribute("minor-ticks", val); 197 } 198 set withRose(val) { 199 this.setAttribute("with-rose", val); 200 } 201 set withBorder(val) { 202 this.setAttribute("with-border", val); 203 } 204 set label(val) { 205 this.setAttribute("label", val); 206 } 207 set hand(val) { 208 this.setAttribute("hand", val); 209 } 210 set shadowRoot(val) { 211 this._shadowRoot = val; 212 } 213 214 get value() { 215 return this._value; 216 } 217 get width() { 218 return this._width; 219 } 220 get height() { 221 return this._height; 222 } 223 get minorTicks() { 224 return this._minor_ticks; 225 } 226 get majorTicks() { 227 return this._major_ticks; 228 } 229 get withRose() { 230 return this._with_rose; 231 } 232 get withBorder() { 233 return this._with_border; 234 } 235 get label() { 236 return this._label; 237 } 238 get hand() { 239 return this._hand; 240 } 241 get shadowRoot() { 242 return this._shadowRoot; 243 } 244 245 // Component methods 246 repaint() { 247 this.drawDisplay(this._value); 248 } 249 250 getColorConfig(classNames) { 251 let colorConfig = defaultDirectionColorConfig; 252 let classes = classNames.split(" "); 253 for (let cls=0; cls<classes.length; cls++) { 254 let className = classes[cls]; 255 for (let s=0; s<document.styleSheets.length; s++) { 256 // console.log("Walking though ", document.styleSheets[s]);
257 try { 258 for (let r = 0; document.styleSheets[s].cssRules !== null && r < document.styleSheets[s].cssRules.length; r++) { 259 let selector = document.styleSheets[s].cssRules[r].selectorText; 260 // console.log(">>> ", selector); 261 if (selector !== undefined && (selector === '.' + className || (selector.indexOf('.' + className) > -1 && selector.indexOf(DIRECTION_TAG_NAME) > -1))) { // Cases like "tag-name .className" 262 // console.log(" >>> Found it! [%s]", selector); 263 let cssText = document.styleSheets[s].cssRules[r].style.cssText; 264 let cssTextElems = cssText.split(";"); 265 cssTextElems.forEach((elem) => { 266 if (elem.trim().length > 0) { 267 let keyValPair = elem.split(":"); 268 let key = keyValPair[0].trim(); 269 let value = keyValPair[1].trim(); 270 switch (key) { 271 case '--bg-color': 272 colorConfig.bgColor = value; 273 break; 274 case '--digit-color': 275 colorConfig.digitColor = value; 276 break; 277 case '--with-gradient': 278 colorConfig.withGradient = (value === 'true'); 279 break; 280 case '--display-background-gradient-from': 281 colorConfig.displayBackgroundGradient.from = value; 282 break; 283 case '--display-background-gradient-to': 284 colorConfig.displayBackgroundGradient.to = value; 285 break; 286 case '--display-line-color': 287 colorConfig.displayLineColor = value; 288 break; 289 case '--label-fill-color': 290 colorConfig.labelFillColor = value; 291 break; 292 case '--with-display-shadow': 293 colorConfig.withDisplayShadow = (value === 'true'); 294 break; 295 case '--shadow-color': 296 colorConfig.shadowColor = value; 297 break; 298 case '--outline-color': 299 colorConfig.outlineColor = value; 300 break; 301 case '--major-tick-color': 302 colorConfig.majorTickColor = value; 303 break; 304 case '--minor-tick-color': 305 colorConfig.minorTickColor = value; 306 break; 307 case '--value-color': 308 colorConfig.valueColor = value; 309 break; 310 case '--value-outline-color': 311 colorConfig.valueOutlineColor = value; 312 break; 313 case '--value-nb-decimal': 314 colorConfig.valueNbDecimal = value; 315 break; 316 case '--hand-color': 317 colorConfig.handColor = value; 318 break; 319 case '--hand-outline-color': 320 colorConfig.handOutlineColor = value; 321 break; 322 case '--with-hand-shadow': 323 colorConfig.withHandShadow = (value === 'true'); 324 break; 325 case '--knob-color': 326 colorConfig.knobColor = value; 327 break; 328 case '--knob-outline-color': 329 colorConfig.knobOutlineColor = value; 330 break; 331 case '--font': 332 colorConfig.font = value; 333 break; 334 default: 335 break; 336 } 337 } 338 }); 339 } 340 } 341 } catch (err) { 342 // Absorb 343 } 344 } 345 } 346 return colorConfig; 347 } 348 349 drawDisplay(directionValue) { 350 351 let currentStyle = this.className; 352 if (this._previousClassName !== currentStyle || true) { 353 // Reload 354 // console.log("Reloading CSS"); 355 try { 356 this.directionColorConfig = this.getColorConfig(currentStyle); 357 } catch (err) { 358 // Absorb? 359 console.log(err); 360 } 361 this._previousClassName = currentStyle; 362 } 363 364 let digitColor = this.directionColorConfig.digitColor; 365 366 let context = this.canvas.getContext('2d'); 367 context.clearRect(0, 0, this.width, this.height); 368 369 let radius = (Math.min(this.width, this.height) / 2) * 0.90; 370 371 // Set the canvas size from its container. 372 this.canvas.width = this.width; 373 this.canvas.height = this.height; 374 375 // Cleanup 376 context.fillStyle = this.directionColorConfig.bgColor; 377 context.fillRect(0, 0, this.canvas.width, this.canvas.height); 378 379 context.beginPath(); 380 if (this.withBorder === true) { 381 //context.arc(x, y, radius, startAngle, startAngle + Math.PI, antiClockwise); 382 context.arc(this.canvas.width / 2, radius + 10, radius, 0, 2 * Math.PI, false); 383 context.lineWidth = 5; 384 } 385 if (this.directionColorConfig.withGradient) { 386 let grd = context.createLinearGradient(0, 5, 0, radius); 387 grd.addColorStop(0, this.directionColorConfig.displayBackgroun
387dGradient.from);// 0 Beginning 388 grd.addColorStop(1, this.directionColorConfig.displayBackgroundGradient.to); // 1 End 389 context.fillStyle = grd; 390 } else { 391 context.fillStyle = this.directionColorConfig.displayBackgroundGradient.to; 392 } 393 394 if (this.directionColorConfig.withDisplayShadow) { 395 context.shadowOffsetX = 3; 396 context.shadowOffsetY = 3; 397 context.shadowBlur = 3; 398 context.shadowColor = this.directionColorConfig.shadowColor; 399 } else { 400 context.shadowOffsetX = 0; 401 context.shadowOffsetY = 0; 402 context.shadowBlur = 0; 403 context.shadowColor = undefined; 404 } 405 context.lineJoin = "round"; 406 context.fill(); 407 context.strokeStyle = this.directionColorConfig.outlineColor; 408 context.stroke(); 409 context.closePath(); 410 411 // Major Ticks 412 context.beginPath(); 413 for (let i = 0; i < 360; i += this.majorTicks) { 414 let xFrom = (this.canvas.width / 2) - ((radius * 0.95) * Math.cos(2 * Math.PI * (i / 360))); 415 let yFrom = (radius + 10) - ((radius * 0.95) * Math.sin(2 * Math.PI * (i / 360))); 416 let xTo = (this.canvas.width / 2) - ((radius * 0.85) * Math.cos(2 * Math.PI * (i / 360))); 417 let yTo = (radius + 10) - ((radius * 0.85) * Math.sin(2 * Math.PI * (i / 360))); 418 context.moveTo(xFrom, yFrom); 419 context.lineTo(xTo, yTo); 420 } 421 context.lineWidth = 3; 422 context.strokeStyle = this.directionColorConfig.majorTickColor; 423 context.stroke(); 424 context.closePath(); 425 426 // Minor Ticks 427 if (this.minorTicks > 0) { 428 context.beginPath(); 429 for (let i = 0; i <= 360; i += this.minorTicks) { 430 let xFrom = (this.canvas.width / 2) - ((radius * 0.95) * Math.cos(2 * Math.PI * (i / 360))); 431 let yFrom = (radius + 10) - ((radius * 0.95) * Math.sin(2 * Math.PI * (i / 360))); 432 let xTo = (this.canvas.width / 2) - ((radius * 0.90) * Math.cos(2 * Math.PI * (i / 360))); 433 let yTo = (radius + 10) - ((radius * 0.90) * Math.sin(2 * Math.PI * (i / 360))); 434 context.moveTo(xFrom, yFrom); 435 context.lineTo(xTo, yTo); 436 } 437 context.lineWidth = 1; 438 context.strokeStyle = this.directionColorConfig.minorTickColor; 439 context.stroke(); 440 context.closePath(); 441 } 442 443 // with rose? 444 if (this.withRose === true) { 445 context.beginPath(); 446 447 context.lineWidth = 1; 448 let outsideRadius = radius * 0.6; 449 let insideRadius = radius * 0.1; 450 451// context.arc(canvas.width / 2, radius + 10, outsideRadius, 0, 2 * Math.PI, false); 452// context.arc(canvas.width / 2, radius + 10, insideRadius, 0, 2 * Math.PI, false); 453 454 // NS/EW axis, the origin is -90 (W) 455 let N = (0 + 90) % 360; 456 let S = (180 + 90) % 360; 457 let E = (90 + 90) % 360; 458 let W = (270 + 90) % 360; 459 460 let NE = (45 + 90) % 360; 461 let SE = (135 + 90) % 360; 462 let NW = (315 + 90) % 360; 463 let SW = (225 + 90) % 360; 464 465 // N-S 466 let xFrom = (this.canvas.width / 2) - (outsideRadius * Math.cos(2 * Math.PI * (N / 360))); 467 let yFrom = (radius + 10) - (outsideRadius * Math.sin(2 * Math.PI * (N / 360))); 468 let xTo = (this.canvas.width / 2) - (outsideRadius * Math.cos(2 * Math.PI * (S / 360))); 469 let yTo = (radius + 10) - (outsideRadius * Math.sin(2 * Math.PI * (S / 360))); 470 context.moveTo(xFrom, yFrom); 471 context.lineTo(xTo, yTo); 472 // E-W 473 xFrom = (this.canvas.width / 2) - (outsideRadius * Math.cos(2 * Math.PI * (E / 360))); 474 yFrom = (radius + 10) - (outsideRadius * Math.sin(2 * Math.PI * (E / 360))); 475 xTo = (this.canvas.width / 2) - (outsideRadius * Math.cos(2 * Math.PI * (W / 360))); 476 yTo = (radius + 10) - (outsideRadius * Math.sin(2 * Math.PI * (W / 360))); 477 context.moveTo(xFrom, yFrom); 478 context.lineTo(xTo, yTo); 479 // NE-SW 480 xFrom = (this.canvas.width / 2) - (outsideRadius * 0.9 * Math.cos(2 * Math.PI * (NE / 360))); 481 yFrom = (radius + 10) - (outsideRadius * 0.9 * Math.sin(2 * Math.PI * (NE / 360))); 482 xTo = (this.canvas.width / 2) - (outsideRadius * 0.9 * Math.cos(2 * Math.PI * (SW / 360))); 483 yTo = (radius + 10) - (outsideRadius * 0.9 * Math.sin(2 * Math.PI * (SW / 360))); 484 context.moveTo(xFrom, yFrom); 485 context.lineTo(xTo, yTo); 486 // NW-SE 487 xFrom = (this.canvas.width / 2) - (outsideRadius * 0.9 * Math.cos(2 * Math.PI * (NW / 360))); 488 yFrom = (radius + 10) - (outsideRadius * 0.9 * Math.sin(2 * Math.PI * (NW / 360)));
489 xTo = (this.canvas.width / 2) - (outsideRadius * 0.9 * Math.cos(2 * Math.PI * (SE / 360))); 490 yTo = (radius + 10) - (outsideRadius * 0.9 * Math.sin(2 * Math.PI * (SE / 360))); 491 context.moveTo(xFrom, yFrom); 492 context.lineTo(xTo, yTo); 493 494 this.drawSpike(radius, outsideRadius, insideRadius, N, context); 495 this.drawSpike(radius, outsideRadius, insideRadius, S, context); 496 this.drawSpike(radius, outsideRadius, insideRadius, E, context); 497 this.drawSpike(radius, outsideRadius, insideRadius, W, context); 498 499 this.drawSpike(radius, outsideRadius * 0.9, insideRadius, NE, context); 500 this.drawSpike(radius, outsideRadius * 0.9, insideRadius, SE, context); 501 this.drawSpike(radius, outsideRadius * 0.9, insideRadius, SW, context); 502 this.drawSpike(radius, outsideRadius * 0.9, insideRadius, NW, context); 503 504 context.strokeStyle = this.directionColorConfig.displayLineColor; 505 context.stroke(); 506 context.closePath(); 507 } 508 509 // Numbers 510 context.beginPath(); 511 let scale = 1; 512 for (let i = 0; i < 360; i += this.majorTicks) { 513 context.save(); 514 context.translate(this.canvas.width / 2, (radius + 10)); // canvas.height); 515 context.rotate((2 * Math.PI * (i / 360))); 516 context.font = "bold " + Math.round(scale * 15) + "px Arial"; // Like "bold 15px Arial" 517 context.fillStyle = digitColor; 518 let str = i.toString(); 519 let len = context.measureText(str).width; 520 context.fillText(str, -len / 2, (-(radius * .8) + 10)); 521 context.lineWidth = 1; 522 context.strokeStyle = this.directionColorConfig.valueOutlineColor; 523 context.strokeText(str, -len / 2, (-(radius * .8) + 10)); // Outlined 524 context.restore(); 525 } 526 context.closePath(); 527 // Value 528 let text; 529 let dv = parseFloat(directionValue); 530 while (dv > 360) dv -= 360; 531 while (dv < 0) dv += 360; 532 try { 533 text = dv.toFixed(this.directionColorConfig.valueNbDecimal); 534 } catch (err) { 535 console.log(err); 536 } 537 let len = 0; 538 context.font = "bold " + Math.round(scale * 40) + "px " + this.directionColorConfig.font; // "bold 40px Arial" 539 let metrics = context.measureText(text); 540 len = metrics.width; 541 542 context.beginPath(); 543 context.fillStyle = this.directionColorConfig.valueColor; 544 context.fillText(text, (this.canvas.width / 2) - (len / 2), ((radius * .75) + 10)); 545 context.lineWidth = 1; 546 context.strokeStyle = this.directionColorConfig.valueOutlineColor; 547 context.strokeText(text, (this.canvas.width / 2) - (len / 2), ((radius * .75) + 10)); // Outlined 548 context.closePath(); 549 550 // Label ? 551 if (this.label !== undefined) { 552 let fontSize = 20; 553 let text = this.label; 554 let len = 0; 555 context.font = "bold " + Math.round(scale * fontSize) + "px " + this.directionColorConfig.font; // "bold 40px Arial" 556 let metrics = context.measureText(text); 557 len = metrics.width; 558 559 context.beginPath(); 560 context.fillStyle = this.directionColorConfig.labelFillColor; 561 context.fillText(text, (this.canvas.width / 2) - (len / 2), (2 * radius - (fontSize * scale * 2.1))); 562 context.lineWidth = 1; 563 context.strokeStyle = this.directionColorConfig.valueOutlineColor; 564 context.strokeText(text, (this.canvas.width / 2) - (len / 2), (2 * radius - (fontSize * scale * 2.1))); // Outlined 565 context.closePath(); 566 } 567 568 // Hand 569 context.beginPath(); 570 if (this.directionColorConfig.withHandShadow) { 571 context.shadowColor = this.directionColorConfig.shadowColor; 572 context.shadowOffsetX = 3; 573 context.shadowOffsetY = 3; 574 context.shadowBlur = 3; 575 } 576 // Center 577 let centerX = this.canvas.width / 2; 578 let centerY = radius + 10; 579 context.moveTo(centerX, centerY); 580 581 // console.log(`This.hand=${this.hand}`); 582 let x; 583 let y; 584 if (this.hand === 'regular' || this.hand === 'wind') { 585 // Left 586 x = centerX - ((radius * 0.05) * Math.cos(Math.toRadians(dv))); // - (Math.PI / 2)))); 587 y = centerY - ((radius * 0.05) * Math.sin(Math.toRadians(dv))); // - (Math.PI / 2)))); 588 context.lineTo(x, y); 589 } 590 if (this.hand === 'regular') { // Regular needle 591 // Tip 592 x = centerX - ((radius * 0.90) * Math.cos(Math.toRadians(dv) + (Math.PI / 2))); 593 y = centerY - ((radius * 0.90) * Math.sin(Math.toRadians(dv) + (Math.PI / 2))); 594 context.lineTo(x, y); 595 } else if (this.hand === 'wind') { // Then draw wind arrow 596 /* 597 +-+ 598 | | 599 | | 600 +-+ +-+ 601 \ / 602 + + 603 | | 604 | |
605 +-+ 606 */ 607 608 let arrowPoints = [ 609 { x: - radius * 0.04, y: - radius * 0.30 }, // Left pointy side of the arrow head 610 { x: - radius * 0.20, y: - radius * 0.60 }, // Left back fat side of the arrow head 611 { x: - radius * 0.04, y: - radius * 0.60 }, // Left back narrow side of the arrow head 612 { x: - radius * 0.04, y: - radius * 0.90 }, // Left tip 613 { x: + radius * 0.04, y: - radius * 0.90 }, // Right tip 614 { x: + radius * 0.04, y: - radius * 0.60 }, // Right back narrow side of the arrow head 615 { x: + radius * 0.20, y: - radius * 0.60 }, // Right back fat side of the arrow head 616 { x: + radius * 0.04, y: - radius * 0.30 } // Right pointy side of the arrow head 617 ]; 618 let radAngle = Math.toRadians(dv); // + (Math.PI / 2); 619 // Apply rotation to the points of the needle 620 arrowPoints.forEach(pt => { 621 x = centerX + ((pt.x * Math.cos(radAngle)) - (pt.y * Math.sin(radAngle))); 622 y = centerY + ((pt.x * Math.sin(radAngle)) + (pt.y * Math.cos(radAngle))); 623 context.lineTo(x, y); 624 }); 625 } else if (this.hand === 'boat') { 626 // console.log("Drawing boat needle...") 627 const BOAT_FACT = 0.8; 628 if (false) { 629 let boatPoints = [ 630 { x: 0, y: - radius * BOAT_FACT * 0.6 }, // Bow 631 { x: radius * BOAT_FACT * 0.22, y: - radius * BOAT_FACT * 0.3 }, // Starboard, point one 632 { x: radius * BOAT_FACT * 0.30, y: - radius * BOAT_FACT * 0.0 }, // Starboard, point two 633 { x: radius * BOAT_FACT * 0.28, y: + radius * BOAT_FACT * 0.3 }, // Starboard, point three 634 { x: radius * BOAT_FACT * 0.20, y: + radius * BOAT_FACT * 0.6 }, // Starboard, point four 635 { x: BOAT_FACT * 0, y: + radius * BOAT_FACT * 0.6 }, // Mid-transom 636 { x: - radius * BOAT_FACT * 0.20, y: + radius * BOAT_FACT * 0.6 }, // Port, point four 637 { x: - radius * BOAT_FACT * 0.28, y: + radius * BOAT_FACT * 0.3 }, // Port, point three 638 { x: - radius * BOAT_FACT * 0.30, y: - radius * BOAT_FACT * 0.0 }, // Port, point two 639 { x: - radius * BOAT_FACT * 0.22, y: - radius * BOAT_FACT * 0.3 }, // Port, point one 640 { x: BOAT_FACT * 0, y: - radius * BOAT_FACT * 0.6 } // Bow 641 ]; 642 let radAngle = Math.toRadians(dv); // + (Math.PI / 2); 643 // Apply rotation to the points of the needle 644 boatPoints.forEach(pt => { 645 x = centerX + ((pt.x * Math.cos(radAngle)) - (pt.y * Math.sin(radAngle))); 646 y = centerY + ((pt.x * Math.sin(radAngle)) + (pt.y * Math.cos(radAngle))); 647 context.lineTo(x, y); 648 }); 649 } else { 650 const BEAM_ANGLE = 90; 651 const TRANSOM_ANGLE = 165; 652 const SCALE = 0.8; 653 let boatRadius = radius * BOAT_FACT * SCALE; 654 let beam = radius * 0.70 * BOAT_FACT * SCALE; // 90 degrees 655 let transom = radius * BOAT_FACT * 1.05 * SCALE; // length from center to transom, 165 degrees 656 657 let bow = { x: centerX + (boatRadius * Math.sin(Math.toRadians(dv))), 658 y: centerY - (boatRadius * Math.cos(Math.toRadians(dv))) }; 659 660 let beam_right = { x: centerX + (beam * Math.sin(Math.toRadians(dv + BEAM_ANGLE))), 661 y: centerY - (beam * Math.cos(Math.toRadians(dv + BEAM_ANGLE))) }; 662 663 let transom_right = { x: centerX + (transom * Math.sin(Math.toRadians(dv + TRANSOM_ANGLE))), 664 y: centerY - (transom * Math.cos(Math.toRadians(dv + TRANSOM_ANGLE))) }; 665 666 let beam_left = { x: centerX + (beam * Math.sin(Math.toRadians(dv - BEAM_ANGLE))), 667 y: centerY - (beam * Math.cos(Math.toRadians(dv - BEAM_ANGLE))) }; 668 669 let transom_left = { x: centerX + (transom * Math.sin(Math.toRadians(dv - TRANSOM_ANGLE))), 670 y: centerY - (transom * Math.cos(Math.toRadians(dv - TRANSOM_ANGLE))) }; 671 672 // Starboard 673 context.lineTo(bow.x, bow.y); // Center to bow. 674 // context.moveTo(bow.x, bow.y); // Bow 675 context.quadraticCurveTo(beam_right.x, 676 beam_right.y, 677 transom_right.x, 678 transom_right.y); 679 // Port 680 context.moveTo(bow.x, bow.y); 681 context.quadraticCurveTo(beam_left.x, beam_left.y, 682 transom_left.x, transom_left.y); 683 684 // Transom 685 context.moveTo(transom_left.x, transom_left.y); 686 context.lineTo(transom_right.x, transom_right.y); 687 } 688 } else if (this.hand === 'cata') { 689 // console.log("Drawing cata needle...") 690 const BOAT_FACT = 0.8; 691 let cataPoints = [
692 { x: 0, y: - radius * BOAT_FACT * 0.142 }, // Arm, front, center 693 /* STARBOARD HULL */ 694 // Starboard 695 { x: radius * BOAT_FACT * 0.142, y: - radius * BOAT_FACT * 0.142 }, // Arm starboard, hull side 696 { x: radius * BOAT_FACT * 0.2143, y: - radius * BOAT_FACT * 0.5714 }, // Starboard bow 697 { x: radius * BOAT_FACT * 0.284, y: - radius * BOAT_FACT * 0.142 }, 698 { x: radius * BOAT_FACT * 0.284, y: radius * BOAT_FACT * 0 }, 699 { x: radius * BOAT_FACT * 0.284, y: radius * BOAT_FACT * 0.142 }, 700 { x: radius * BOAT_FACT * 0.2571, y: radius * BOAT_FACT * 0.4286 }, // Starboard transom, ext 701 { x: radius * BOAT_FACT * 0.1714, y: radius * BOAT_FACT * 0.4286 }, // Starboard transom, int 702 { x: radius * BOAT_FACT * 0.142, y: radius * BOAT_FACT * 0.142 }, // Arm, back, starboard, hull side 703 { x: radius * BOAT_FACT * 0, y: radius * BOAT_FACT * 0.142 }, // Arm, back, starboard, center 704 // Port 705 { x: radius * BOAT_FACT * 0, y: radius * BOAT_FACT * 0.142 }, 706 { x: radius * BOAT_FACT * 0.142, y: radius * BOAT_FACT * 0.142 }, 707 { x: radius * BOAT_FACT * 0.1714, y: radius * BOAT_FACT * 0.4286 }, 708 { x: radius * BOAT_FACT * 0.2571, y: radius * BOAT_FACT * 0.4286 }, 709 { x: radius * BOAT_FACT * 0.284, y: radius * BOAT_FACT * 0.142 }, 710 { x: radius * BOAT_FACT * 0.284, y: radius * BOAT_FACT * 0 }, 711 { x: radius * BOAT_FACT * 0.284, y: - radius * BOAT_FACT * 0.142 }, 712 { x: radius * BOAT_FACT * 0.2143, y: - radius * BOAT_FACT * 0.5714 }, 713 { x: radius * BOAT_FACT * 0.142, y: - radius * BOAT_FACT * 0.142 }, 714 /* PORT HULL */ 715 // Starboard 716 { x: - radius * BOAT_FACT * 0.142, y: - radius * BOAT_FACT * 0.142 }, // Arm starboard, hull side 717 { x: - radius * BOAT_FACT * 0.2143, y: - radius * BOAT_FACT * 0.5714 }, // Starboard bow 718 { x: - radius * BOAT_FACT * 0.284, y: - radius * BOAT_FACT * 0.142 }, 719 { x: - radius * BOAT_FACT * 0.284, y: radius * BOAT_FACT * 0 }, 720 { x: - radius * BOAT_FACT * 0.284, y: radius * BOAT_FACT * 0.142 }, 721 { x: - radius * BOAT_FACT * 0.2571, y: radius * BOAT_FACT * 0.4286 }, // Starboard transom, ext 722 { x: - radius * BOAT_FACT * 0.1714, y: radius * BOAT_FACT * 0.4286 }, // Starboard transom, int 723 { x: - radius * BOAT_FACT * 0.142, y: radius * BOAT_FACT * 0.142 }, // Arm, back, starboard, hull side 724 { x: - radius * BOAT_FACT * 0, y: radius * BOAT_FACT * 0.142 }, // Arm, back, starboard, center 725 // Port 726 { x: - radius * BOAT_FACT * 0, y: radius * BOAT_FACT * 0.142 }, 727 { x: - radius * BOAT_FACT * 0.142, y: radius * BOAT_FACT * 0.142 }, 728 { x: - radius * BOAT_FACT * 0.1714, y: radius * BOAT_FACT * 0.4286 }, 729 { x: - radius * BOAT_FACT * 0.2571, y: radius * BOAT_FACT * 0.4286 }, 730 { x: - radius * BOAT_FACT * 0.284, y: radius * BOAT_FACT * 0.142 }, 731 { x: - radius * BOAT_FACT * 0.284, y: radius * BOAT_FACT * 0 }, 732 { x: - radius * BOAT_FACT * 0.284, y: - radius * BOAT_FACT * 0.142 }, 733 { x: - radius * BOAT_FACT * 0.2143, y: - radius * BOAT_FACT * 0.5714 }, 734 { x: - radius * BOAT_FACT * 0.142, y: - radius * BOAT_FACT * 0.142 }, 735 736 { x: 0, y: - radius * BOAT_FACT * 0.142 } 737 ]; 738 let radAngle = Math.toRadians(dv); // + (Math.PI / 2); 739 // Apply rotation to the points of the needle 740 cataPoints.forEach(pt => { 741 x = centerX + ((pt.x * Math.cos(radAngle)) - (pt.y * Math.sin(radAngle))); 742 y = centerY + ((pt.x * Math.sin(radAngle)) + (pt.y * Math.cos(radAngle))); 743 context.lineTo(x, y); 744 }); 745 } else if (this.hand === 'tri') { 746 // console.log("Drawing tri needle...") 747 const BOAT_FACT = 0.8; 748 let triPoints = [ 749 { x: 0, y: - radius * BOAT_FACT * 0.5714}, // Bow, center hull 750 // Starboard 751 { x: radius * BOAT_FACT * 0.04286, y: - radius * BOAT_FACT * 0.4286 }, 752 { x: radius * BOAT_FACT * 0.08571, y: - radius * BOAT_FACT * 0.1429}, // Arm, front, starboard, inside 753 { x: radius * BOAT_FACT * 0.2286, y: - radius * BOAT_FACT * 0.1429}, // Arm, front, starboard, outside 754 { x: radius * BOAT_FACT * 0.2571, y: - radius * BOAT_FACT * 0.3714 }, // Outrigger bow
755 { x: radius * BOAT_FACT * 0.2857, y: - radius * BOAT_FACT * 0.2143 }, 756 { x: radius * BOAT_FACT * 0.2857, y: radius * BOAT_FACT * 0.2143 }, 757 { x: radius * BOAT_FACT * 0.2714, y: radius * BOAT_FACT * 0.3571 }, // Outrigger transom, ext 758 { x: radius * BOAT_FACT * 0.2429, y: radius * BOAT_FACT * 0.3571 }, // Outrigger transom, int 759 { x: radius * BOAT_FACT * 0.2286, y: radius * BOAT_FACT * 0.1429}, // Arm, back, starboard, outside 760 { x: radius * BOAT_FACT * 0.08571, y: radius * BOAT_FACT * 0.1429}, // Arm, back, starboard, inside 761 { x: radius * BOAT_FACT * 0.04286, y: radius * BOAT_FACT * 0.4286 }, // Main hull, transom starboard, 762 // Port 763 { x: - radius * BOAT_FACT * 0.04286, y: radius * BOAT_FACT * 0.4286 }, 764 { x: - radius * BOAT_FACT * 0.08571, y: radius * BOAT_FACT * 0.1429}, 765 { x: - radius * BOAT_FACT * 0.2286, y: radius * BOAT_FACT * 0.1429}, 766 { x: - radius * BOAT_FACT * 0.2429, y: radius * BOAT_FACT * 0.3571 }, 767 { x: - radius * BOAT_FACT * 0.2714, y: radius * BOAT_FACT * 0.3571 }, 768 { x: - radius * BOAT_FACT * 0.2857, y: radius * BOAT_FACT * 0.2143 }, 769 { x: - radius * BOAT_FACT * 0.2857, y: - radius * BOAT_FACT * 0.2143 }, 770 { x: - radius * BOAT_FACT * 0.2571, y: - radius * BOAT_FACT * 0.3714 }, 771 { x: - radius * BOAT_FACT * 0.2286, y: - radius * BOAT_FACT * 0.1429}, 772 { x: - radius * BOAT_FACT * 0.08571, y: - radius * BOAT_FACT * 0.1429}, 773 { x: - radius * BOAT_FACT * 0.04286, y: - radius * BOAT_FACT * 0.4286 }, 774 775 { x: 0, y: - radius * BOAT_FACT * 0.5714}, // Bow, center hull 776 ]; 777 let radAngle = Math.toRadians(dv); // + (Math.PI / 2); 778 // Apply rotation to the points of the needle 779 triPoints.forEach(pt => { 780 x = centerX + ((pt.x * Math.cos(radAngle)) - (pt.y * Math.sin(radAngle))); 781 y = centerY + ((pt.x * Math.sin(radAngle)) + (pt.y * Math.cos(radAngle))); 782 context.lineTo(x, y); 783 }); 784 } else if (this.hand === 'plane') { 785 // console.log("Drawing plane needle...") 786 const BOAT_FACT = 0.8; 787 let planePoints = [ 788 { x: 0, y: - radius * BOAT_FACT * 0.5714 }, // Nose 789 // Starboard 790 { x: radius * BOAT_FACT * 0.04286, y: - radius * BOAT_FACT * 0.4286 }, 791 { x: radius * BOAT_FACT * 0.08571, y: - radius * BOAT_FACT * 0.2857 }, 792 { x: radius * BOAT_FACT * 0.5714, y: - radius * BOAT_FACT * 0.1429 }, 793 { x: radius * BOAT_FACT * 0.5714, y: 0 }, 794 { x: radius * BOAT_FACT * 0.08571, y: radius * BOAT_FACT * 0.07143 }, 795 { x: radius * BOAT_FACT * 0.04286, y: radius * BOAT_FACT * 0.2571 }, 796 { x: radius * BOAT_FACT * 0.2857, y: radius * BOAT_FACT * 0.3571 }, 797 { x: radius * BOAT_FACT * 0.2857, y: radius * BOAT_FACT * 0.4286 }, 798 { x: radius * BOAT_FACT * 0.01429, y: radius * BOAT_FACT * 0.40 }, 799 // Port 800 { x: - radius * BOAT_FACT * 0.01429, y: radius * BOAT_FACT * 0.40 }, 801 { x: - radius * BOAT_FACT * 0.2857, y: radius * BOAT_FACT * 0.4286 }, 802 { x: - radius * BOAT_FACT * 0.2857, y: radius * BOAT_FACT * 0.3571 }, 803 { x: - radius * BOAT_FACT * 0.04286, y: radius * BOAT_FACT * 0.2571 }, 804 { x: - radius * BOAT_FACT * 0.08571, y: radius * BOAT_FACT * 0.07143 }, 805 { x: - radius * BOAT_FACT * 0.5714, y: 0 }, 806 { x: - radius * BOAT_FACT * 0.5714, y: - radius * BOAT_FACT * 0.1429 }, 807 { x: - radius * BOAT_FACT * 0.08571, y: - radius * BOAT_FACT * 0.2857 }, 808 { x: - radius * BOAT_FACT * 0.04286, y: - radius * BOAT_FACT * 0.4286 }, 809 810 { x: 0, y: - radius * BOAT_FACT * 0.5714 } 811 ]; 812 let radAngle = Math.toRadians(dv); // + (Math.PI / 2); 813 // Apply rotation to the points of the needle 814 planePoints.forEach(pt => { 815 x = centerX + ((pt.x * Math.cos(radAngle)) - (pt.y * Math.sin(radAngle))); 816 y = centerY + ((pt.x * Math.sin(radAngle)) + (pt.y * Math.cos(radAngle))); 817 context.lineTo(x, y); 818 }); 819 } else if (this.hand === 'arrow') { 820 // console.log("Drawing arrow needle...") 821 const BOAT_FACT = 0.8; 822 let arrowPoints = [ 823 { x: 0, y: - radius * BOAT_FACT * 0.7 }, // Nose 824 // Starboard 825 { x: radius * BOAT_FACT * 0.25, y: - radius * BOAT_FACT * 0.4 },
826 { x: radius * BOAT_FACT * 0.1, y: - radius * BOAT_FACT * 0.4 }, 827 { x: radius * BOAT_FACT * 0.1, y: radius * BOAT_FACT * 0.5 }, 828 // Port 829 { x: - radius * BOAT_FACT * 0.1, y: radius * BOAT_FACT * 0.5 }, 830 { x: - radius * BOAT_FACT * 0.1, y: - radius * BOAT_FACT * 0.4 }, 831 { x: - radius * BOAT_FACT * 0.25, y: - radius * BOAT_FACT * 0.4 }, 832 833 { x: 0, y: - radius * BOAT_FACT * 0.7 } 834 ]; 835 let radAngle = Math.toRadians(dv); // + (Math.PI / 2); 836 // Apply rotation to the points of the needle 837 arrowPoints.forEach(pt => { 838 x = centerX + ((pt.x * Math.cos(radAngle)) - (pt.y * Math.sin(radAngle))); 839 y = centerY + ((pt.x * Math.sin(radAngle)) + (pt.y * Math.cos(radAngle))); 840 context.lineTo(x, y); 841 }); 842 } 843 // Right 844 if (this.hand === 'regular' || this.hand === 'wind') { 845 x = centerX - ((radius * 0.05) * Math.cos(Math.toRadians(dv) + (2 * Math.PI / 2))); 846 y = centerY - ((radius * 0.05) * Math.sin(Math.toRadians(dv) + (2 * Math.PI / 2))); 847 context.lineTo(x, y); 848 } 849 850 context.closePath(); 851 context.fillStyle = this.directionColorConfig.handColor; 852 context.fill(); 853 context.lineWidth = 2; 854 context.strokeStyle = this.directionColorConfig.handOutlineColor; 855 context.stroke(); 856 // Knob 857 context.beginPath(); 858 context.arc((this.canvas.width / 2), (radius + 10), 7, 0, 2 * Math.PI, false); 859 context.closePath(); 860 context.fillStyle = this.directionColorConfig.knobColor; 861 context.fill(); 862 context.strokeStyle = this.directionColorConfig.knobOutlineColor; 863 context.stroke(); 864 } 865 866 drawSpike(radius, outsideRadius, insideRadius, angle, context) { 867 let xFrom = (this.canvas.width / 2) - (outsideRadius * Math.cos(2 * Math.PI * (angle / 360))); 868 let yFrom = (radius + 10) - (outsideRadius * Math.sin(2 * Math.PI * (angle / 360))); 869 // 870 let xTo = (this.canvas.width / 2) - (insideRadius * Math.cos(2 * Math.PI * ((angle - 90) / 360))); 871 let yTo = (radius + 10) - (insideRadius * Math.sin(2 * Math.PI * ((angle - 90) / 360))); 872 context.moveTo(xFrom, yFrom); 873 context.lineTo(xTo, yTo); 874 // 875 xTo = (this.canvas.width / 2) - (insideRadius * Math.cos(2 * Math.PI * ((angle + 90) / 360))); 876 yTo = (radius + 10) - (insideRadius * Math.sin(2 * Math.PI * ((angle + 90) / 360))); 877 context.moveTo(xFrom, yFrom); 878 context.lineTo(xTo, yTo); 879 } 880} 881 882// Associate the tag and the class 883window.customElements.define(DIRECTION_TAG_NAME, DirectionDisplay);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.