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

add a new and improved unit conversion mechanism

The "old" method for converting units was to store numerical values
alongside their units in a "Measure" struct which was then passed
through the various layers of the code, from parsing to the graphics
backend. The problem with that approach was that we now have a big
mess where unit conversions are scattered throughout the whole
codebase and it's often unclear at which point a given value should
be converted.

The new approach will be to convert all user-specified values into
internal (unitless) numbers as soon as possible; ideally right
after parsing. The conversion from all user-specifiable units to
the internal unit system is clearly defined using the UnitConvMap
struct. During export, all values should then be converted back from
the internal unitless system to the requested output coordinate
system and units.

This change only adds the first parts of the new unit conversion
system. The refactoring of all existing commands to use the new
mechanism is yet to be done.
parent 382dca29
Loading
Loading
Loading
Loading
+2 −10
Original line number Diff line number Diff line
@@ -16,21 +16,13 @@
#include <vector>
#include <string>
#include <return_code.h>
#include "utils/option.h"
#include "units.h"

namespace clip {
struct Layer;
struct Rectangle;

enum class Unit {
  UNIT,  // Screen units
  MM,    // Millimeters
  PX,    // Pixels
  PT,    // Typographic Points
  REM,   // Typographic "em" size
  REL,   // Relative to the enclosing element (0..1),
  USER,  // User units on arbitrary scale
};

struct Measure {
  Measure();
  explicit Measure(Unit unit, double value);
+48 −1
Original line number Diff line number Diff line
@@ -135,6 +135,32 @@ const Layer* layer_get(const Context* ctx) {
  return ctx->layer.get();
}

Number layer_get_width(const Layer& layer) {
  switch (layer.width.unit) {
    case Unit::MM:
      return unit_from_mm(layer.width.value, layer.dpi);
    case Unit::PT:
      return unit_from_pt(layer.width.value, layer.dpi);
    case Unit::PX:
      return unit_from_px(layer.width.value);
    default:
      return 0;
  }
}

Number layer_get_height(const Layer& layer) {
  switch (layer.height.unit) {
    case Unit::MM:
      return unit_from_mm(layer.height.value, layer.dpi);
    case Unit::PT:
      return unit_from_pt(layer.height.value, layer.dpi);
    case Unit::PX:
      return unit_from_px(layer.height.value);
    default:
      return 0;
  }
}

double layer_get_dpi(const Layer* layer) {
  return layer->dpi;
}
@@ -166,6 +192,28 @@ ReturnCode layer_set_dpi(
  return OK;
}

UnitConvMap layer_get_uconv_width(const Layer& layer) {
  auto width = layer_get_width(layer).value;

  UnitConvMap conv;
  conv[Unit::MM] = std::bind(&unit_from_mm, _1, layer.dpi);
  conv[Unit::PT] = std::bind(&unit_from_pt, _1, layer.dpi);
  conv[Unit::PX] = std::bind(&unit_from_px, _1);
  conv[Unit::PERCENT] = std::bind(&unit_from_percent, _1, width);
  return conv;
}

UnitConvMap layer_get_uconv_height(const Layer& layer) {
  auto height = layer_get_height(layer).value;

  UnitConvMap conv;
  conv[Unit::MM] = std::bind(&unit_from_mm, _1, layer.dpi);
  conv[Unit::PT] = std::bind(&unit_from_pt, _1, layer.dpi);
  conv[Unit::PX] = std::bind(&unit_from_px, _1);
  conv[Unit::PERCENT] = std::bind(&unit_from_percent, _1, height);
  return conv;
}

Measure layer_get_rem(const Layer* ctx) {
  auto rem_default = from_px(16);
  auto rem = ctx->font_size;
@@ -177,7 +225,6 @@ Measure layer_get_rem(const Context* ctx) {
  return layer_get_rem(layer_get(ctx));
}


const FontInfo& layer_get_font(const Layer* layer) {
  return layer->font;
}
+6 −1
Original line number Diff line number Diff line
@@ -41,7 +41,6 @@ struct Layer {
  DrawCommandList drawlist;
};


ReturnCode layer_create(
    Context* ctx,
    std::unique_ptr<Layer>* layer_storage);
@@ -62,11 +61,17 @@ ReturnCode layer_resize(
Layer* layer_get(Context* ctx);
const Layer* layer_get(const Context* ctx);

Number layer_get_width(const Layer& layer);
Number layer_get_height(const Layer& layer);

double layer_get_dpi(const Layer* layer);
double layer_get_dpi(const Context* ctx);
void layer_set_dpi(Layer* layer, double dpi);
ReturnCode layer_set_dpi(Context* ctx, const Expr* expr);

UnitConvMap layer_get_uconv_width(const Layer& layer);
UnitConvMap layer_get_uconv_height(const Layer& layer);

Measure layer_get_rem(const Layer* layer);
Measure layer_get_rem(const Context* ctx);

+64 −0
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@
 * limitations under the License.
 */
#include "sexpr_conv.h"
#include "sexpr_util.h"
#include "utils/fileutil.h"
#include "utils/csv.h"
#include "utils/algo.h"
@@ -150,6 +151,69 @@ ReturnCode expr_to_switch(
      expr_inspect(expr));
}

ReturnCode expr_to_number(
    const Expr* expr,
    const UnitConvMap& conv,
    Number* v) {
  if (!expr_is_value(expr)) {
    return errorf(ERROR, "invalid number: '{}'", expr_inspect(expr));
  }

  auto value_str = expr_get_value(expr);
  double value;
  size_t unit_pos;
  try {
    value = std::stod(value_str, &unit_pos);
  } catch (...) {
    return errorf(ERROR, "invalid number: '{}'", value_str);
  }

  if (unit_pos == value_str.size()) {
    return errorf(ERROR, "expected '{}' to be followed by a unit", value_str);
  }

  auto unit_str = value_str.substr(unit_pos);

  Unit unit;
  if (unit_parse(unit_str, &unit)) {
    auto conv_iter = conv.find(unit);
    if (conv_iter != conv.end()) {
      *v = conv_iter->second(value);
      return OK;
    }
  }

  return errorf(
      ERROR,
      "invalid unit: '{}'",
      unit_str);
}

ReturnCode expr_to_vec2(
    const Expr* expr,
    const UnitConvMap& conv1,
    const UnitConvMap& conv2,
    Vector2* v) {
  if (!expr_is_list(expr)) {
    return err_invalid_value(expr_inspect(expr), {"(<x> <y>)"});
  }

  auto args = expr_collect(expr_get_list(expr));
  if (args.size() != 2) {
    return err_invalid_nargs(args.size(), 2);
  }

  for (size_t i = 0; i < 2; ++i) {
    const auto& conv = i == 0 ? conv1 : conv2;

    if (auto rc = expr_to_number(args[i], conv, &v->at(i)); !rc) {
      return rc;
    }
  }

  return OK;
}

ReturnCode expr_to_stroke_style(
    const Expr* expr,
    StrokeStyle* style) {
+11 −0
Original line number Diff line number Diff line
@@ -65,6 +65,17 @@ ReturnCode expr_to_stroke_style(
    const Expr* expr,
    StrokeStyle* style);

ReturnCode expr_to_number(
    const Expr* expr,
    const UnitConvMap& conv,
    Number* v);

ReturnCode expr_to_vec2(
    const Expr* expr,
    const UnitConvMap& conv1,
    const UnitConvMap& conv2,
    Vector2* v);

ReturnCode expr_to_copy(
    const Expr* expr,
    ExprStorage* copy);
Loading