fix(studio): a repair wears its band only while the pointer is on it, so the photo is not left under a wash of the tool's own
This commit is contained in:
@@ -744,7 +744,12 @@ button.hint:disabled { cursor: default; }
|
||||
wash turns into an opaque stripe over a warm one.
|
||||
A stroke is one path, so where it crosses itself its translucency is laid once
|
||||
rather than twice — that doubling-up is exactly what a row of overlapping
|
||||
circles looks like. */
|
||||
circles looks like.
|
||||
The wash is a mark over the photo, though, and over a smooth sky a repair
|
||||
reads as a colour of its own that the photo does not have — the one thing a
|
||||
repair must never look like. So it is worn only while the pointer is on that
|
||||
run (the mark names the repair in hand) and the photo is left bare of it the
|
||||
rest of the time. */
|
||||
.brush-band {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
@@ -760,22 +765,20 @@ button.hint:disabled { cursor: default; }
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
/* No run in hand here: the band is the tool's record, not the photo's own
|
||||
colour, so it steps out of the way of what it was drawn over. */
|
||||
.brush-band path[data-idle] { opacity: 0; }
|
||||
/* A spot the band stands for: its box on the photo is still the spot's, its own
|
||||
edge is the band's now. */
|
||||
.brush-spot[data-band] { border-color: transparent; box-shadow: none; }
|
||||
/* A repair the pointer is not on and no press has chosen: the ring would be one
|
||||
more circle on a photo that already carries as many repairs as the user laid,
|
||||
so an idle spot is a faint print of the mark it made — soft, because it is a
|
||||
record of an edit rather than something to aim at, and it is the ring under
|
||||
the pointer that says the spot can be taken hold of again. */
|
||||
.brush-spot[data-idle] {
|
||||
border-color: transparent;
|
||||
background: rgba(127, 127, 127, 0.14);
|
||||
box-shadow: 0 0 7px 2px rgba(0, 0, 0, 0.25);
|
||||
filter: blur(1px);
|
||||
}
|
||||
/* A run the band already stands for needs no print of its own under it. */
|
||||
.brush-spot[data-idle][data-band] { background: none; box-shadow: none; filter: none; }
|
||||
and a print of the mark it made is a wash of the tool's own over the photo —
|
||||
over a smooth sky, a repair that reads as a colour the photo does not have.
|
||||
So an idle spot shows nothing at all: it is the ring under the pointer that
|
||||
says the spot can be taken hold of again, and the photo carries only the
|
||||
repair itself until then. */
|
||||
.brush-spot[data-idle] { opacity: 0; }
|
||||
/* In hand, one of the run's spots is the circle it would be taken hold of by, so
|
||||
it wears that circle again over the band — the ring is what says the spot is
|
||||
under the pointer. */
|
||||
|
||||
@@ -90,14 +90,16 @@ const wmBoxOf = (mark: WmMark, fontPx: number) => {
|
||||
// after a reload, an undo, or a copy of the recipe to another photo: a stroke
|
||||
// is not stored anywhere.
|
||||
const strokeRuns = (spots: { x: number; y: number; r: number }[], box: { width: number; height: number }) => {
|
||||
const paths: { d: string; w: number }[] = [];
|
||||
const paths: { d: string; w: number; spots: number[] }[] = [];
|
||||
const joined = spots.map(() => false);
|
||||
let run: number[] = [];
|
||||
const flush = () => {
|
||||
if (run.length > 1) {
|
||||
const w = 2 * spots[run[0]].r * box.width;
|
||||
const at = (i: number) => `${spots[i].x * box.width} ${spots[i].y * box.height}`;
|
||||
paths.push({ d: `M ${run.map(at).join(' L ')}`, w });
|
||||
// The run's own spots travel with the band, so the band can be shown for a
|
||||
// run the pointer is on and left off for the runs it is not.
|
||||
paths.push({ d: `M ${run.map(at).join(' L ')}`, w, spots: run.slice() });
|
||||
for (const i of run) joined[i] = true;
|
||||
}
|
||||
run = [];
|
||||
@@ -703,6 +705,22 @@ export function ImageStage({
|
||||
})()
|
||||
: null;
|
||||
|
||||
// Whether a run's own band is the one in hand: the pointer is on one of the
|
||||
// spots the run is made of, or a press has chosen one of them. A band is the
|
||||
// tool's mark over the photo and not the photo, so every other run's band —
|
||||
// and every other repair's print — stays off it, and what a repair looks like
|
||||
// is what the renderer made of it.
|
||||
const bandOn = (b: { spots: number[] }) => {
|
||||
if (selSpot !== null && b.spots.includes(selSpot)) return true;
|
||||
if (!brushPos || !box) return false;
|
||||
return b.spots.some((i) => {
|
||||
const s = brushSpots[i];
|
||||
return (
|
||||
!!s && Math.hypot((brushPos.fx - s.x) * box.width, (brushPos.fy - s.y) * box.height) <= Math.max(4, s.r * box.width)
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const brushDown = (e: React.PointerEvent<HTMLDivElement>) => {
|
||||
// The stage's own hand, under the brush: the middle button and the space
|
||||
// bar are how the photo is moved out of the way when the brush is down, and
|
||||
@@ -1637,7 +1655,12 @@ export function ImageStage({
|
||||
run is made of, joined into the one mark the drag painted.
|
||||
What is drawn is the recipe's own spots, so the band is what
|
||||
survives a reload, an undo and a copy of the recipe to another
|
||||
photo, where a stroke of its own would not. */}
|
||||
photo, where a stroke of its own would not. It, like the print
|
||||
of a spot, is the tool's own record and not part of the photo:
|
||||
it is worn while the pointer is on that run — the mark says
|
||||
which repair is in hand — and given up again the moment the
|
||||
pointer leaves, so what a repair left on the photo is read off
|
||||
the photo and not off a wash laid over it. */}
|
||||
{bands?.dst.paths.length ? (
|
||||
<svg
|
||||
className="brush-band"
|
||||
@@ -1647,7 +1670,12 @@ export function ImageStage({
|
||||
aria-hidden="true"
|
||||
>
|
||||
{bands.dst.paths.map((b, i) => (
|
||||
<path key={i} d={b.d} style={{ strokeWidth: b.w }} />
|
||||
<path
|
||||
key={i}
|
||||
d={b.d}
|
||||
style={{ strokeWidth: b.w }}
|
||||
data-idle={bandOn(b) ? undefined : '1'}
|
||||
/>
|
||||
))}
|
||||
</svg>
|
||||
) : null}
|
||||
|
||||
Reference in New Issue
Block a user