Commit 6df73dd3 authored by Paul Asmuth's avatar Paul Asmuth
Browse files

improved default tick layout for log scales

parent 9b96a98e
Loading
Loading
Loading
Loading
+88 −15
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ namespace fviz {

static const double kDefaultLogBase = 10;
static const size_t kMaxTicks = 8192;
static const double EPSILON = 0.001;

ScaleConfig::ScaleConfig() :
    kind(ScaleKind::LINEAR),
@@ -78,19 +79,25 @@ double scale_translate_linear(
double scale_translate_log(
    const ScaleConfig& domain,
    double v) {
  if (v <= 0) {
    return  0;
  }

  auto min = scale_min(domain);
  auto max = scale_max(domain);
  auto log_base = domain.log_base;
  double range_log = log(max - min) / log(log_base);

  auto vf = v - min;
  if (vf >= 1.0) {
  double min_log = min == 0 ? 0 : log(min) / log(log_base);
  double max_log = max == 0 ? 0 : log(max) / log(log_base);

  auto vf = v;
  if (vf != 0.0) {
    vf = log(vf) / log(log_base);
  } else {
    vf = 0;
  }

  auto vt = vf / range_log;
  auto vt = (vf - min_log) / (max_log - min_log);
  if (domain.inverted) {
    vt = 1.0 - vt;
  }
@@ -360,24 +367,29 @@ ReturnCode scale_layout_exponential_steps(
    ScaleLayout* layout,
    double base,
    size_t steps) {
  auto begin = scale_min(domain);
  auto end = scale_max(domain);
  double min = scale_min(domain);
  double max = scale_max(domain);
  double exp = min == 0 ? 0 : log(min) / log(domain.log_base);

  for (size_t idx = 0, exp = 0; ; ++idx) {
  for (size_t idx = 0; ; ++idx) {
    auto v = pow(base, exp++);
    auto vn = pow(base, exp);

    if (v < begin) {
    if (v < min) {
      continue;
    }

    if (v > end) {
    if (v > max + EPSILON) {
      break;
    }

    for (size_t s = 0; s < steps - 1; ++s) {
      auto vs = v + (s / double(steps - 1)) * (vn - v);
      auto vp = scale_translate(domain, vs);
      if (vs > max + EPSILON) {
        break;
      }

      layout->positions.emplace_back(vp);
      layout->labels.emplace_back(label_format(idx++, std::to_string(v))); // FIXME
    }
@@ -391,20 +403,23 @@ ReturnCode scale_layout_exponential(
    const Formatter& label_format,
    ScaleLayout* layout,
    double base) {
  auto begin = scale_min(domain);
  auto end = scale_max(domain);
  double min = scale_min(domain);
  double max = scale_max(domain);
  double exp = min == 0 ? 0 : log(min) / log(domain.log_base);

  for (size_t idx = 0, exp = 0; ; ++idx) {
  size_t idx = 0;
  for (;;) {
    auto v = pow(base, exp++);
    if (v < begin) {
    auto vp = scale_translate(domain, v);

    if (v < min) {
      continue;
    }

    if (v > end) {
    if (v > max + EPSILON) {
      break;
    }

    auto vp = scale_translate(domain, v);
    layout->positions.emplace_back(vp);
    layout->labels.emplace_back(label_format(idx++, std::to_string(v))); // FIXME
  }
@@ -748,6 +763,64 @@ ReturnCode scale_configure_layout(
      "  - categorical-bounds\n");
}

void scale_configure_layout_defaults(
    const ScaleConfig& config,
    ScaleLayoutFn* label_placement,
    ScaleLayoutFn* tick_placement) {
  // default label placement
  if (label_placement && !*label_placement) {
    switch (config.kind) {
      case ScaleKind::CATEGORICAL:
        *label_placement = bind(&scale_layout_categorical, _1, _2, _3);
        break;
      case ScaleKind::LOGARITHMIC:
        *label_placement = bind(
            &scale_layout_exponential,
            _1,
            _2,
            _3,
            config.log_base);
        break;
      default:
        *label_placement = bind(&scale_layout_subdivide, _1, _2, _3, 10);
        break;
    }
  }

  // default tick placement
  if (tick_placement && !*tick_placement) {
    switch (config.kind) {
      case ScaleKind::CATEGORICAL:
        *tick_placement = bind(&scale_layout_categorical_bounds, _1, _2, _3);
        break;
      case ScaleKind::LOGARITHMIC:
        if (config.log_base == 10) {
          *tick_placement = bind(
              &scale_layout_exponential_steps,
              _1,
              _2,
              _3,
              10,
              10);
        } else {
          *tick_placement =  bind(
              &scale_layout_exponential,
              _1,
              _2,
              _3,
              config.log_base);
        }
        break;
      default:
        if (label_placement) {
          *tick_placement = *label_placement;
        } else {
          *tick_placement =  bind(&scale_layout_subdivide, _1, _2, _3, 10);
        }
        break;
    }
  }
}

} // namespace fviz
+5 −0
Original line number Diff line number Diff line
@@ -146,5 +146,10 @@ ReturnCode scale_configure_layout(
    const Expr* expr,
    ScaleLayoutFn* layout);

void scale_configure_layout_defaults(
    const ScaleConfig& config,
    ScaleLayoutFn* label_placement,
    ScaleLayoutFn* tick_placement);

} // namespace fviz
+5 −25
Original line number Diff line number Diff line
@@ -752,27 +752,11 @@ ReturnCode build(const Environment& env, const Expr* expr, ElementRef* elem) {
    }
  }

  if (!config->label_placement) {
    switch (config->scale.kind) {
      case ScaleKind::CATEGORICAL:
        config->label_placement = bind(&scale_layout_categorical, _1, _2, _3);
        if (!config->tick_placement) {
          config->tick_placement = bind(&scale_layout_categorical_bounds, _1, _2, _3);
        }
        break;
      case ScaleKind::LOGARITHMIC:
        config->label_placement = bind(
            &scale_layout_exponential,
            _1,
            _2,
            _3,
            config->scale.log_base);
        break;
      default:
        config->label_placement = bind(&scale_layout_subdivide, _1, _2, _3, 10);
        break;
    }
  }
  /* setup defaults */
  scale_configure_layout_defaults(
      config->scale,
      &config->label_placement,
      &config->tick_placement);

  if (!config->label_formatter) {
    if (config->scale.kind == ScaleKind::CATEGORICAL) {
@@ -782,10 +766,6 @@ ReturnCode build(const Environment& env, const Expr* expr, ElementRef* elem) {
    }
  }

  if (!config->tick_placement) {
    config->tick_placement = config->label_placement;
  }

  *elem = std::make_shared<Element>();
  (*elem)->draw = bind(&axis_draw, config, _1, _2);
  (*elem)->size_hint = bind(&axis_calculate_size, config, _1, _2, _3, _4, _5);
+2 −15
Original line number Diff line number Diff line
@@ -108,21 +108,8 @@ ReturnCode build(const Environment& env, const Expr* expr, ElementRef* elem) {
    return config_rc;
  }

  if (!c->layout_x) {
    if (c->scale_x.kind == ScaleKind::CATEGORICAL) {
      c->layout_x = bind(&scale_layout_categorical, _1, _2, _3);
    } else {
      c->layout_x = bind(&scale_layout_subdivide, _1, _2, _3, 10);
    }
  }

  if (!c->layout_y) {
    if (c->scale_y.kind == ScaleKind::CATEGORICAL) {
      c->layout_y = bind(&scale_layout_categorical, _1, _2, _3);
    } else {
      c->layout_y = bind(&scale_layout_subdivide, _1, _2, _3, 10);
    }
  }
  scale_configure_layout_defaults(c->scale_x, nullptr, &c->layout_x);
  scale_configure_layout_defaults(c->scale_y, nullptr, &c->layout_y);

  *elem = std::make_shared<Element>();
  (*elem)->draw = bind(&draw, c, _1, _2);
+32 −0
Original line number Diff line number Diff line
@@ -21,9 +21,41 @@
  <text x="396.514856" y="356.444444" fill="#333333" font-size="14.666667" font-family="Arial,Helvetica,'Helvetica Neue',sans-serif">6.0</text>
  <path stroke-width="1.333333" stroke="#333333" fill="none" d="M84.3045 19.5556 L84.3045 330.711 "/>
  <path stroke-width="1.333333" stroke="#333333" fill="none" d="M84.3045 330.711 L89.6378 330.711 "/>
  <path stroke-width="1.333333" stroke="#333333" fill="none" d="M84.3045 307.294 L89.6378 307.294 "/>
  <path stroke-width="1.333333" stroke="#333333" fill="none" d="M84.3045 293.596 L89.6378 293.596 "/>
  <path stroke-width="1.333333" stroke="#333333" fill="none" d="M84.3045 283.878 L89.6378 283.878 "/>
  <path stroke-width="1.333333" stroke="#333333" fill="none" d="M84.3045 276.339 L89.6378 276.339 "/>
  <path stroke-width="1.333333" stroke="#333333" fill="none" d="M84.3045 270.18 L89.6378 270.18 "/>
  <path stroke-width="1.333333" stroke="#333333" fill="none" d="M84.3045 264.972 L89.6378 264.972 "/>
  <path stroke-width="1.333333" stroke="#333333" fill="none" d="M84.3045 260.461 L89.6378 260.461 "/>
  <path stroke-width="1.333333" stroke="#333333" fill="none" d="M84.3045 256.482 L89.6378 256.482 "/>
  <path stroke-width="1.333333" stroke="#333333" fill="none" d="M84.3045 252.922 L89.6378 252.922 "/>
  <path stroke-width="1.333333" stroke="#333333" fill="none" d="M84.3045 229.505 L89.6378 229.505 "/>
  <path stroke-width="1.333333" stroke="#333333" fill="none" d="M84.3045 215.807 L89.6378 215.807 "/>
  <path stroke-width="1.333333" stroke="#333333" fill="none" d="M84.3045 206.089 L89.6378 206.089 "/>
  <path stroke-width="1.333333" stroke="#333333" fill="none" d="M84.3045 198.55 L89.6378 198.55 "/>
  <path stroke-width="1.333333" stroke="#333333" fill="none" d="M84.3045 192.391 L89.6378 192.391 "/>
  <path stroke-width="1.333333" stroke="#333333" fill="none" d="M84.3045 187.183 L89.6378 187.183 "/>
  <path stroke-width="1.333333" stroke="#333333" fill="none" d="M84.3045 182.672 L89.6378 182.672 "/>
  <path stroke-width="1.333333" stroke="#333333" fill="none" d="M84.3045 178.693 L89.6378 178.693 "/>
  <path stroke-width="1.333333" stroke="#333333" fill="none" d="M84.3045 175.133 L89.6378 175.133 "/>
  <path stroke-width="1.333333" stroke="#333333" fill="none" d="M84.3045 151.717 L89.6378 151.717 "/>
  <path stroke-width="1.333333" stroke="#333333" fill="none" d="M84.3045 138.019 L89.6378 138.019 "/>
  <path stroke-width="1.333333" stroke="#333333" fill="none" d="M84.3045 128.3 L89.6378 128.3 "/>
  <path stroke-width="1.333333" stroke="#333333" fill="none" d="M84.3045 120.761 L89.6378 120.761 "/>
  <path stroke-width="1.333333" stroke="#333333" fill="none" d="M84.3045 114.602 L89.6378 114.602 "/>
  <path stroke-width="1.333333" stroke="#333333" fill="none" d="M84.3045 109.394 L89.6378 109.394 "/>
  <path stroke-width="1.333333" stroke="#333333" fill="none" d="M84.3045 104.883 L89.6378 104.883 "/>
  <path stroke-width="1.333333" stroke="#333333" fill="none" d="M84.3045 100.904 L89.6378 100.904 "/>
  <path stroke-width="1.333333" stroke="#333333" fill="none" d="M84.3045 97.3444 L89.6378 97.3444 "/>
  <path stroke-width="1.333333" stroke="#333333" fill="none" d="M84.3045 73.9277 L89.6378 73.9277 "/>
  <path stroke-width="1.333333" stroke="#333333" fill="none" d="M84.3045 60.2297 L89.6378 60.2297 "/>
  <path stroke-width="1.333333" stroke="#333333" fill="none" d="M84.3045 50.5109 L89.6378 50.5109 "/>
  <path stroke-width="1.333333" stroke="#333333" fill="none" d="M84.3045 42.9723 L89.6378 42.9723 "/>
  <path stroke-width="1.333333" stroke="#333333" fill="none" d="M84.3045 36.8129 L89.6378 36.8129 "/>
  <path stroke-width="1.333333" stroke="#333333" fill="none" d="M84.3045 31.6052 L89.6378 31.6052 "/>
  <path stroke-width="1.333333" stroke="#333333" fill="none" d="M84.3045 27.0941 L89.6378 27.0941 "/>
  <path stroke-width="1.333333" stroke="#333333" fill="none" d="M84.3045 23.115 L89.6378 23.115 "/>
  <path stroke-width="1.333333" stroke="#333333" fill="none" d="M84.3045 19.5556 L89.6378 19.5556 "/>
  <text x="52.180556" y="335.711111" fill="#333333" font-size="14.666667" font-family="Arial,Helvetica,'Helvetica Neue',sans-serif">1.0</text>
  <text x="44.024306" y="257.922222" fill="#333333" font-size="14.666667" font-family="Arial,Helvetica,'Helvetica Neue',sans-serif">10.0</text>
Loading