screengraft 0.54.1 → 0.54.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/ui/index.html +38 -7
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "screengraft",
3
- "version": "0.54.1",
3
+ "version": "0.54.2",
4
4
  "description": "Put a UI screenshot or screen recording onto a photographed device screen with the perspective exactly right \u2014 a homography you confirm by hand, not a generative guess.",
5
5
  "keywords": [
6
6
  "mockup",
package/ui/index.html CHANGED
@@ -2655,6 +2655,10 @@ $('#dofMeasure').onclick = measureDof;
2655
2655
  const gz = $('#dofGizmo');
2656
2656
  const SIGMA_FULL_FRAC = 0.02; // mirrors dof.DOF_MAX_FRAC -- keep in step
2657
2657
  let dofEnd = parseFloat(recall('dofEnd','1')); if (!(dofEnd > 0)) dofEnd = 1;
2658
+ // Where along the lines the handles sit (0..1 across the screen's extent).
2659
+ // Page-only: it changes nothing in the render. Turning re-derives it so the
2660
+ // handle stays on the point you turned about instead of sliding along the line.
2661
+ let dofK = 0.5;
2658
2662
  // The near limit. null = one-sided (the plane of focus at the screen's edge).
2659
2663
  let dofEnd2 = recall('dofEnd2','') === '' ? null : parseFloat(recall('dofEnd2','')); if (dofEnd2 !== null && !(dofEnd2 < 1)) dofEnd2 = null;
2660
2664
  function dofEndV(){ return dofOn ? dofEnd : 1; }
@@ -2695,10 +2699,10 @@ function paintGizmo(){
2695
2699
  const k = W / Math.max(im.clientWidth, 1); // photo px per screen px
2696
2700
  const g = gizmoGeom(); if (!g.H || !g.Hi) return;
2697
2701
  const P = (f, q) => applyH(g.H, gzPt(g, f, q));
2698
- const a0 = P(dofStart, -0.06), a1 = P(dofStart, 1.06), ac = P(dofStart, 0.5);
2699
- const b0 = P(dofEnd, -0.06), b1 = P(dofEnd, 1.06), bc = P(dofEnd, 0.5);
2702
+ const a0 = P(dofStart, -0.06), a1 = P(dofStart, 1.06), ac = P(dofStart, dofK);
2703
+ const b0 = P(dofEnd, -0.06), b1 = P(dofEnd, 1.06), bc = P(dofEnd, dofK);
2700
2704
  const two = dofEnd2 !== null;
2701
- const c0 = two ? P(dofEnd2, -0.06) : null, c1 = two ? P(dofEnd2, 1.06) : null, cc = two ? P(dofEnd2, 0.5) : null;
2705
+ const c0 = two ? P(dofEnd2, -0.06) : null, c1 = two ? P(dofEnd2, 1.06) : null, cc = two ? P(dofEnd2, dofK) : null;
2702
2706
  const th = P(dofEnd, 0.08 + 0.84 * dofStr); // the strength thumb slides along the dashed line
2703
2707
  const sigma = dofStr * SIGMA_FULL_FRAC * g.side;
2704
2708
  const r = 6 * k, rp = 4 * k, fs11 = 11 * k, off = 14 * k;
@@ -2733,7 +2737,19 @@ let gzDrag = null;
2733
2737
  gz.addEventListener('pointerdown', e => {
2734
2738
  const h = e.target.dataset && e.target.dataset.h; if (!h) return;
2735
2739
  e.preventDefault(); gz.setPointerCapture(e.pointerId);
2736
- gzDrag = {h}; gz.classList.add('drag');
2740
+ gzDrag = {h};
2741
+ if (h === 'rotA' || h === 'rotB'){
2742
+ // Turning keeps the SHARP line's centre where it is on the screen and the
2743
+ // limits at their distances from it, in screenshot pixels. The stored
2744
+ // numbers are fractions of the screen's extent along the direction, and
2745
+ // that extent changes with the angle -- so turning on the fractions alone
2746
+ // slid every line as it turned (reported 13 Sep 2026).
2747
+ const g = gizmoGeom();
2748
+ gzDrag.pivot = gzPt(g, dofStart, dofK);
2749
+ gzDrag.dFar = (dofEnd - dofStart) * (g.hi - g.lo);
2750
+ gzDrag.dNear = dofEnd2 === null ? null : (dofStart - dofEnd2) * (g.hi - g.lo);
2751
+ }
2752
+ gz.classList.add('drag');
2737
2753
  });
2738
2754
  gz.addEventListener('pointermove', e => {
2739
2755
  if (!gzDrag) return;
@@ -2752,11 +2768,26 @@ gz.addEventListener('pointermove', e => {
2752
2768
  } else {
2753
2769
  // Turn about the SHARP line's centre -- the plane of focus is what turns;
2754
2770
  // the pip under the pointer follows.
2755
- const c = gzPt(g, dofStart, 0.5);
2771
+ const c = gzDrag.pivot;
2756
2772
  const v = [p[0]-c[0], p[1]-c[1]];
2757
2773
  const sign = gzDrag.h === 'rotB' ? 1 : -1; // rotB sits at +u, rotA at -u
2758
- dofAng = ((Math.atan2(v[1]*sign, v[0]*sign) * 180 / Math.PI - 90) % 360 + 360) % 360;
2759
- if (e.shiftKey) dofAng = Math.round(dofAng / 15) * 15;
2774
+ let ang = ((Math.atan2(v[1]*sign, v[0]*sign) * 180 / Math.PI - 90) % 360 + 360) % 360;
2775
+ if (e.shiftKey) ang = Math.round(ang / 15) * 15;
2776
+ else {
2777
+ // Magnetic to the screen's own axes: within 4° of 0/90/180/270 the lines
2778
+ // run exactly along the screen's edges, which is what "along the phone"
2779
+ // means in the photograph once the perspective is applied.
2780
+ const near = Math.round(ang / 90) * 90;
2781
+ if (Math.abs(((ang - near) + 180) % 360 - 180) < 4) ang = near % 360;
2782
+ }
2783
+ dofAng = ang;
2784
+ // Re-express the lines against the new extent so nothing moves but the angle.
2785
+ const g2 = gizmoGeom();
2786
+ const f0 = (c[0]*g2.d[0] + c[1]*g2.d[1] - g2.lo) / (g2.hi - g2.lo);
2787
+ dofStart = Math.max(0, Math.min(0.95, f0));
2788
+ dofK = Math.max(0.05, Math.min(0.95, (c[0]*g2.u[0] + c[1]*g2.u[1] - g2.ulo) / (g2.uhi - g2.ulo)));
2789
+ dofEnd = Math.max(dofStart + 0.05, Math.min(1.5, dofStart + gzDrag.dFar / (g2.hi - g2.lo)));
2790
+ if (dofEnd2 !== null) dofEnd2 = Math.max(-0.5, Math.min(dofStart - 0.05, dofStart - gzDrag.dNear / (g2.hi - g2.lo)));
2760
2791
  }
2761
2792
  paintDof(); paintGizmo(); autoPreview();
2762
2793
  });