UNPKG

@thi.ng/imgui

Version:

Immediate mode GUI with flexible state handling & data only shape output

126 lines (125 loc) 3.36 kB
import { group } from "@thi.ng/geom/group"; import { line } from "@thi.ng/geom/line"; import { polygon } from "@thi.ng/geom/polygon"; import { rect } from "@thi.ng/geom/rect"; import { triangle } from "@thi.ng/geom/triangle"; import { mix } from "@thi.ng/math/mix"; import { roundTo } from "@thi.ng/math/prec"; import { map } from "@thi.ng/transducers/map"; import { ONE2, ZERO2 } from "@thi.ng/vectors/api"; import { clamp01_2 } from "@thi.ng/vectors/clamp01"; import { fit2 } from "@thi.ng/vectors/fit"; import { hash } from "@thi.ng/vectors/hash"; import { mix2 } from "@thi.ng/vectors/mix"; import { Key } from "../api.js"; import { isHoverSlider } from "../behaviors/slider.js"; import { layoutBox } from "../layout.js"; import { tooltipRaw } from "./tooltip.js"; const ramp = ({ gui, layout, id, ramp: ramp2, mode = 0, info, eps = 0.05, samples = 100, fill = gui.textColor(false) }) => { const { x, y, w, h } = layoutBox(layout); const maxX = x + w; const maxY = y + h; const pos = [x, maxY]; const maxPos = [maxX, y]; const key = hash([x, y, w, h]); gui.registerID(id, key); const box = gui.resource(id, key, () => rect([x, y], [w, h])); const hover = isHoverSlider(gui, id, box, "move"); const stops = ramp2.stops; let selID = -1; let sel; let res; const focused = gui.requestFocus(id); if (hover) { sel = clamp01_2(null, fit2([], gui.mouse, pos, maxPos, ZERO2, ONE2)); selID = ramp2.closestIndex(sel[0], eps); if (gui.isMouseDown()) { gui.activeID = id; if (selID >= 0) { stops[selID] = sel; } else { ramp2.setStopAt( roundTo(sel[0], 1e-3), roundTo(sel[1], 1e-3), eps ); } res = ramp2; } if (focused && selID >= 0 && __handleRampKeys(gui, ramp2, selID)) { res = ramp2; } info && gui.draw && tooltipRaw(gui, info); } if (gui.draw) { box.attribs = { fill: gui.bgColor(hover || focused), stroke: gui.focusColor(id) }; gui.add( box, gui.resource( id, hash(stops.flatMap((x2) => x2)) + mode, () => polygon( [ [x, maxY], mix2([], pos, maxPos, [0, stops[0][1]]), ...__rampVertices(ramp2, pos, maxPos, samples), mix2([], pos, maxPos, [1, stops[stops.length - 1][1]]), [maxX, maxY] ], { fill } ) ), ...stops.map(([t], i) => { const xx = mix(x, maxX, t); return triangle( [ [xx - 5, maxY], [xx + 5, maxY], [xx, maxY - 5] ], { fill: gui.fgColor(selID === i) } ); }) ); if (sel) { const [cx, cy] = mix2([], pos, maxPos, sel); gui.add( group({ stroke: gui.fgColor(selID >= 0) }, [ line([x, cy], [maxX, cy]), line([cx, y], [cx, maxY]) ]) ); } } gui.lastID = id; return res; }; const __rampVertices = (ramp2, pos, maxPos, numSamples) => map((p) => mix2(p, pos, maxPos, p), ramp2.samples(numSamples)); const __handleRampKeys = (gui, ramp2, selID) => { switch (gui.key) { case Key.TAB: gui.switchFocus(); break; case "x": case Key.DELETE: ramp2.removeStopAtIndex(selID); return true; default: } }; export { ramp };