|
|
| Line 4: |
Line 4: |
| =Usage= | | =Usage= |
| {{#widget:TwinSuns | | {{#widget:TwinSuns |
| | id=wh | | | id=ts1 |
| | size=512 | | | size=768 |
| | fps=5 | | | fps=5 |
| | solara=https://www.wyrdehavenchronicl.es/wiki/images/f/fe/Solara.png | | | seasonAmp=8 <!-- seasonal equatorial inclination amplitude (deg) --> |
| | vaelus=https://www.wyrdehavenchronicl.es/wiki/images/7/7a/Vaelus.png | | | wobbleTotal=2.5 <!-- total relative wobble between suns (deg); split 40/60 --> |
| | backdrop=http://wyrdehavenchronicl.es/images/WyrdeHaven_Sky.png | | | solara=/wiki/images/f/fe/Solara.png |
| | cosine=http://wyrdehavenchronicl.es/images/Cosinus.png | | | vaelus=/wiki/images/7/7a/Vaelus.png |
| | | sky=/images/WyrdeHaven_Sky.png |
| | | cosine=/images/Cosinus.png |
| }} | | }} |
|
| |
| {| class="wikitable"
| |
| |-
| |
| ! Param !! Meaning !! Default
| |
| |-
| |
| | id || Unique id prefix || wh
| |
| |-
| |
| | size || Stage width/height in px || 512
| |
| |-
| |
| | fps || Animation fps || 5
| |
| |-
| |
| | solara || Solara PNG (transparent) || (URL above)
| |
| |-
| |
| | vaelus || Vaelus PNG (transparent) || (URL above)
| |
| |-
| |
| | backdrop || Sky backdrop image || (URL above)
| |
| |-
| |
| | cosine || Cosinus trace PNG (transparent) || (URL above)
| |
| |}
| |
| </noinclude><includeonly> | | </noinclude><includeonly> |
| <div class="wh-twinsuns" style="max-width:<!--{$size|default:512|escape:'html'}-->px;margin:1rem 0;"> | | <div class="wh-twinsuns" style="max-width:<!--{$size|default:768|escape:'html'}-->px;margin:1rem 0;"> |
| <!-- Day number centered over slider --> | | <!-- Top line with centered day readout --> |
| <div style="display:flex;justify-content:center;font-weight:600;margin-bottom:.25rem;"> | | <div style="display:flex;align-items:center;gap:.5rem;"> |
| <span id="<!--{$id|default:'wh'|escape:'html'}-->-daynum">1</span> / 479 | | <div style="flex:1;height:0.1rem;"></div> |
| | <div id="<!--{$id|default:'wh'|escape:'html'}-->-daycenter" |
| | style="font-weight:600;">1 / 479</div> |
| | <div style="flex:1;height:0.1rem;"></div> |
| </div> | | </div> |
|
| |
|
| <!-- Slider with cosine behind --> | | <!-- Slider + cosine (cosine exactly the slider width, aligned) --> |
| <div style="position:relative;display:flex;align-items:center;gap:.5rem;margin-bottom:.25rem;"> | | <div style="position:relative;display:flex;align-items:center;gap:.5rem;margin:.25rem 0 .5rem;"> |
| <div style="position:relative;flex:1;"> | | <div style="position:relative;flex:1;"> |
| <img id="<!--{$id|default:'wh'|escape:'html'}-->-cos" | | <img src="<!--{$cosine|default:'/images/Cosinus.png'|escape:'html'}-->" |
| src="<!--{$cosine|default:'http://wyrdehavenchronicl.es/images/Cosinus.png'|escape:'html'}-->"
| | alt="cosine" decoding="async" draggable="false" |
| alt="Cosinus" | | style="position:absolute;left:0;top:50%;transform:translateY(-50%);width:100%;height:auto;pointer-events:none;opacity:.65;"/> |
| decoding="async" fetchpriority="low"
| |
| style="position:absolute;inset:0;object-fit:contain;pointer-events:none;"/> | |
| <input id="<!--{$id|default:'wh'|escape:'html'}-->-x" type="range" min="1" max="479" value="1" | | <input id="<!--{$id|default:'wh'|escape:'html'}-->-x" type="range" min="1" max="479" value="1" |
| style="position:relative;width:100%;background:transparent;"> | | style="position:relative;display:block;width:100%;background:transparent;"> |
| </div>
| |
| <button id="<!--{$id|default:'wh'|escape:'html'}-->-play"
| |
| style="font-size:1rem;padding:.2rem .5rem;cursor:pointer;line-height:1;">▶</button>
| |
| </div>
| |
| | |
| <!-- Date text -->
| |
| <div style="margin:.25rem 0;">
| |
| <strong>Date:</strong>
| |
| <span id="<!--{$id|default:'wh'|escape:'html'}-->-date">Fortnight of Renewal (Dawning), day 8</span>
| |
| </div>
| |
| | |
| <!-- Stage -->
| |
| <div id="<!--{$id|default:'wh'|escape:'html'}-->-stage"
| |
| style="position:relative;width:<!--{$size|default:512|escape:'html'}-->px;height:<!--{$size|default:512|escape:'html'}-->px;isolation:isolate;border:1px solid #333;overflow:hidden;background:#9cccf6;">
| |
| <img id="<!--{$id|default:'wh'|escape:'html'}-->-bg"
| |
| src="<!--{$backdrop|default:'http://wyrdehavenchronicl.es/images/WyrdeHaven_Sky.png'|escape:'html'}-->"
| |
| alt="Backdrop"
| |
| decoding="async" fetchpriority="high" draggable="false"
| |
| style="position:absolute;inset:0;width:100%;height:100%;object-fit:cover;z-index:1;"/>
| |
| <img id="<!--{$id|default:'wh'|escape:'html'}-->-solara"
| |
| src="<!--{$solara|default:'https://www.wyrdehavenchronicl.es/wiki/images/f/fe/Solara.png'|escape:'html'}-->"
| |
| alt="Solara"
| |
| style="position:absolute;transform:translate(-50%,-50%);z-index:3;width:24%;max-width:180px;image-rendering:auto;"/>
| |
| <img id="<!--{$id|default:'wh'|escape:'html'}-->-vaelus"
| |
| src="<!--{$vaelus|default:'https://www.wyrdehavenchronicl.es/wiki/images/7/7a/Vaelus.png'|escape:'html'}-->"
| |
| alt="Vaelus"
| |
| style="position:absolute;transform:translate(-50%,-50%);z-index:2;width:18%;max-width:140px;image-rendering:auto;"/>
| |
| </div>
| |
| | |
| <!-- Inclination info -->
| |
| <div style="margin-top:.5rem;line-height:1.4;">
| |
| <div><strong>Relative Solar Inclination (Δ):</strong>
| |
| <span id="<!--{$id|default:'wh'|escape:'html'}-->-rel">+2.50°</span>
| |
| (<span id="<!--{$id|default:'wh'|escape:'html'}-->-state">Closing</span>)
| |
| </div>
| |
| <div><strong>Seasonal Equatorial Inclination (barycenter):</strong>
| |
| <span id="<!--{$id|default:'wh'|escape:'html'}-->-bar">-2.50°</span>
| |
| </div> | | </div> |
| </div>
| | <button id="<!--{$id|de |
| </div>
| |
| | |
| <script type="text/javascript">
| |
| (function(){
| |
| var ID = "<!--{$id|default:'wh'|escape:'js'}-->";
| |
| function ge(s){ return document.getElementById(ID + s); }
| |
| var el = {
| |
| x: ge("-x"),
| |
| day: ge("-daynum"),
| |
| date:ge("-date"),
| |
| cos: ge("-cos"),
| |
| play:ge("-play"),
| |
| stage:ge("-stage"),
| |
| s: ge("-solara"),
| |
| v: ge("-vaelus"),
| |
| rel: ge("-rel"),
| |
| bar: ge("-bar"),
| |
| st: ge("-state")
| |
| };
| |
| | |
| // sizing
| |
| var SIZE = <!--{$size|default:512|escape:'js'}-->;
| |
| // animation
| |
| var FPS = <!--{$fps|default:5|escape:'js'}-->;
| |
| var interval = 1000 / Math.max(1, FPS);
| |
| var timer = null, playing = false;
| |
| | |
| // months (15×31), Fortnight mapping per spec
| |
| var months = [
| |
| "Primaris","Vernalis","Tharion","Lunarae","Cindrel","Solvane","Auren","Veyrath",
| |
| "Rhendol","Tirien","Gairos","Fenmir","Wyrdrun","Cernis","Eirathen"
| |
| ];
| |
| | |
| function dateText(D){
| |
| // D: 1..479
| |
| if(D <= 7){
| |
| return "Fortnight of Renewal (Dawning), day " + (D + 7);
| |
| }
| |
| if(D >= 473){
| |
| return "Fortnight of Renewal (Fading), day " + (D - 472);
| |
| }
| |
| var idx = D - 7; // 1-based inside the 465-day span
| |
| var mIx = Math.ceil(idx / 31) - 1; // 0..14
| |
| var dIn = ((idx - 1) % 31) + 1; // 1..31
| |
| return months[mIx] + " " + dIn;
| |
| }
| |
| | |
| // inclination model
| |
| // Day 1 => +2.5° (max), midsummer => -2.5°.
| |
| var TWO_PI = Math.PI * 2;
| |
| function relIncl(D){ // degrees
| |
| return 2.5 * Math.cos(TWO_PI * ( (D-1) / 479 ));
| |
| }
| |
| function barIncl(D){ // inverted sign (lowest at midwinter when rel=+2.5)
| |
| return -relIncl(D);
| |
| }
| |
| function openingOrClosing(D){
| |
| var cur = Math.abs(relIncl(D));
| |
| var nxt = Math.abs(relIncl(D === 479 ? 1 : D+1));
| |
| return (nxt < cur) ? "Closing" : "Opening";
| |
| }
| |
| | |
| // stage placement
| |
| // Base anchor for the pair (roughly upper right sky)
| |
| var anchor = {
| |
| x: Math.round(SIZE * 0.68),
| |
| y: Math.round(SIZE * 0.22)
| |
| };
| |
| // Horizontal separation cue (~constant small separation to suggest 2.5°)
| |
| var sepX = Math.round(SIZE * 0.10); // tweakable; ~10% width
| |
| // Vertical wobble scale (px per degree), makes Δ±2.5° visible but subtle
| |
| var pxPerDeg = Math.max(6, Math.round(SIZE * 0.03 / 2.5)); // scales with size
| |
| | |
| function placeSuns(D){
| |
| var rel = relIncl(D); // ±2.5°
| |
| // split the relative Δ: Solara +Δ/2, Vaelus -Δ/2
| |
| var dY = (rel * pxPerDeg) / 2;
| |
| | |
| var sX = anchor.x - Math.round(sepX/2);
| |
| var vX = anchor.x + Math.round(sepX/2);
| |
| var sY = anchor.y - Math.round(dY);
| |
| var vY = anchor.y + Math.round(dY);
| |
| | |
| el.s.style.left = sX + "px";
| |
| el.s.style.top = sY + "px";
| |
| el.v.style.left = vX + "px";
| |
| el.v.style.top = vY + "px";
| |
| }
| |
| | |
| function fmtDeg(x){
| |
| var sign = (x >= 0) ? "+" : "−";
| |
| return sign + Math.abs(x).toFixed(2) + "°";
| |
| }
| |
| | |
| function update(){
| |
| var D = parseInt(el.x.value,10) || 1;
| |
| el.day.textContent = D;
| |
| el.date.textContent = dateText(D);
| |
| | |
| var r = relIncl(D);
| |
| var b = barIncl(D);
| |
| el.rel.textContent = fmtDeg(r);
| |
| el.bar.textContent = fmtDeg(b);
| |
| el.st.textContent = openingOrClosing(D);
| |
| | |
| placeSuns(D);
| |
| }
| |
| | |
| // Slider input throttled by rAF
| |
| var raf = null;
| |
| el.x.addEventListener("input", function(){
| |
| if(raf) return;
| |
| raf = requestAnimationFrame(function(){ raf=null; update(); });
| |
| });
| |
| | |
| function step(){
| |
| var v = parseInt(el.x.value,10) || 1;
| |
| v = (v >= 479) ? 1 : (v+1);
| |
| el.x.value = v;
| |
| update();
| |
| }
| |
| | |
| el.play.addEventListener("click", function(){
| |
| if(!playing){
| |
| playing = true;
| |
| el.play.textContent = "■";
| |
| timer = setInterval(step, interval);
| |
| } else {
| |
| playing = false;
| |
| el.play.textContent = "▶";
| |
| clearInterval(timer); timer = null;
| |
| }
| |
| });
| |
| | |
| // Initial paint
| |
| update();
| |
| })();
| |
| </script>
| |
| </includeonly>
| |