diff --git a/.gitignore b/.gitignore index 95570ebb6..ce3dadfa5 100644 --- a/.gitignore +++ b/.gitignore @@ -36,4 +36,5 @@ hs_err_pid*.log /libsrc/ffdec_lib/revision.txt /libsrc/jpproxy/nbproject/private/ /libsrc/jpproxy/dist/ -/libsrc/jpproxy/build/ \ No newline at end of file +/libsrc/jpproxy/build/ +/libsrc/LZMA/nbproject/private/ \ No newline at end of file diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/commonshape/Matrix.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/commonshape/Matrix.java index 957378c81..1489cc15d 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/commonshape/Matrix.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/exporters/commonshape/Matrix.java @@ -12,7 +12,8 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library. */ + * License along with this library. + */ package com.jpexs.decompiler.flash.exporters.commonshape; import com.jpexs.decompiler.flash.types.MATRIX; @@ -101,6 +102,22 @@ public class Matrix { public Point transform(Point point) { return transform(point.x, point.y); } + + public Point deltaTransform(double x, double y){ + Point result = new Point( + scaleX * x + rotateSkew1 * y, + rotateSkew0 * x + scaleY * y); + return result; + } + + public Point deltaTransform(Point point) { + return deltaTransform(point.x, point.y); + } + + public java.awt.Point deltaTransform(java.awt.Point point) { + Point p = deltaTransform(point.x, point.y); + return new java.awt.Point((int) p.x, (int) p.y); + } public java.awt.Point transform(java.awt.Point point) { Point p = transform(point.x, point.y); @@ -228,4 +245,48 @@ public class Matrix { public String toString() { return "[Matrix scale:" + scaleX + "," + scaleY + ", rotate:" + rotateSkew0 + "," + rotateSkew1 + ", translate:" + translateX + "," + translateY + "]"; } + + public Matrix inverse(Matrix m){ + double a=m.scaleX; + double b=m.rotateSkew0; + double c=m.rotateSkew1; + double d=m.scaleY; + double tx=m.translateX; + double ty=m.translateY; + + double a2=d/(a*d-b*c); + double b2=-b/(a*d-b*c); + double c2=-c/(a*d-b*c); + double d2=a*(a*d-b*c); + double tx2=(c*ty-d*tx)/(a*d-b*c); + double ty2=-(a*ty-b*tx)/(a*d-b*c); + + Matrix ret=new Matrix(); + ret.scaleX = a2; + ret.rotateSkew0 = b2; + ret.rotateSkew1 = c2; + ret.scaleY = d2; + ret.translateX = tx2; + ret.translateY = ty2; + return ret; + } + + + public double getTotalSkewAngleX(){ + Point px = deltaTransform(new Point(0,1)); + return ((180/Math.PI) * Math.atan2(px.y, px.x) - 90); + } + + public double getTotalSkewAngleY(){ + Point py = deltaTransform(new Point(1,0)); + return ((180/Math.PI) * Math.atan2(py.y, py.x)); + } + + public double getTotalScaleX(){ + return Math.sqrt(scaleX * scaleX + rotateSkew0 * rotateSkew0); + } + + public double getTotalScaleY(){ + return Math.sqrt(rotateSkew1 * rotateSkew1 + scaleY * scaleY); + } } diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/DepthState.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/DepthState.java index 116e55ac1..04444e660 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/DepthState.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/DepthState.java @@ -12,7 +12,8 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library. */ + * License along with this library. + */ package com.jpexs.decompiler.flash.timeline; import com.jpexs.decompiler.flash.SWF; @@ -48,6 +49,7 @@ public class DepthState { private final SWF swf; public Frame frame; public long instanceId; + public boolean motionTween = false; private static AtomicLong lastInstanceId = new AtomicLong(0); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java index 7f1d66fe9..4e8bfaa03 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/Timeline.java @@ -12,7 +12,8 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library. */ + * License along with this library. + */ package com.jpexs.decompiler.flash.timeline; import com.jpexs.decompiler.flash.SWF; @@ -27,6 +28,7 @@ import com.jpexs.decompiler.flash.tags.base.ButtonTag; import com.jpexs.decompiler.flash.tags.base.CharacterIdTag; import com.jpexs.decompiler.flash.tags.base.CharacterTag; import com.jpexs.decompiler.flash.tags.base.DrawableTag; +import com.jpexs.decompiler.flash.tags.base.MorphShapeTag; import com.jpexs.decompiler.flash.tags.base.PlaceObjectTypeTag; import com.jpexs.decompiler.flash.tags.base.RemoveTag; import com.jpexs.decompiler.flash.types.CLIPACTIONS; @@ -39,7 +41,9 @@ import java.awt.Rectangle; import java.awt.Shape; import java.awt.geom.Area; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.Stack; @@ -55,6 +59,7 @@ public class Timeline { public RECT displayRect; public int frameRate; public List tags; + public Map depthMaxFrame= new HashMap(); public int getMaxDepth() { int max_depth = 0; @@ -186,6 +191,51 @@ public class Timeline { if (tagAdded) { frames.add(frame); } + detectTweens(); + for(int d=1;d<=getMaxDepth();d++){ + for(int f=frames.size()-1;f>=0;f--){ + if(frames.get(f).layers.get(d) != null){ + depthMaxFrame.put(d, f+1); + break; + } + } + } + } + + private boolean compare(int a, int b, int c, int tolerance) { + return Math.abs((b - a) - (c - b))=frames.size()?null:frames.get(f).layers.get(d); + + if(f matrices=new ArrayList<>(); + for(int k=0;k ranges=TweenDetector.detectRanges(matrices); + for(TweenRange r:ranges){ + System.out.println(""+r); + for(int t = r.startPosition;t<=r.endPosition;t++){ + frames.get(f-len+t).layers.get(d).motionTween = true; + frames.get(f-len+t).layers.get(d).key = false; + } + frames.get(r.startPosition).layers.get(d).key = true; + } + } + len = 1; + } + characterId = ds==null?-1:ds.characterId; + } + } } public void getNeededCharacters(Set usedCharacters) { diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/TweenDetector.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/TweenDetector.java new file mode 100644 index 000000000..3aea640a2 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/TweenDetector.java @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2014 JPEXS + * + * This program 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. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.jpexs.decompiler.flash.timeline; + +import com.jpexs.decompiler.flash.types.MATRIX; +import java.util.ArrayList; +import java.util.List; + +/** + * + * NOT WORKING STUB!!! FIXME + * + * @author JPEXS + */ +public class TweenDetector { + + + public static List detectRanges(List matrices) { + //TODO: make this working :-( + return new ArrayList<>(); + /* + + List ret = new ArrayList<>(); + double tolerance = 1; + int min = 3; + int startpos = 0; + Double last = null; + int i = min; + List translateX=new ArrayList<>(); + List translateY=new ArrayList<>(); + List scaleX=new ArrayList<>(); + List scaleY=new ArrayList<>(); + List rotateSkew0=new ArrayList<>(); + List rotateSkew1=new ArrayList<>(); + + + Set ms=new HashSet(); + ms.addAll(matrices); + if(ms.size()==1){ + return new ArrayList<>(); + } + + + for(MATRIX n:matrices){ + //... + } + + for (; startpos + i <= matrices.size() + 1; i++) { + double errTranslateX = startpos + i > matrices.size() ? Double.MAX_VALUE : getErrorLevel(translateX, startpos, i); + double errTranslateY = startpos + i > matrices.size() ? Double.MAX_VALUE : getErrorLevel(translateY, startpos, i); + double errScaleX = startpos + i > matrices.size() ? Double.MAX_VALUE : getErrorLevel(scaleX, startpos, i); + double errScaleY = startpos + i > matrices.size() ? Double.MAX_VALUE : getErrorLevel(scaleY, startpos, i); + double errRotateSkew0 = startpos + i > matrices.size() ? Double.MAX_VALUE : getErrorLevel(rotateSkew0, startpos, i); + double errRotateSkew1 = startpos + i > matrices.size() ? Double.MAX_VALUE : getErrorLevel(rotateSkew1, startpos, i); + double err = startpos + i > matrices.size()?Double.MAX_VALUE:(errTranslateX/20+errTranslateY/20+0.1*errScaleX+0.1*errScaleY+errRotateSkew0/360+errRotateSkew1/360); + if (err > tolerance) { + if (last == null) { + startpos++; + i = min - 1; + continue; + } + ret.add(new TweenRange(startpos, startpos+i-1-1)); + startpos = startpos + i -1; + i = min - 1; + last = null; + continue; + } + last = err; + } + return ret;*/ + } + + private static double getErrorLevel(List yValues, int start, int len) { + double[] ret = calc(yValues, start, len); + double a = ret[0]; + double b = ret[1]; + double sumdelta = 0; + double maxdelta = 0; + for (int i = start; i < start + len; i++) { + double yorig = yValues.get(i); + double ynew = a + b * (i - start); + double ydelta = Math.abs(ynew - yorig); + sumdelta += ydelta; + if(ydelta>maxdelta){ + maxdelta = ydelta; + } + } + return maxdelta; //sumdelta / len; + } + + private static double[] calc(List yValues, int start, int len) { + List xValues = new ArrayList<>(); + for (int i = 0; i < len; i++) { + xValues.add((double)i); + } + + yValues = yValues.subList(start, start + len); + + // get the value of the gradient using the formula b = cov[x,y] / var[x] + double b = covariance(xValues, yValues) / variance(xValues); + // get the value of the y-intercept using the formula a = ybar + b * xbar + double a = mean(yValues) - b * mean(xValues); + return new double[]{a, b}; + } + + /* + * Calculate the covariance of two sets of data + * + * @param x + * The first set of data + * @param y + * The second set of data + * @return The covariance of x and y + */ + private static double covariance(List x, List y) { + double xmean = mean(x); + double ymean = mean(y); + + double result = 0; + + for (int i = 0; i < x.size(); i++) { + result += (x.get(i) - xmean) * (y.get(i) - ymean); + } + + result /= x.size() - 1; + + return result; + } + + /** + * Calculate the mean of a data set + * + * @param data The data set to calculate the mean of + * @return The mean of the data set + */ + private static double mean(List data) { + double sum = 0; + + for (int i = 0; i < data.size(); i++) { + sum += data.get(i); + } + + return sum / data.size(); + } + + /** + * Calculate the variance of a data set + * + * @param data The data set to calculate the variance of + * @return The variance of the data set + */ + private static double variance(List data) { + // Get the mean of the data set + double mean = mean(data); + + double sumOfSquaredDeviations = 0; + + // Loop through the data set + for (int i = 0; i < data.size(); i++) { + // sum the difference between the data element and the mean squared + sumOfSquaredDeviations += Math.pow(data.get(i) - mean, 2); + } + + // Divide the sum by the length of the data set - 1 to get our result + return sumOfSquaredDeviations / (data.size() - 1); + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/TweenRange.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/TweenRange.java new file mode 100644 index 000000000..1fcf5a34a --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/timeline/TweenRange.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2014 JPEXS + * + * This program 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. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.jpexs.decompiler.flash.timeline; + +/** + * + * @author JPEXS + */ +public class TweenRange { + public int startPosition; + public int endPosition; + + public TweenRange(int startPosition, int endPosition) { + this.startPosition = startPosition; + this.endPosition = endPosition; + } + + @Override + public String toString() { + return "["+startPosition+"-"+endPosition+"]"; + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/MATRIX.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/MATRIX.java index 1ff93b980..c25aaa0fb 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/MATRIX.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/types/MATRIX.java @@ -12,7 +12,8 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library. */ + * License along with this library. + */ package com.jpexs.decompiler.flash.types; import com.jpexs.decompiler.flash.types.annotations.Calculated; @@ -177,4 +178,49 @@ public class MATRIX implements Serializable { public boolean isEmpty() { return (translateX == 0) && (translateY == 0) && (!hasRotate) && (!hasScale); } + + @Override + public int hashCode() { + int hash = 7; + hash = 37 * hash + this.getScaleX(); + hash = 37 * hash + this.getScaleY(); + hash = 37 * hash + this.getRotateSkew0(); + hash = 37 * hash + this.getRotateSkew1(); + hash = 37 * hash + this.translateX; + hash = 37 * hash + this.translateY; + return hash; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final MATRIX other = (MATRIX) obj; + if (this.getScaleX() != other.getScaleX()) { + return false; + } + if (this.getScaleY() != other.getScaleY()) { + return false; + } + if (this.getRotateSkew0() != other.getRotateSkew0()) { + return false; + } + if (this.getRotateSkew1() != other.getRotateSkew1()) { + return false; + } + if (this.translateX != other.translateX) { + return false; + } + if (this.translateY != other.translateY) { + return false; + } + return true; + } + + + } diff --git a/src/com/jpexs/decompiler/flash/gui/Main.java b/src/com/jpexs/decompiler/flash/gui/Main.java index f46b35b1d..2f00d24ad 100644 --- a/src/com/jpexs/decompiler/flash/gui/Main.java +++ b/src/com/jpexs/decompiler/flash/gui/Main.java @@ -30,7 +30,10 @@ import com.jpexs.decompiler.flash.console.ContextMenuTools; import com.jpexs.decompiler.flash.gui.proxy.ProxyFrame; import com.jpexs.decompiler.flash.helpers.SWFDecompilerPlugin; import com.jpexs.decompiler.flash.tags.base.FontTag; +import com.jpexs.decompiler.flash.timeline.Timeline; +import com.jpexs.decompiler.flash.timeline.TweenDetector; import com.jpexs.decompiler.flash.treeitems.SWFList; +import com.jpexs.decompiler.flash.types.MATRIX; import com.jpexs.helpers.Cache; import com.jpexs.helpers.CancellableWorker; import com.jpexs.helpers.Helper; @@ -50,6 +53,7 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.awt.geom.AffineTransform; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.File; @@ -877,7 +881,8 @@ public class Main { * @throws IOException */ public static void main(String[] args) throws IOException { - + + String pluginPath = Configuration.pluginPath.get(); if (pluginPath != null && !pluginPath.isEmpty()) { try { diff --git a/src/com/jpexs/decompiler/flash/gui/timeline/TimelineBodyPanel.java b/src/com/jpexs/decompiler/flash/gui/timeline/TimelineBodyPanel.java index 6ebc516c0..4329bf26e 100644 --- a/src/com/jpexs/decompiler/flash/gui/timeline/TimelineBodyPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/timeline/TimelineBodyPanel.java @@ -16,6 +16,8 @@ */ package com.jpexs.decompiler.flash.gui.timeline; +import com.jpexs.decompiler.flash.tags.base.CharacterTag; +import com.jpexs.decompiler.flash.tags.base.MorphShapeTag; import com.jpexs.decompiler.flash.timeline.DepthState; import com.jpexs.decompiler.flash.timeline.Timeline; import java.awt.Color; @@ -41,6 +43,8 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe private final Timeline timeLine; + public static Color motionTweenColor = new Color(0x59, 0xfe, 0x7c); + public static Color shapeTweenColor = new Color(0xd1, 0xac, 0xf1); public static Color frameColor = new Color(0xbd, 0xd8, 0xfc); public static Color emptyFrameColor = Color.white; public static Color emptyFrameSecondColor = new Color(0xea, 0xf2, 0xfc); @@ -127,10 +131,27 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe for (int f = start_f; f <= end_f; f++) { for (int d = start_d; d <= end_d; d++) { DepthState fl = timeLine.frames.get(f).layers.get(d); + boolean motionTween = fl == null ? false : fl.motionTween; + DepthState flNext = null; if (f < max_f) { flNext = timeLine.frames.get(f + 1).layers.get(d); } + DepthState flPrev = null; + if (f > 0) { + flPrev = timeLine.frames.get(f - 1).layers.get(d); + } + + CharacterTag cht = fl == null ? null : timeLine.swf.characters.get(fl.characterId); + boolean shapeTween = cht != null && (cht instanceof MorphShapeTag); + boolean motionTweenStart = (!motionTween) && (flNext != null && flNext.motionTween); + boolean motionTweenEnd = (!motionTween) && (flPrev != null && flPrev.motionTween); + //boolean shapeTweenStart = shapeTween && (flPrev == null || flPrev.characterId != fl.characterId); + //boolean shapeTweenEnd = shapeTween && (flNext == null || flNext.characterId != fl.characterId); + + if (motionTweenStart || motionTweenEnd) { + motionTween = true; + } boolean selected = false; if (cursor != null) { if (f == cursor.x && d == cursor.y) { @@ -138,14 +159,77 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe } } if (selected) { - if (!(fl != null && (flNext == null || flNext.key))) { + //if (!(fl != null && (flNext == null || flNext.key))) { g.setColor(selectedColor); g.fillRect(f * TimelinePanel.FRAME_WIDTH + 1, d * TimelinePanel.FRAME_HEIGHT + 1, TimelinePanel.FRAME_WIDTH - 1, TimelinePanel.FRAME_HEIGHT - 1); - } + //} } if (fl == null) { + if (timeLine.depthMaxFrame.containsKey(d) && f < timeLine.depthMaxFrame.get(d)) { + int draw_f = f; + + DepthState prev_ds = f<1?null:timeLine.frames.get(f-1).layers.get(d); + + + if (f==0 || prev_ds!=null) { + draw_f = f; + keyfound[d - start_d] = true; + } else if (!keyfound[d - start_d]) { + for (; draw_f >= 0; draw_f--) { + if (timeLine.frames.get(draw_f).layers.get(d) != null) { + if (timeLine.frames.get(draw_f).layers.get(d).characterId != -1) { + break; + } + } + } + } else { + continue; + } + int num_frames = 1; + for (int i = draw_f + 1; i < timeLine.frames.size(); i++) { + if (timeLine.frames.get(i).layers.get(d) != null) { + if (timeLine.frames.get(i).layers.get(d).characterId != -1) { + break; + } + } + num_frames++; + } + g.setColor(emptyFrameColor); + g.fillRect(draw_f * TimelinePanel.FRAME_WIDTH, d * TimelinePanel.FRAME_HEIGHT, num_frames * TimelinePanel.FRAME_WIDTH, TimelinePanel.FRAME_HEIGHT); + g.setColor(borderColor); + g.drawRect(draw_f * TimelinePanel.FRAME_WIDTH, d * TimelinePanel.FRAME_HEIGHT, num_frames * TimelinePanel.FRAME_WIDTH, TimelinePanel.FRAME_HEIGHT); + + + if (selected) { + g.setColor(selectedColor); + g.fillRect(draw_f * TimelinePanel.FRAME_WIDTH, d * TimelinePanel.FRAME_HEIGHT, TimelinePanel.FRAME_WIDTH, TimelinePanel.FRAME_HEIGHT); + } + + g.setColor(keyColor); + g.drawOval(draw_f * TimelinePanel.FRAME_WIDTH + TimelinePanel.FRAME_WIDTH / 4, d * TimelinePanel.FRAME_HEIGHT + TimelinePanel.FRAME_HEIGHT * 3 / 4 - TimelinePanel.FRAME_WIDTH / 2 / 2, TimelinePanel.FRAME_WIDTH / 2, TimelinePanel.FRAME_WIDTH / 2); + + + if (num_frames > 1) { + if (cursor != null && cursor.y == d && cursor.x == f + num_frames - 1) { + g.setColor(selectedColor); + g.fillRect((f + num_frames - 1) * TimelinePanel.FRAME_WIDTH + 1, d * TimelinePanel.FRAME_HEIGHT + 1, TimelinePanel.FRAME_WIDTH - 1, TimelinePanel.FRAME_HEIGHT - 1); + } + + g.setColor(stopColor); + g.fillRect((draw_f + num_frames - 1) * TimelinePanel.FRAME_WIDTH + TimelinePanel.FRAME_WIDTH / 4, d * TimelinePanel.FRAME_HEIGHT + TimelinePanel.FRAME_HEIGHT / 2 - 2, TimelinePanel.FRAME_WIDTH / 2, TimelinePanel.FRAME_HEIGHT / 2); + g.setColor(stopBorderColor); + g.drawRect((draw_f + num_frames - 1) * TimelinePanel.FRAME_WIDTH + TimelinePanel.FRAME_WIDTH / 4, d * TimelinePanel.FRAME_HEIGHT + TimelinePanel.FRAME_HEIGHT / 2 - 2, TimelinePanel.FRAME_WIDTH / 2, TimelinePanel.FRAME_HEIGHT / 2); + + g.setColor(borderLinesColor); + for (int n = draw_f + 1; n < draw_f + num_frames; n++) { + g.drawLine(n * TimelinePanel.FRAME_WIDTH, d * TimelinePanel.FRAME_HEIGHT + 1, n * TimelinePanel.FRAME_WIDTH, d * TimelinePanel.FRAME_HEIGHT + borderLinesLength); + g.drawLine(n * TimelinePanel.FRAME_WIDTH, d * TimelinePanel.FRAME_HEIGHT + TimelinePanel.FRAME_HEIGHT - 1, n * TimelinePanel.FRAME_WIDTH, d * TimelinePanel.FRAME_HEIGHT + TimelinePanel.FRAME_HEIGHT - borderLinesLength); + } + } + } + if (d == 0) { if (timeLine.frames.get(f).action != null) { g.setColor(aColor); @@ -190,6 +274,15 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe g.setColor(frameColor); g.fillRect(draw_f * TimelinePanel.FRAME_WIDTH, d * TimelinePanel.FRAME_HEIGHT, num_frames * TimelinePanel.FRAME_WIDTH, TimelinePanel.FRAME_HEIGHT); + if (motionTween) { + g.setColor(motionTweenColor); + g.fillRect(draw_f * TimelinePanel.FRAME_WIDTH, d * TimelinePanel.FRAME_HEIGHT, num_frames * TimelinePanel.FRAME_WIDTH, TimelinePanel.FRAME_HEIGHT); + } + if (shapeTween) { + g.setColor(shapeTweenColor); + g.fillRect(draw_f * TimelinePanel.FRAME_WIDTH, d * TimelinePanel.FRAME_HEIGHT, num_frames * TimelinePanel.FRAME_WIDTH, TimelinePanel.FRAME_HEIGHT); + } + if (selected) { g.setColor(selectedColor); g.fillRect(draw_f * TimelinePanel.FRAME_WIDTH, d * TimelinePanel.FRAME_HEIGHT, TimelinePanel.FRAME_WIDTH, TimelinePanel.FRAME_HEIGHT); @@ -197,18 +290,30 @@ public class TimelineBodyPanel extends JPanel implements MouseListener, KeyListe g.setColor(borderColor); g.drawRect(draw_f * TimelinePanel.FRAME_WIDTH, d * TimelinePanel.FRAME_HEIGHT, num_frames * TimelinePanel.FRAME_WIDTH, TimelinePanel.FRAME_HEIGHT); + //} + if ((motionTween || shapeTween)) { + g.drawLine(draw_f * TimelinePanel.FRAME_WIDTH, d * TimelinePanel.FRAME_HEIGHT + TimelinePanel.FRAME_HEIGHT * 3 / 4, + draw_f * TimelinePanel.FRAME_WIDTH + num_frames * TimelinePanel.FRAME_WIDTH - TimelinePanel.FRAME_WIDTH / 2, d * TimelinePanel.FRAME_HEIGHT + TimelinePanel.FRAME_HEIGHT * 3 / 4 + ); + } g.setColor(keyColor); g.fillOval(draw_f * TimelinePanel.FRAME_WIDTH + TimelinePanel.FRAME_WIDTH / 4, d * TimelinePanel.FRAME_HEIGHT + TimelinePanel.FRAME_HEIGHT * 3 / 4 - TimelinePanel.FRAME_WIDTH / 2 / 2, TimelinePanel.FRAME_WIDTH / 2, TimelinePanel.FRAME_WIDTH / 2); + if ((motionTween || shapeTween)) { + g.fillOval((draw_f + num_frames - 1) * TimelinePanel.FRAME_WIDTH + TimelinePanel.FRAME_WIDTH / 4, d * TimelinePanel.FRAME_HEIGHT + TimelinePanel.FRAME_HEIGHT * 3 / 4 - TimelinePanel.FRAME_WIDTH / 2 / 2, TimelinePanel.FRAME_WIDTH / 2, TimelinePanel.FRAME_WIDTH / 2); + } + if (num_frames > 1) { if (cursor != null && cursor.y == d && cursor.x == f + num_frames - 1) { g.setColor(selectedColor); g.fillRect((f + num_frames - 1) * TimelinePanel.FRAME_WIDTH + 1, d * TimelinePanel.FRAME_HEIGHT + 1, TimelinePanel.FRAME_WIDTH - 1, TimelinePanel.FRAME_HEIGHT - 1); } - g.setColor(stopColor); - g.fillRect((draw_f + num_frames - 1) * TimelinePanel.FRAME_WIDTH + TimelinePanel.FRAME_WIDTH / 4, d * TimelinePanel.FRAME_HEIGHT + TimelinePanel.FRAME_HEIGHT / 2 - 2, TimelinePanel.FRAME_WIDTH / 2, TimelinePanel.FRAME_HEIGHT / 2); - g.setColor(stopBorderColor); - g.drawRect((draw_f + num_frames - 1) * TimelinePanel.FRAME_WIDTH + TimelinePanel.FRAME_WIDTH / 4, d * TimelinePanel.FRAME_HEIGHT + TimelinePanel.FRAME_HEIGHT / 2 - 2, TimelinePanel.FRAME_WIDTH / 2, TimelinePanel.FRAME_HEIGHT / 2); + if (!(motionTween || shapeTween)) { + g.setColor(stopColor); + g.fillRect((draw_f + num_frames - 1) * TimelinePanel.FRAME_WIDTH + TimelinePanel.FRAME_WIDTH / 4, d * TimelinePanel.FRAME_HEIGHT + TimelinePanel.FRAME_HEIGHT / 2 - 2, TimelinePanel.FRAME_WIDTH / 2, TimelinePanel.FRAME_HEIGHT / 2); + g.setColor(stopBorderColor); + g.drawRect((draw_f + num_frames - 1) * TimelinePanel.FRAME_WIDTH + TimelinePanel.FRAME_WIDTH / 4, d * TimelinePanel.FRAME_HEIGHT + TimelinePanel.FRAME_HEIGHT / 2 - 2, TimelinePanel.FRAME_WIDTH / 2, TimelinePanel.FRAME_HEIGHT / 2); + } g.setColor(borderLinesColor); for (int n = draw_f + 1; n < draw_f + num_frames; n++) { g.drawLine(n * TimelinePanel.FRAME_WIDTH, d * TimelinePanel.FRAME_HEIGHT + 1, n * TimelinePanel.FRAME_WIDTH, d * TimelinePanel.FRAME_HEIGHT + borderLinesLength);