import {
  basis_default,
  line_default
} from "./chunk-KSGA62R2.js";
import {
  axisTop
} from "./chunk-LOZEKOES.js";
import {
  linear
} from "./chunk-OAWQ6LOO.js";
import {
  decimalPlacesUntilFirstNonZero
} from "./chunk-TLT4YIG3.js";
import {
  brushX
} from "./chunk-KYBIQBXE.js";
import {
  rgb
} from "./chunk-OMR2DT66.js";

// dom/numericRangeInput.ts
var NumericRangeInput = class {
  constructor(holder, range, callback, opts) {
    this.input = holder.append("input").attr("name", "rangeInput").attr("aria-label", "Leave blank for the allowed minimum value").style("width", opts?.width || "250px").style("margin", "3px 5px").on("change", () => {
      try {
        this.parseRange();
      } catch (ex) {
        alert(ex);
        this.setRange();
      }
    });
    this.setRange(range);
    this.callback = callback;
  }
  getInput() {
    return this.input;
  }
  parseRange() {
    const str = this.input.node().value;
    const new_range = parseRange(str);
    if (this.range?.min != void 0) {
      if (!new_range.startunbounded && this.range?.min > new_range.start) throw "Invalid start value < minimum allowed";
      if (!new_range.stopunbounded && this.range?.min >= new_range.stop) throw "Invalid stop value >= minimum allowed";
    }
    if (this.range?.max != void 0) {
      if (!new_range.stopunbounded && this.range?.max < new_range.stop) throw "Invalid stop value > maximum allowed";
      if (!new_range.startunbounded && new_range.start >= this.range?.max)
        throw "Invalid start value >= maximum allowed";
    }
    this.range = new_range;
    this.callback(new_range);
    return new_range;
  }
  getRange() {
    return this.range;
  }
  setRange(range) {
    if (!range) range = this.range;
    else this.range = range;
    if (!range) return;
    const start = range.start != void 0 ? `${range.start} <=` : "";
    const stop = range.stop != void 0 ? `<= ${range.stop}` : "";
    this.input.node().value = range.value != void 0 ? ` x=${range.value} ` : `${start} x ${stop}`;
  }
};
function parseRange(str) {
  if (!str) throw "Empty range";
  const tokens = str.replace(/\s/g, "").split("x");
  let start, stop, startinclusive, stopinclusive, value;
  if (tokens[0]) parseRangeToken(tokens[0]);
  if (tokens[1]) parseRangeToken(tokens[1]);
  if (value != void 0) return { value, label: `x = ${value}` };
  const startunbounded = start === void 0;
  const stopunbounded = stop === void 0;
  if (!startunbounded && !stopunbounded && start > stop) throw "start must be lower than stop";
  return { start, stop, value, startinclusive, stopinclusive, startunbounded, stopunbounded };
  function parseRangeToken(rangeToken) {
    const floatExpr = "[+-]?\\d+(\\.\\d+)?";
    if (new RegExp(`^${floatExpr}<$`).test(rangeToken) || new RegExp(`^>${floatExpr}$`).test(rangeToken)) {
      start = parseFloat(rangeToken.match(floatExpr));
      startinclusive = false;
    } else if (new RegExp(`^${floatExpr}<=$`).test(rangeToken) || new RegExp(`^>=${floatExpr}$`).test(rangeToken)) {
      start = parseFloat(rangeToken.match(floatExpr));
      startinclusive = true;
    } else if (new RegExp(`^${floatExpr}>$`).test(rangeToken) || new RegExp(`^<${floatExpr}$`).test(rangeToken)) {
      stop = parseFloat(rangeToken.match(floatExpr));
      stopinclusive = false;
    } else if (new RegExp(`^${floatExpr}>=$`).test(rangeToken) || new RegExp(`^<=${floatExpr}$`).test(rangeToken)) {
      stop = parseFloat(rangeToken.match(floatExpr));
      stopinclusive = true;
    } else if (new RegExp(`^${floatExpr}=$`).test(rangeToken) || new RegExp(`^=${floatExpr}$`).test(rangeToken)) {
      value = parseFloat(rangeToken.match(floatExpr));
      stopinclusive = true;
      startinclusive = true;
    } else throw `Could not parse expression '${rangeToken}'`;
  }
}

