All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.squarespace.less.plugins.MathFunctions Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (c) 2014 SQUARESPACE, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.squarespace.less.plugins;

import java.util.List;

import com.squarespace.less.LessException;
import com.squarespace.less.exec.ExecEnv;
import com.squarespace.less.exec.Function;
import com.squarespace.less.exec.Registry;
import com.squarespace.less.exec.SymbolTable;
import com.squarespace.less.model.Dimension;
import com.squarespace.less.model.Node;
import com.squarespace.less.model.Unit;
import com.squarespace.less.model.UnitConversions;


/**
 * Math function implementations.
 *
 * http://lesscss.org/functions/#math-functions
 */
public class MathFunctions implements Registry {

  @Override
  public void registerPlugins(SymbolTable table) {
    table.add(ABS);
    table.add(ACOS);
    table.add(ASIN);
    table.add(ATAN);
    table.add(CEIL);
    table.add(COS);
    table.add(FLOOR);
    table.add(MAX);
    table.add(MIN);
    table.add(MOD);
    table.add(PERCENTAGE);
    table.add(PI);
    table.add(POW);
    table.add(ROUND);
    table.add(SIN);
    table.add(SQRT);
    table.add(TAN);
  }

  public static final Function ABS = new Function("abs", "d") {
    public Node invoke(ExecEnv env, java.util.List args) throws LessException {
      Dimension dim = (Dimension)args.get(0);
      return new Dimension(Math.abs(dim.value()), dim.unit());
    };
  };

  public static final Function ASIN = new Function("asin", "d") {
    @Override
    public Node invoke(ExecEnv env, List args) throws LessException {
      double value = Math.asin(((Dimension)args.get(0)).value());
      return new Dimension(value, Unit.RAD);
    }
  };

  public static final Function ACOS = new Function("acos", "d") {
    @Override
    public Node invoke(ExecEnv env, List args) throws LessException {
      double value = Math.acos(((Dimension)args.get(0)).value());
      return new Dimension(value, Unit.RAD);
    }
  };

  public static final Function ATAN = new Function("atan", "d") {
    @Override
    public Node invoke(ExecEnv env, List args) throws LessException {
      double value = Math.atan(((Dimension)args.get(0)).value());
      return new Dimension(value, Unit.RAD);
    }
  };

  public static final Function CEIL = new Function("ceil", "d") {
    @Override
    public Node invoke(ExecEnv env, List args) throws LessException {
      Dimension dim = (Dimension)args.get(0);
      return new Dimension(Math.ceil(dim.value()), dim.unit());
    }
  };

  public static final Function COS = new Function("cos", "d") {
    @Override
    public Node invoke(ExecEnv env, List args) throws LessException {
      return trigResult(TrigFunction.COS, args.get(0));
    }
  };

  public static final Function FLOOR = new Function("floor", "d") {
    @Override
    public Node invoke(ExecEnv env, List args) throws LessException {
      Dimension dim = (Dimension)args.get(0);
      return new Dimension(Math.floor(dim.value()), dim.unit());
    }
  };

  public static final Function MAX = new Function("max", "*.") {
    @Override
    public Node invoke(ExecEnv env, List args) throws LessException {
      Dimension result = calculateMinOrMax(args, false);
      return (result == null) ? null : result;
    }
  };

  public static final Function MIN = new Function("min", "*.") {
    @Override
    public Node invoke(ExecEnv env, List args) throws LessException {
      Node result = calculateMinOrMax(args, true);
      return (result == null) ? null : result;
    }
  };

  public static final Function MOD = new Function("mod", "dd") {
    @Override
    public Node invoke(ExecEnv env, List args) throws LessException {
      Dimension dividend = (Dimension)args.get(0);
      double divisor = ((Dimension)args.get(1)).value();
      double result = Double.NaN;
      if (divisor != 0.0) {
        result = dividend.value() % divisor;
      }
      return new Dimension(result, dividend.unit());
    }
  };

  public static final Function PERCENTAGE = new Function("percentage", "d") {
    @Override
    public Node invoke(ExecEnv env, List args) throws LessException {
      Dimension dim = (Dimension)args.get(0);
      return new Dimension(dim.value() * 100, Unit.PERCENTAGE);
    }
  };

  public static final Function PI = new Function("pi", "") {
    @Override
    public Node invoke(ExecEnv env, List args) throws LessException {
      return new Dimension(Math.PI);
    }
  };

  public static final Function POW = new Function("pow", "dd") {
    @Override
    public Node invoke(ExecEnv env, List args) throws LessException {
      Dimension base = (Dimension)args.get(0);
      Dimension exp = (Dimension)args.get(1);
      double value = Math.pow(base.value(), exp.value());
      return new Dimension(value, base.unit());
    }
  };

  public static final Function ROUND = new Function("round", "d:n") {
    @Override
    public Node invoke(ExecEnv env, List args) throws LessException {
      double places = 0.0;
      int size = args.size();
      Dimension dim = (Dimension)args.get(0);
      if (size == 2) {
        places = Math.max(((Dimension)args.get(1)).value(), 0);
      }
      double scale = Math.pow(10, places);
      return new Dimension(Math.round(dim.value() * scale) / scale, dim.unit());
    }
  };

  public static final Function SIN = new Function("sin", "d") {
    @Override
    public Node invoke(ExecEnv env, List args) throws LessException {
      return trigResult(TrigFunction.SIN, args.get(0));
    }
  };

  public static final Function SQRT = new Function("sqrt", "d") {
    @Override
    public Node invoke(ExecEnv env, List args) throws LessException {
      Dimension dim = (Dimension)args.get(0);
      return new Dimension(Math.sqrt(dim.value()), dim.unit());
    }
  };

  public static final Function TAN = new Function("tan", "d") {
    @Override
    public Node invoke(ExecEnv env, List args) throws LessException {
      return trigResult(TrigFunction.TAN, args.get(0));
    }
  };

  private static Dimension calculateMinOrMax(List args, boolean minimum) throws LessException {
    // These will be set to value of first argument.
    double value = 0.0;
    Unit unit = null;

    int size = args.size();
    for (int i = 0; i < size; i++) {
      Node node = args.get(i);
      if (!(node instanceof Dimension)) {
        // Non-dimension is invalid. Bail out and return static representation of function call.
        return null;
      }

      Dimension dim = (Dimension)node;
      if (i == 0) {
        value = dim.value();
        unit = dim.unit();

      } else if (dim.unit() != unit) {
        // Mixed units are invalid. Bail out and return static representation of function call
        return null;

      } else {
        value = (minimum) ? Math.min(value, dim.value()) : Math.max(value, dim.value());
      }

    }
    return new Dimension(value, unit);
  }

  private enum TrigFunction {
    SIN,
    COS,
    TAN
  }

  private static Node trigResult(TrigFunction function, Node argument) {
    Dimension dim = (Dimension)argument;
    double factor = UnitConversions.factor(dim.unit(), Unit.RAD);
    double result = dim.value() * factor;
    switch (function) {
      case SIN:
        result = Math.sin(result);
        break;

      case COS:
        result = Math.cos(result);
        break;

      case TAN:
        result = Math.tan(result);
        break;

      // Checkstyle-enforced, reducing our code coverage percentage
      default:
        throw new RuntimeException("unsupported enum value " + function);
    }
    return new Dimension(result);
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy