/** * Catroid: An on-device visual programming system for Android devices * Copyright (C) 2010-2014 The Catrobat Team * (<http://developer.catrobat.org/credits>) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * An additional term exception under section 7 of the GNU Affero * General Public License, version 3, is available at * http://developer.catrobat.org/license_additional_term * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.catrobat.html5player.client.formulaeditor; import java.io.Serializable; import org.catrobat.html5player.client.Sprite; import org.catrobat.html5player.client.Stage; public class FormulaElement implements Serializable { private static final long serialVersionUID = 1L; public static enum ElementType { OPERATOR, FUNCTION, NUMBER, SENSOR, USER_VARIABLE, BRACKET } public static final Double NOT_EXISTING_USER_VARIABLE_INTERPRETATION_VALUE = 0d; private ElementType type; private String value; private FormulaElement leftChild = null; private FormulaElement rightChild = null; private transient FormulaElement parent = null; public FormulaElement(ElementType type, String value, FormulaElement parent) { this.type = type; this.value = value; this.parent = parent; } public FormulaElement(ElementType type, String value, FormulaElement parent, FormulaElement leftChild, FormulaElement rightChild) { this.type = type; this.value = value; this.parent = parent; this.leftChild = leftChild; this.rightChild = rightChild; if (leftChild != null) { this.leftChild.parent = this; } if (rightChild != null) { this.rightChild.parent = this; } } public String getValue() { return value; } public FormulaElement getRoot() { FormulaElement root = this; while (root.getParent() != null) { root = root.getParent(); } return root; } public Double interpretRecursive(Sprite sprite) { Double returnValue = 0d; switch (type) { case BRACKET: returnValue = rightChild.interpretRecursive(sprite); break; case NUMBER: returnValue = Double.parseDouble(value); break; case OPERATOR: Operators operator = Operators.getOperatorByValue(value); returnValue = interpretOperator(operator, sprite); break; case FUNCTION: Functions function = Functions.getFunctionByValue(value); returnValue = interpretFunction(function, sprite); break; case SENSOR: returnValue = getResultFromSensor(value, sprite); break; case USER_VARIABLE: UserVariablesContainer userVariables = Stage.getInstance().getUserVariables(); UserVariable userVariable = userVariables.getUserVariable(value, sprite.getName()); if (userVariable == null) { returnValue = NOT_EXISTING_USER_VARIABLE_INTERPRETATION_VALUE; break; } returnValue = userVariable.getValue(); break; } returnValue = checkDegeneratedDoubleValues(returnValue); return returnValue; } private double getResultFromSensor(String sensorValue, Sprite sprite){ double result = 0.0F; Sensors sensor = Sensors.getSensorByValue(sensorValue); if (sensor.isLookSensor) { result = interpretLookSensor(sensor, sprite); } else{ switch (sensor) { case COMPASS_DIRECTION: result = interpretCompass(sensor, sprite); break; default: break; } } return result; } private double interpretCompass(Sensors sensor,Sprite sprite) { return 0.0F; } private Double interpretFunction(Functions function, Sprite sprite) { Double left = null; if (leftChild != null) { left = leftChild.interpretRecursive(sprite); } switch (function) { case SIN: return java.lang.Math.sin(Math.toRadians(left)); case COS: return java.lang.Math.cos(Math.toRadians(left)); case TAN: return java.lang.Math.tan(Math.toRadians(left)); case LN: return java.lang.Math.log(left); case LOG: return java.lang.Math.log10(left); case SQRT: return java.lang.Math.sqrt(left); case RAND: Double right = rightChild.interpretRecursive(sprite); Double minimum; Double maximum; if (right > left) { minimum = left; maximum = right; } else { minimum = right; maximum = left; } Double randomDouble = minimum + (java.lang.Math.random() * (maximum - minimum)); if (isInteger(minimum) && isInteger(maximum) && !(rightChild.type == ElementType.NUMBER && rightChild.value.contains(".")) && !(leftChild.type == ElementType.NUMBER && leftChild.value.contains("."))) { if ((Math.abs(randomDouble) - (int) Math.abs(randomDouble)) >= 0.5) { return Double.valueOf(randomDouble.intValue()) + 1; } else { return Double.valueOf(randomDouble.intValue()); } } else { return randomDouble; } case ABS: return java.lang.Math.abs(left); case ROUND: return (double) java.lang.Math.round(left); case PI: return java.lang.Math.PI; case MOD: return (double) 0; //TODO case ARCSIN: return java.lang.Math.asin(left); case ARCCOS: return java.lang.Math.acos(left); case ARCTAN: return java.lang.Math.atan(left); case MIN: return (double) java.lang.Math.min(0, 0); //TODO case MAX: return (double) java.lang.Math.max(0, 0); //TODO case EXP: return (double) java.lang.Math.exp(left); case TRUE: return (double) 1; case FALSE: return (double) 0; } return 0d; } private Double interpretOperator(Operators operator, Sprite sprite) { if (leftChild != null) {// binär operator Double left = leftChild.interpretRecursive(sprite); Double right = rightChild.interpretRecursive(sprite); switch (operator) { case PLUS: return left + right; case MINUS: return left - right; case MULT: return left * right; case DIVIDE: return left / right; case POW: return java.lang.Math.pow(left, right); case EQUAL: return left.equals(right) ? 1d : 0d; //TODO Double equality, may round first? case NOT_EQUAL: return left.equals(right) ? 0d : 1d;//TODO Double equality, may round first? case GREATER_THAN: return left.compareTo(right) > 0 ? 1d : 0d; case GREATER_OR_EQUAL: return left.compareTo(right) >= 0 ? 1d : 0d; case SMALLER_THAN: return left.compareTo(right) < 0 ? 1d : 0d; case SMALLER_OR_EQUAL: return left.compareTo(right) <= 0 ? 1d : 0d; case LOGICAL_AND: return (left * right) != 0d ? 1d : 0d; case LOGICAL_OR: return left != 0d || right != 0d ? 1d : 0d; default: break; } } else {//unary operators Double right = rightChild.interpretRecursive(sprite); switch (operator) { case MINUS: return -right; case LOGICAL_NOT: return right == 0d ? 1d : 0d; default: break; } } return 0d; } private Double interpretLookSensor(Sensors sensor, Sprite sprite) { Double returnValue = 0d; switch (sensor) { case OBJECT_BRIGHTNESS: //TODO, Brick-basis not working properly returnValue = (double) sprite.getLook().getBrightnessValue(); break; case OBJECT_GHOSTEFFECT: returnValue = (((double) sprite.getLook().getAlphaValue()*100)-100)*(-1); break; case OBJECT_LAYER: if((double) sprite.getLook().getZPosition() == 0) returnValue = (double) 1; else returnValue = Math.abs((double) sprite.getLook().getZPosition()); break; case OBJECT_ROTATION: returnValue = (double) -(sprite.getLook().getRotation()-90); break; case OBJECT_SIZE: returnValue = (double) sprite.getLook().getSize()*100; break; case OBJECT_X: returnValue = (double) sprite.getLook().getMiddleX(); break; case OBJECT_Y: returnValue = (double) -sprite.getLook().getMiddleY(); break; default: break; } return returnValue; } private Double checkDegeneratedDoubleValues(Double valueToCheck) { if (valueToCheck.doubleValue() == Double.NEGATIVE_INFINITY) { return -Double.MAX_VALUE; } if (valueToCheck.doubleValue() == Double.POSITIVE_INFINITY) { return Double.MAX_VALUE; } if (valueToCheck.isNaN()) { return 1.0; } return valueToCheck; } public FormulaElement getParent() { return parent; } public void setRightChild(FormulaElement rightChild) { this.rightChild = rightChild; this.rightChild.parent = this; } public void setLeftChild(FormulaElement leftChild) { this.leftChild = leftChild; this.leftChild.parent = this; } public void replaceElement(FormulaElement current) { parent = current.parent; leftChild = current.leftChild; rightChild = current.rightChild; value = current.value; type = current.type; if (leftChild != null) { leftChild.parent = this; } if (rightChild != null) { rightChild.parent = this; } } public void replaceElement(ElementType type, String value) { this.value = value; this.type = type; } public void replaceWithSubElement(String operator, FormulaElement rightChild) { FormulaElement cloneThis = new FormulaElement(ElementType.OPERATOR, operator, this.getParent(), this, rightChild); cloneThis.parent.rightChild = cloneThis; } private boolean isInteger(double value) { if ((Math.abs(value) - (int) Math.abs(value) < Double.MIN_VALUE)) { return true; } return false; } public boolean isLogicalOperator() { if (type == ElementType.OPERATOR) { return Operators.getOperatorByValue(value).isLogicalOperator; } return false; } public boolean containsElement(ElementType elementType) { if (type.equals(elementType)) { return true; } if (leftChild != null && leftChild.containsElement(elementType)) { return true; } if (rightChild != null && rightChild.containsElement(elementType)) { return true; } return false; } public boolean isSingleNumberFormula() { if (type == ElementType.OPERATOR) { Operators operator = Operators.getOperatorByValue(value); if (operator == Operators.MINUS && leftChild == null) { return rightChild.isSingleNumberFormula(); } return false; } else if (type == ElementType.NUMBER) { return true; } return false; } public FormulaElement clone() { FormulaElement leftChildClone = leftChild == null ? null : leftChild.clone(); FormulaElement rightChildClone = rightChild == null ? null : rightChild.clone(); return new FormulaElement(type, new String(value == null ? "" : value), null, leftChildClone, rightChildClone); } }