// dom/violinRenderer.js
var violinRenderer = class {
  constructor({
    holder,
    rd,
    radius = 10,
    width = 500,
    height = 100,
    shiftx = 20,
    shifty = 20,
    callback = null,
    scaleFactor = 1
  }) {
    this.holder = holder;
    this.rd = rd;
    this.vd = rd.charts?.[""]?.plots?.[0];
    if (!this.vd) throw "No density plot data to render.";
    this.width = width;
    this.height = height;
    this.radius = radius;
    this.callback = callback;
    this.shiftx = shiftx;
    this.shifty = shifty;
    this.svg = holder.append("svg").attr("width", `${width + 50}px`).attr("height", `${height + 50}px`);
    this.axisScale = linear().domain([rd.min, rd.max]).range([0, width]);
    this.axisScaleUI = linear().domain([rd.min * scaleFactor, rd.max * scaleFactor]).range([0, width]);
    this.wScale = linear().domain([this.vd.density.densityMax, this.vd.density.densityMin]).range([height * 0.45, 0]);
  }
  render() {
    this.svg.selectAll("*").remove();
    this.violinG = this.svg.append("g").attr("transform", `translate(${this.shiftx}, ${this.height / 2 + this.shifty})`);
    this.brushG = this.svg.append("g").attr("transform", `translate(${this.shiftx}, ${this.shifty})`);
    this.scaleG = this.svg.append("g").attr("transform", `translate(${this.shiftx}, ${this.shifty})`);
    this.scaleG.call(axisTop(this.axisScaleUI).tickValues(this.axisScaleUI.ticks()));
    this.renderArea(false);
    this.renderArea(true);
    if (this.vd.src) {
      this.violinG.append("image").classed("sjpp-beans-img", true).attr("xlink:href", this.vd.src).attr("transform", `translate(0, -${this.radius / 2})`).attr("width", this.width);
    }
    if (this.callback)
      this.svg.append("g").call(
        brushX().extent([
          [this.shiftx, this.shifty],
          [this.width + this.shiftx, this.height + this.shifty]
        ]).on("end", async (event) => {
          const selection = event.selection;
          if (!selection) return;
          const range_start = this.axisScale.invert(selection[0] - this.shiftx);
          const range_end = this.axisScale.invert(selection[1] - this.shiftx);
          this.callback({ range_start, range_end });
        })
      );
  }
  renderArea(invert) {
    if (this.vd.densityMax == 0) return;
    const areaBuilder = line_default().curve(basis_default).x((d) => this.axisScale(d.x0)).y((d) => invert ? -this.wScale(d.density) : this.wScale(d.density));
    const color = this.vd.color || "#dddddd";
    this.violinG.append("path").style("fill", color).style("opacity", 0).attr("stroke", rgb(color).darker()).attr("stroke-width", 1).attr("stroke-linejoin", "round").style("opacity", "0.8").attr("d", areaBuilder(this.vd.density.bins));
  }
};

// dom/niceNumLabels.ts
function niceNumLabels(nums) {
  const sorted = nums.sort((a, b) => a - b);
  const abs = Math.abs(sorted[0] - sorted[sorted.length - 1]);
  const zeroDecimalNum = decimalPlacesUntilFirstNonZero(abs);
  const decimals2Show = abs >= 10 ? 0 : abs >= 1 ? 1 : zeroDecimalNum + 2;
  return nums.map((num) => {
    if (Math.abs(num) < 1 && decimals2Show == 0) return Number(num.toPrecision(1));
    return Number(num.toFixed(decimals2Show));
  });
}

export {
  NumericRangeInput,
  violinRenderer,
  niceNumLabels
};
//# sourceMappingURL=chunk-DD4R5P6W.js.map
