mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
SUN: white day — the brightest moment of the day is white (owner 2026-08-03)
- BG_STOPS: +10deg #e8ddcf (morning light), +30..90deg #ffffff; night half of the scale untouched (-4 #131a28, -12..-90 #070c14) - drawn-plan paper vs white sky: all paper shapes now sit in one .hp-paperg group; in daynight mode the group gets a subtle drop-shadow so the sheet contour stays readable at high sun (static mode and night unaffected) - pinned colors updated: test/sun.test.mjs, demo/smoke_sun.mjs; smoke_bg_color paperUnderneath follows the .hp-paperg wrapper - docs/SUN.md: explicit BG_STOPS table - demo/shot_daynight.mjs: noon/sunset/night stills of the scale
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -0,0 +1,36 @@
|
||||
// Three stills of the daynight scale (noon/sunset/night) for the white-day review.
|
||||
import { launch } from './serve.mjs';
|
||||
const outDir = process.argv[2] || '/tmp';
|
||||
const { page, browser } = await launch({ width: 700, height: 700 }, 1);
|
||||
await page.emulateMedia({ reducedMotion: 'reduce' });
|
||||
await page.evaluate(async () => {
|
||||
const c = window.__card;
|
||||
const sp = c._serverCfg.spaces.find((s) => s.id === 'f1');
|
||||
sp.openings = [
|
||||
{ id: 'wN', type: 'window', x: 0.30, y: 0.14, angle: 0, length: 0.08 },
|
||||
{ id: 'wE', type: 'window', x: 0.96, y: 0.60, angle: 90, length: 0.08 },
|
||||
{ id: 'wW', type: 'window', x: 0.04, y: 0.30, angle: 90, length: 0.08 },
|
||||
];
|
||||
sp.plan_url = ''; // hand-drawn plan: white paper — the risky case for a white day
|
||||
sp.settings = { ...(sp.settings || {}), show_borders: true, fill_mode: 'glow' };
|
||||
c._serverCfg.settings = { ...(c._serverCfg.settings || {}), north_deg: 0, sun_rays: true, bg_mode: 'daynight' };
|
||||
c._cfgRev = (c._cfgRev || 0) + 1;
|
||||
});
|
||||
const shot = async (az, el, name) => {
|
||||
await page.evaluate(async ([az, el]) => {
|
||||
const c = window.__card;
|
||||
c.hass = { ...c.hass, states: { ...c.hass.states, 'sun.sun': {
|
||||
entity_id: 'sun.sun', state: el > 0 ? 'above_horizon' : 'below_horizon',
|
||||
attributes: { azimuth: az, elevation: el },
|
||||
} } };
|
||||
c.requestUpdate(); await c.updateComplete;
|
||||
}, [az, el]);
|
||||
await page.waitForTimeout(300);
|
||||
const stage = await page.evaluateHandle(() => window.__card.shadowRoot.querySelector('.stage'));
|
||||
await stage.asElement().screenshot({ path: `${outDir}/${name}.png` });
|
||||
};
|
||||
await shot(180, 60, 'dn_noon');
|
||||
await shot(265, 3, 'dn_sunset');
|
||||
await shot(0, -20, 'dn_night');
|
||||
await browser.close();
|
||||
console.log('shots done');
|
||||
@@ -231,9 +231,11 @@ const drawn = await page.evaluate(async () => {
|
||||
const cs = getComputedStyle(p);
|
||||
return cs.stroke === 'none' || cs.strokeOpacity === '0' || cs.strokeWidth === '0px';
|
||||
});
|
||||
// paper first, everything else on top of it
|
||||
// paper first, everything else on top of it (one .hp-paperg group wraps
|
||||
// all paper shapes so the daynight drop shadow composites without seams)
|
||||
const first = sr().querySelector('.stage.noplan svg').firstElementChild;
|
||||
out.paperUnderneath = !!first && first.classList.contains('hp-paper');
|
||||
out.paperUnderneath = !!first && first.classList.contains('hp-paperg')
|
||||
&& !!first.firstElementChild && first.firstElementChild.classList.contains('hp-paper');
|
||||
// live resize preview: drag the L's right wall 0.625 -> 0.6875 — the paper
|
||||
// must move WITH the room, not lag behind until the drop
|
||||
const pv = JSON.parse(JSON.stringify(sp));
|
||||
|
||||
+2
-2
@@ -90,11 +90,11 @@ const res = await page.evaluate(async () => {
|
||||
|
||||
// 8) day/night background: daynight breathes, static does not
|
||||
await setSun(180, 60);
|
||||
out.staticBgAtNoon = !stageStyle().includes('background:#5a6673');
|
||||
out.staticBgAtNoon = !stageStyle().includes('background:#ffffff');
|
||||
cfg().settings.bg_mode = 'daynight';
|
||||
touchCfg();
|
||||
await setSun(180, 60);
|
||||
out.daynightNoonBg = stageStyle().includes('background:#5a6673');
|
||||
out.daynightNoonBg = stageStyle().includes('background:#ffffff');
|
||||
out.daynightClass = !!sr().querySelector('.stage.daynight');
|
||||
await setSun(180, -20);
|
||||
out.daynightNightBg = stageStyle().includes('background:#070c14');
|
||||
|
||||
File diff suppressed because one or more lines are too long
Vendored
+10
-2
File diff suppressed because one or more lines are too long
+13
-2
@@ -50,8 +50,19 @@ only — no entities are created, no services are called.
|
||||
- `'static'` — the existing `bg_color` behaviour, color picker and
|
||||
all. Nothing changes for existing installs.
|
||||
- `'daynight'` — the stage background follows the sun's elevation:
|
||||
neutral by day, a warm shift in the golden hour (elevation below
|
||||
~10°), cooling through dusk, deep darkening at night. The PLAN
|
||||
WHITE at full day (the brightest moment of the day is white — owner,
|
||||
2026-08-03), a warm bright shift in the golden hour (elevation below
|
||||
~10°), cooling through dusk, deep darkening at night. The scale
|
||||
(piecewise-linear between stops, `BG_STOPS` in `src/sun.ts`):
|
||||
|
||||
| elevation | color | phase |
|
||||
| --- | --- | --- |
|
||||
| −90°…−12° | `#070c14` | deep night |
|
||||
| −4° | `#131a28` | dusk cools down |
|
||||
| 0° | `#4a3527` | warm band right at the horizon |
|
||||
| +10° | `#e8ddcf` | morning light — warm and bright |
|
||||
| +30°…+90° | `#ffffff` | plain day, white |
|
||||
The PLAN
|
||||
itself dims only ~10% at night (`filter: brightness(.9)`), so the
|
||||
daytime room fills stay readable. Transitions are a CSS
|
||||
background/filter transition tens of seconds long;
|
||||
|
||||
@@ -5071,15 +5071,18 @@ class HouseplanCard extends LitElement {
|
||||
// L-shaped house — the scene bg reaches the exterior walls).
|
||||
// `space` comes from _renderCfg, so a live resize preview
|
||||
// (_rszPreview) moves the paper together with the rooms.
|
||||
// One <g> around ALL paper shapes: the daynight drop shadow
|
||||
// (styles.ts) is composited once for the whole sheet, so
|
||||
// adjacent rooms never cast seams onto each other's paper.
|
||||
if (space.bg) {
|
||||
const pp = this._paperRect(space)!;
|
||||
return svg`<rect class="hp-paper" x="${pp.x}" y="${pp.y}" width="${pp.w}" height="${pp.h}" pointer-events="none"></rect>`;
|
||||
return svg`<g class="hp-paperg"><rect class="hp-paper" x="${pp.x}" y="${pp.y}" width="${pp.w}" height="${pp.h}" pointer-events="none"></rect></g>`;
|
||||
}
|
||||
return paperRoomShapes(space.rooms).map((sh) =>
|
||||
return svg`<g class="hp-paperg">${paperRoomShapes(space.rooms).map((sh) =>
|
||||
'poly' in sh
|
||||
? svg`<polygon class="hp-paper" points="${sh.poly}" pointer-events="none"></polygon>`
|
||||
: svg`<rect class="hp-paper" x="${sh.rect.x}" y="${sh.rect.y}" width="${sh.rect.w}" height="${sh.rect.h}" rx="${sh.rect.rx}" pointer-events="none"></rect>`,
|
||||
);
|
||||
)}</g>`;
|
||||
})()}
|
||||
${this._editing ? this._renderMarkupDefs(vb) : nothing}
|
||||
${this._editing && !this._markup
|
||||
|
||||
@@ -328,6 +328,14 @@ export const cardStyles = css`
|
||||
.stage.noplan .hp-paper {
|
||||
fill: #ffffff;
|
||||
}
|
||||
/* White day (owner 2026-08-03): at high sun the daynight sky is #ffffff —
|
||||
the same white as a drawn plan's paper. A whisper of a drop shadow on
|
||||
the paper GROUP keeps the sheet's contour readable on the white sky
|
||||
(user units on the 1000-unit canvas ≈ a few screen px). Invisible at
|
||||
night against the dark sky; 'static' mode never gets it. */
|
||||
.stage.daynight .hp-paperg {
|
||||
filter: drop-shadow(0 2px 8px rgba(10, 16, 26, 0.28));
|
||||
}
|
||||
.room {
|
||||
transition: 0.12s;
|
||||
cursor: default; /* v1.40.1: rooms are not clickable — the label's link icon is */
|
||||
|
||||
+3
-3
@@ -49,9 +49,9 @@ const BG_STOPS: [number, string][] = [
|
||||
[-12, '#070c14'],
|
||||
[-4, '#131a28'], // dusk cools down
|
||||
[0, '#4a3527'], // warm band right at the horizon
|
||||
[10, '#46505f'], // golden hour fades into neutral
|
||||
[30, '#5a6673'], // plain day
|
||||
[90, '#5a6673'],
|
||||
[10, '#e8ddcf'], // morning light — warm and bright on the way to white
|
||||
[30, '#ffffff'], // plain day: the brightest moment is white (owner 2026-08-03)
|
||||
[90, '#ffffff'],
|
||||
];
|
||||
|
||||
const clamp01 = (t: number) => Math.min(1, Math.max(0, t));
|
||||
|
||||
+2
-2
@@ -50,12 +50,12 @@ test('sunDirOnPlan: compass points map to canvas vectors (y grows down)', () =>
|
||||
assert.ok(near(d[0], 0, 1e-12) && near(d[1], -1, 1e-12));
|
||||
});
|
||||
|
||||
test('dayPhase: night is dark and dim, noon is neutral, sunrise is warm', () => {
|
||||
test('dayPhase: night is dark and dim, noon is white, sunrise is warm', () => {
|
||||
const night = dayPhase(-20);
|
||||
const dawn = dayPhase(2);
|
||||
const noon = dayPhase(60);
|
||||
assert.equal(night.bg, '#070c14');
|
||||
assert.equal(noon.bg, '#5a6673');
|
||||
assert.equal(noon.bg, '#ffffff');
|
||||
assert.notEqual(dawn.bg, night.bg);
|
||||
assert.notEqual(dawn.bg, noon.bg);
|
||||
assert.ok(near(night.planDim, 0.1));
|
||||
|
||||
Reference in New Issue
Block a user