Widget:TwinSuns: Difference between revisions

From WyrdeWiki
No edit summary
No edit summary
 
(13 intermediate revisions by the same user not shown)
Line 7: Line 7:
  | size=768
  | size=768
  | fps=5
  | fps=5
  | wobble=2.5
  | wobble=5
  | equator=8
  | equator=8
  | sep=2.5
  | sep=2.5
Line 50: Line 50:
   </div>
   </div>


   <!-- date line with extra top margin to avoid cosine overlap -->
   <!-- date line moved slightly lower to avoid cosine overlap -->
   <div style="margin:.65rem 0 .35rem 0;">
   <div style="margin:2rem 0 .4rem 0;">
     <strong>Date:</strong>
     <strong>Date:</strong>
     <span id="<!--{$id|default:'ts'|escape:'html'}-->-date">Fortnight of Renewal (Dawning), day 8 (FR 8)</span>
     <span id="<!--{$id|default:'ts'|escape:'html'}-->-date">Fortnight of Renewal (Dawning), day 8 (FR 8)</span>
Line 84: Line 84:
   var ID  = "<!--{$id|default:'ts'|escape:'js'}-->";
   var ID  = "<!--{$id|default:'ts'|escape:'js'}-->";
   var FPS = parseFloat("<!--{$fps|default:5|escape:'js'}-->") || 5;
   var FPS = parseFloat("<!--{$fps|default:5|escape:'js'}-->") || 5;
   var WOBBLE = parseFloat("<!--{$wobble|default:2.5|escape:'js'}-->") || 2.5;     // Δ amplitude (deg)
   var WOBBLE = parseFloat("<!--{$wobble|default:2.5|escape:'js'}-->") || 2.5; // observable Δ (deg)
   var EQUATOR = parseFloat("<!--{$equator|default:8|escape:'js'}-->") || 8;       // seasonal max (deg)
   var EQUATOR = parseFloat("<!--{$equator|default:8|escape:'js'}-->") || 8;   // seasonal max (deg)
   var SEP = parseFloat("<!--{$sep|default:2.5|escape:'js'}-->") || 2.5;           // horizontal sep (deg)
   var SEP     = parseFloat("<!--{$sep|default:2.5|escape:'js'}-->")   || 2.5; // horizontal sep (deg)
   var DEGPX = parseFloat("<!--{$degpx|default:10|escape:'js'}-->") || 10;         // px per degree
   var DEGPX   = parseFloat("<!--{$degpx|default:10|escape:'js'}-->")   || 10; // px per degree


   function ge(s){ return document.getElementById(ID + s); }
  // Find and init exactly one instance in a given DOM scope
  var el = {
   function initIn(root){
    day: ge("-daylabel"),
    var wrap = root.querySelector('.wh-twins');
     rng: ge("-x"),
     if(!wrap) return;
    play: ge("-play"),
    date: ge("-date"),
    stage: ge("-stage"),
    sky: ge("-sky"),
    s: ge("-solara"),
    v: ge("-vaelus"),
    rel: ge("-relinc"),
    eq: ge("-eqinc")
  };


  // sizes: keep suns proportionally similar to your PNGs
    function ge(s){ return document.getElementById(ID + s); }
  function setStarSizes(){
    var el = {
    var w = el.stage.clientWidth;
      day: ge("-daylabel"),
    var base = Math.round(w * 0.05);     // tweakable
      rng: ge("-x"),
    el.s.width = base; el.s.height = base;
      play: ge("-play"),
    el.v.width = Math.round(base * 0.72);
      date: ge("-date"),
     el.v.height = Math.round(base * 0.72);
      stage: ge("-stage"),
   }
      sky: ge("-sky"),
  setStarSizes();
      s: ge("-solara"),
  window.addEventListener("resize", setStarSizes);
      v: ge("-vaelus"),
      rel: ge("-relinc"),
      eq: ge("-eqinc")
    };
    for (var k in el){ if(!el[k]) return; } // quietly bail if something’s missing
 
    var TAU = Math.PI*2;
    function cosPhase(d){ return Math.cos(TAU*((d-1)/479)); }
    function fmtDeg(x){ var s=(x>=0?"+":""); return s + x.toFixed(2) + "°"; }
    function openingClosing(_, phase){
      var s = Math.sin(TAU*phase);
      return (-s>0) ? "Closing" : "Opening";
    }
 
    var months = ["Primaris","Vernalis","Tharion","Lunarae","Cindrel","Solvane","Auren","Veyrath","Rhendol","Tirien","Gairos","Fenmir","Wyrdrun","Cernis","Eirathen"];
    function dateText(d){
      if(d<=7)        return "Fortnight of Renewal (Dawning), day " + (d+7) + " (FR " + (d+7) + ")";
      if(d>=473){var k=d-472; return "Fortnight of Renewal (Fading), day " + k + " (FR " + k + ")";}
      var n=d-7, m=Math.ceil(n/31), day=((n-1)%31)+1; return day + " " + months[m-1];
    }
 
    var SOL_SHARE=1.0, VAE_SHARE=1.5, SUM=SOL_SHARE+VAE_SHARE;
 
    function setStarSizes(){
      var w = el.stage.clientWidth || el.stage.getBoundingClientRect().width || 0;
      if(!w) return;
      var base = Math.round(w*0.08);
      el.s.width = el.s.height = base;
      el.v.width = el.v.height = Math.round(base*0.8);
     }
 
    function update(){
      try{
        var d = parseInt(el.rng.value,10) || 1;
        var phase = (d-1)/479;
 
        // Your “observable” Δ already in sky-arc degrees
        var delta = (WOBBLE * cosPhase(d)) / 8;     // ±WOBBLE
        var eq    = -EQUATOR * cosPhase(d);   // lowest at midwinter
 
        el.day.textContent  = d + " / 479";
        el.date.textContent = dateText(d);
        el.rel.textContent  = fmtDeg(delta) + " (" + openingClosing(delta, phase) + ")";
        el.eq.textContent  = fmtDeg(eq);
 
        var w = el.stage.clientWidth, h = el.stage.clientHeight;
        if(!w||!h) return;
 
        var cx = w*0.5, cy = h*0.33;
        var dx = (SEP/2)*DEGPX;
 
        var solOff =  (delta*(SOL_SHARE/SUM))*DEGPX;
        var vaeOff = -(delta*(VAE_SHARE/SUM))*DEGPX;
        var yBase  = cy - (eq*DEGPX);


  // helpers
        el.s.style.left = (cx - dx) + "px";
  var TAU = Math.PI * 2;
        el.v.style.left = (cx + dx) + "px";
  function cosPhase(d){ return Math.cos(TAU * ( (d-1) / 479 )); }  // day1 => +1.0
        el.s.style.top  = (yBase - solOff) + "px";
  function fmtDeg(x){ var s = (x>=0?"+":""); return s + x.toFixed(2) + "°"; }
        el.v.style.top  = (yBase - vaeOff) + "px";
  function openingClosing(val, phase){ // val is Δ(d); dφ/dt sign decides
      }catch(e){}
    // derivative of cos is -sin; positive means decreasing (Closing), negative means Opening
    }
    var s = Math.sin(TAU * ((phase))); // phase=(d-1)/479
    return ( -s > 0 ? "Closing" : "Opening" );
  }


  // calendar text
    function ensureSizedThen(fn){
  var months = ["Primaris","Vernalis","Tharion","Lunarae","Cindrel","Solvane","Auren","Veyrath","Rhendol","Tirien","Gairos","Fenmir","Wyrdrun","Cernis","Eirathen"];
       setStarSizes();
  function dateText(d){
      if(el.stage.clientWidth) fn();
    if(d <= 7){
      else setTimeout(function(){ ensureSizedThen(fn); }, 50);
       return "Fortnight of Renewal (Dawning), day " + (d+7) + " (FR " + (d+7) + ")";
    } else if (d >= 473){
      var k = d - 472; // 1..7
      return "Fortnight of Renewal (Fading), day " + k + " (FR " + k + ")";
    } else {
      var n = d - 7;                // 1..465
      var m = Math.ceil(n / 31);     // 1..15
      var day = ((n-1) % 31) + 1;    // 1..31
      return day + " " + months[m-1];
     }
     }
  }


  // layout constants
    var timer=null, playing=false, interval=1000/Math.max(1,FPS);
  var SOL_SHARE = 1.0, VAE_SHARE = 1.5; // sum = Δ split
    function step(){ var v=parseInt(el.rng.value,10)||1; el.rng.value=(v>=479)?1:(v+1); update(); }
  var SUM = SOL_SHARE + VAE_SHARE;


  function update(){
    el.rng.addEventListener("input", update);
    var d = parseInt(el.rng.value,10);
    el.play.addEventListener("click", function(){
     var phase = (d-1)/479;
      if(!playing){ playing=true; el.play.textContent="■"; timer=setInterval(step, interval); }
      else { playing=false; el.play.textContent="▶"; clearInterval(timer); timer=null; }
    });
     window.addEventListener("resize", function(){ setStarSizes(); update(); });


     // angles (deg)
     ensureSizedThen(update);
    var delta = WOBBLE * cosPhase(d);       // +Δ at day 1
  }
    var eq = -EQUATOR * cosPhase(d);        // lowest (negative) at day 1, highest midsummer


    el.day.textContent = d + " / 479";
  // Run on normal load
     el.date.textContent = dateText(d);
  if (document.readyState === "complete" || document.readyState === "interactive"){
    initIn(document);
  } else {
     document.addEventListener("DOMContentLoaded", function(){ initIn(document); }, {once:true});
  }


    var oc = openingClosing(delta, phase);
  // Also run when MediaWiki injects content (tabs, previews, etc.)
     el.rel.textContent = fmtDeg(delta) + " (" + oc + ")";
  if (window.mw && mw.hook) {
     el
     mw.hook('wikipage.content').add(function($c){
      initIn($c && $c[0] ? $c[0] : document);
     });
  }
})();
</script>
</includeonly>

Latest revision as of 21:50, 11 November 2025

This widget shows WyrdeHaven’s twin suns with seasonal wobble & calendar.

Usage

1 / 479
Date: Fortnight of Renewal (Dawning), day 8 (FR 8)
Sky Solara Vaelus
Relative Solar Inclination (Δ): +2.50° (Closing)
Seasonal Equatorial Inclination (barycenter): -8.00°

param meaning default
size max stage width (px) 768
fps play speed (frames/s) 5
wobble solar relative wobble amplitude (Δ, degrees) 2.5
equator seasonal/barycenter max tilt (degrees) 8
sep visible horizontal separation between suns (degrees) 2.5
degpx vertical degree→pixel scale (px per degree) 10