/*
* JAME 6.2.1
* http://jame.sourceforge.net
*
* Copyright 2001, 2016 Andrea Medeghini
*
* This file is part of JAME.
*
* JAME is an application for creating fractals and other graphics artifacts.
*
* JAME is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* JAME 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with JAME. If not, see <http://www.gnu.org/licenses/>.
*
*/
package net.sf.jame.contextfree.parser;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import org.antlr.v4.runtime.Token;
class ASTModification extends ASTExpression {
public static final int SIZE = 9;
private EModClass modClass;
private Modification modData = new Modification();
private List<ASTModTerm> modExp = new ArrayList<ASTModTerm>();
private int entropyIndex;
private boolean canonical;
public ASTModification(Token location) {
super(true, false, EExpType.ModType, location);
this.modClass = EModClass.NotAClass;
this.entropyIndex = 0;
this.canonical = true;
}
public ASTModification(ASTModification mod, Token location) {
super(true, false, EExpType.ModType, location);
if (mod != null) {
modData.getRand64Seed().setSeed(0);
grab(mod);
} else {
this.modClass = EModClass.NotAClass;
}
}
public ASTModification(ASTModification mod) {
super(true, false, EExpType.ModType, mod.getLocation());
this.modClass = mod.modClass;
this.entropyIndex = mod.entropyIndex;
this.canonical = mod.canonical;
}
public Modification getModData() {
return modData;
}
public EModClass getModClass() {
return modClass;
}
public List<ASTModTerm> getModExp() {
return modExp;
}
public void setIsConstant(boolean isConstant) {
this.isConstant = isConstant;
}
public void setCanonical(boolean canonical) {
this.canonical = canonical;
}
public int getEntropyIndex() {
return entropyIndex;
}
public void setEntropyIndex(int entropyIndex) {
this.entropyIndex = entropyIndex;
}
public boolean isCanonial() {
return canonical;
}
public void grab(ASTModification mod) {
Rand64 oldEntropy = modData.getRand64Seed();
modData = mod.getModData();
modData.getRand64Seed().add(oldEntropy);
modExp.clear();
modExp.addAll(mod.getModExp());
modClass = mod.getModClass();
entropyIndex = (entropyIndex + mod.getEntropyIndex()) & 7;
isConstant = modExp.isEmpty();
canonical = mod.isCanonial();
}
public void makeCanonial() {
// Receive a vector of modification terms and return an ASTexpression with
// those terms rearranged into TRSSF canonical order. Duplicate terms are
// deleted with a warning.
List<ASTModTerm> temp = new ArrayList<ASTModTerm>(modExp);
modExp.clear();
ASTModTerm x = null;
ASTModTerm y = null;
ASTModTerm z = null;
ASTModTerm rot = null;
ASTModTerm skew = null;
ASTModTerm size = null;
ASTModTerm zsize = null;
ASTModTerm flip = null;
ASTModTerm xform = null;
for (ASTModTerm term : temp) {
switch (term.getModType()) {
case x:
x = term;
break;
case y:
y = term;
break;
case z:
z = term;
break;
case modification:
case transform:
xform = term;
break;
case rot:
rot = term;
break;
case size:
size = term;
break;
case zsize:
zsize = term;
break;
case skew:
skew = term;
break;
case flip:
flip = term;
break;
default:
modExp.add(term);
break;
}
}
if (x != null) modExp.add(x);
if (y != null) modExp.add(y);
if (z != null) modExp.add(z);
if (rot != null) modExp.add(rot);
if (size != null) modExp.add(size);
if (zsize != null) modExp.add(zsize);
if (skew != null) modExp.add(skew);
if (flip != null) modExp.add(flip);
if (xform != null) modExp.add(xform);
}
public void addEntropy(String name) {
int[] index = new int[1];
modData.getRand64Seed().xorString(name, index);
entropyIndex = index[0];
}
public void setVal(Modification[] mod, RTI rti) {
mod[0] = modData;
for (ASTModTerm term : modExp) {
term.evaluate(mod, false, rti);
}
}
@Override
public int evaluate(double[] result, int length, RTI rti) {
error("Improper evaluation of an adjustment expression");
return -1;
}
@Override
public void evaluate(Modification[] result, boolean shapeDest, RTI rti) {
if (shapeDest) {
result[0].concat(modData);
} else {
if (result[0].merge(modData)) {
if (rti != null) rti.colorConflict();
}
}
for (ASTModTerm term : modExp) {
term.evaluate(result, shapeDest, rti);
}
}
protected void evalConst() {
Map<EModType, EModClass> map = new HashMap<EModType, EModClass>();
map.put(EModType.unknown, EModClass.classByOrdinal(EModClass.NotAClass.getType()));
map.put(EModType.x, EModClass.classByOrdinal(EModClass.GeomClass.getType() | EModClass.PathOpClass.getType()));
map.put(EModType.y, EModClass.classByOrdinal(EModClass.GeomClass.getType() | EModClass.PathOpClass.getType()));
map.put(EModType.z, EModClass.ZClass);
map.put(EModType.xyz, EModClass.classByOrdinal(EModClass.NotAClass.getType()));
map.put(EModType.transform, EModClass.GeomClass);
map.put(EModType.size, EModClass.GeomClass);
map.put(EModType.sizexyz, EModClass.classByOrdinal(EModClass.GeomClass.getType() | EModClass.ZClass.getType()));
map.put(EModType.rot, EModClass.classByOrdinal(EModClass.GeomClass.getType() | EModClass.PathOpClass.getType()));
map.put(EModType.skew, EModClass.GeomClass);
map.put(EModType.flip, EModClass.GeomClass);
map.put(EModType.zsize, EModClass.ZClass);
map.put(EModType.hue, EModClass.HueClass);
map.put(EModType.sat, EModClass.SatClass);
map.put(EModType.bright, EModClass.BrightClass);
map.put(EModType.alpha, EModClass.AlphaClass);
map.put(EModType.hueTarg, EModClass.HueClass);
map.put(EModType.satTarg, EModClass.SatClass);
map.put(EModType.brightTarg, EModClass.BrightClass);
map.put(EModType.alphaTarg, EModClass.AlphaClass);
map.put(EModType.targHue, EModClass.HueTargetClass);
map.put(EModType.targSat, EModClass.SatTargetClass);
map.put(EModType.targBright, EModClass.BrightTargetClass);
map.put(EModType.targAlpha, EModClass.AlphaTargetClass);
map.put(EModType.time, EModClass.TimeClass);
map.put(EModType.timescale, EModClass.TimeClass);
map.put(EModType.param, EModClass.ParamClass);
map.put(EModType.x1, EModClass.PathOpClass);
map.put(EModType.y1, EModClass.PathOpClass);
map.put(EModType.x2, EModClass.PathOpClass);
map.put(EModType.y2, EModClass.PathOpClass);
map.put(EModType.xrad, EModClass.PathOpClass);
map.put(EModType.yrad, EModClass.PathOpClass);
map.put(EModType.modification, EModClass.InvalidClass);
int nonConstant = 0;
List<ASTModTerm> temp = new ArrayList<ASTModTerm>(modExp);
modExp.clear();
for (ASTModTerm term : temp) {
EModClass mc = map.get(term.getModType());
modClass = EModClass.classByOrdinal(modClass.getType() | mc.getType());
if (!term.isConstant())
nonConstant |= mc.getType();
boolean keepThisOne = (mc.getType() & nonConstant) != 0;
if (Builder.currentBuilder().isInPathContainer() && (mc.getType() & EModClass.ZClass.getType()) != 0) {
error("Z changes are not supported within paths");
}
if (Builder.currentBuilder().isInPathContainer() && (mc.getType() & EModClass.TimeClass.getType()) != 0) {
error("Time changes are not supported within paths");
}
try {
if (!keepThisOne) {
Modification[] mod = new Modification[1];
term.evaluate(mod, false, null);
modData = mod[0];
}
} catch (DeferUntilRuntimeException e) {
keepThisOne = true;
}
if (keepThisOne) {
if (term.getArguments() != null) {
term.getArguments().simplify();
}
modExp.add(term);
}
}
}
@Override
public ASTExpression simplify() {
evalConst();
return this;
}
@Override
public ASTExpression compile(ECompilePhase ph) {
for (ASTModTerm term : modExp) {
term.compile(ph);
}
switch (ph) {
case TypeCheck:
{
List<ASTModTerm> temp = new ArrayList<ASTModTerm>(modExp);
modExp.clear();
for (ListIterator<ASTModTerm> iterm = temp.listIterator(); iterm.hasNext();) {
ASTModTerm term = iterm.next();
if (term.getArguments() == null || term.getArguments().getType() != EExpType.NumericType) {
modExp.add(term);
}
int argcount = term.getArguments().evaluate(null, 0);
switch (term.getModType()) {
case x:
case y:
{
if (!iterm.hasNext()) {
break;
}
ASTModTerm next = iterm.next();
if (term.getModType() == EModType.x && next.getModType() == EModType.y && argcount == 1) {
term.setArguments(term.getArguments().append(next));
term.setArgumentsCount(2);
modExp.add(term);
continue;
}
}
break;
// Try to split the XYZ term into an XY term and a Z term. Drop the XY term
// if it is the identity. First try an all-constant route, then try to tease
// apart the arguments.
case xyz:
case sizexyz:
{
double[] d = new double[3];
if (term.getArguments().isConstant() && term.getArguments().evaluate(d, 3) != 3) {
term.setArguments(new ASTCons(location, new ASTReal(d[0], location), new ASTReal(d[1], location)));
term.setModType(term.getModType() == EModType.xyz ? EModType.x : EModType.size);
term.setArgumentsCount(2);
// Check if xy part is the identity transform and only save it if it is not
EModType ztype = term.getModType() == EModType.size ? EModType.zsize : EModType.z;
ASTModTerm zmod = new ASTModTerm(ztype, new ASTReal(d[2], location), location);
zmod.setArgumentsCount(1);
if (d[0] == 1.0 && d[1] == 1.0 && term.getModType() == EModType.size) {
// Drop xy term and just save z term if xy term
// is the identity transform
term.setArguments(zmod);
} else {
modExp.add(zmod);
}
modExp.add(term);
continue;
}
List<ASTExpression> xyzargs = extract(term.getArguments());
ASTExpression xyargs = null;
ASTExpression zargs = null;
for (ASTExpression arg : xyzargs) {
if (xyargs == null || xyargs.evaluate(null, 0) < 2) {
xyargs = append(xyargs, arg);
} else {
zargs = append(zargs, arg);
}
}
if (xyargs != null && zargs != null && xyargs.evaluate(null, 0) == 2) {
// We have successfully split the 3-tuple into a 2-tuple and a scalar
term.setArguments(xyargs);
term.setModType(term.getModType() == EModType.xyz ? EModType.x : EModType.size);
term.setArgumentsCount(2);
EModType ztype = term.getModType() == EModType.size ? EModType.zsize : EModType.z;
ASTModTerm zmod = new ASTModTerm(ztype, new ASTReal(d[2], location), location);
zmod.setArgumentsCount(1);
if (term.getModType() == EModType.size && xyargs.isConstant() && xyargs.evaluate(d, 2) == 2 && d[0] == 1.0 && d[1] == 1.0) {
// Drop xy term and just save z term if xy term
// is the identity transform
term.setArguments(zmod);
} else {
modExp.add(zmod);
}
} else {
// No dice, put it all back
xyargs = append(xyargs, zargs);
term.setArguments(xyargs);
}
modExp.add(term);
}
continue;
default:
break;
}
modExp.add(term);
}
isConstant = true;
locality = ELocality.PureLocal;
for (ASTModTerm term : modExp) {
isConstant = isConstant && term.isConstant();
locality = combineLocality(locality, term.getLocality());
StringBuilder ent = new StringBuilder();
term.entropy(ent);
addEntropy(ent.toString());
}
if (canonical) {
makeCanonial();
}
}
break;
case Simplify:
break;
default:
break;
}
return null;
}
public void concat(ASTModTerm t) {
// TODO Auto-generated method stub
}
}