diff --git a/desktop/ConstraintLayoutInspector/app/src/androidx/constraintLayout/desktop/ui/adapters/MEUI.java b/desktop/ConstraintLayoutInspector/app/src/androidx/constraintLayout/desktop/ui/adapters/MEUI.java index 9aeb0a10b..c5f1602e3 100644 --- a/desktop/ConstraintLayoutInspector/app/src/androidx/constraintLayout/desktop/ui/adapters/MEUI.java +++ b/desktop/ConstraintLayoutInspector/app/src/androidx/constraintLayout/desktop/ui/adapters/MEUI.java @@ -51,6 +51,10 @@ public static Color makeColor(int rgb, int darkRGB) { return !dark ? new Color(rgb) : new Color(darkRGB); } + public static Color makeColorWithAlpha(int rgba, int darkRGBA) { + return !dark ? new Color(rgba, true) : new Color(darkRGBA, true); + } + private static Color makeColor(String name, int rgb, int darkRGB) { return makeColor(rgb, darkRGB); } @@ -110,7 +114,7 @@ public static void invokeLater(Runnable runnable) { public static final Color ourMySelectedLineColor = new Color(0x3879d9); public static final Color ourAddConstraintColor = makeColor("UIDesigner.motion.AddConstraintColor", 0xff838383, 0xff666666); public static final Color ourAddConstraintPlus = makeColor("UIDesigner.motion.AddConstraintPlus", 0xffc9c9c9, 0xff333333); - + public static final Color ourDashedLineColor = makeColor(0xA0A0A0, 0xBBBBBB); public static void copy(MTag tag) { } @@ -118,7 +122,24 @@ public static void copy(MTag tag) { public static void cut(MTag mSelectedKeyFrame) { } - + /** List of colors with alpha = 0.7 for graphs. */ + public static Color[] graphColors = { + makeColorWithAlpha(0xa6bcc9b3, 0x8da9bab3), + makeColorWithAlpha(0xaee3feb3, 0xaee3feb3), + makeColorWithAlpha(0xf8a981b3, 0xf68f5bb3), + makeColorWithAlpha(0x89e69ab3, 0x67df7db3), + makeColorWithAlpha(0xb39bdeb3, 0x9c7cd4b3), + makeColorWithAlpha(0xea85aab3, 0xe46391b3), + makeColorWithAlpha(0x6de9d6b3, 0x49e4cdb3), + makeColorWithAlpha(0xe3d2abb3, 0xd9c28cb3), + makeColorWithAlpha(0x0ab4ffb3, 0x0095d6b3), + makeColorWithAlpha(0x1bb6a2b3, 0x138173b3), + makeColorWithAlpha(0x9363e3b3, 0x7b40ddb3), + makeColorWithAlpha(0xe26b27b3, 0xc1571ab3), + makeColorWithAlpha(0x4070bfb3, 0x335a99b3), + makeColorWithAlpha(0xc6c54eb3, 0xadac38b3), + makeColorWithAlpha(0xcb53a3b3, 0xb8388eb3), + makeColorWithAlpha(0x3d8effb3, 0x1477ffb3)}; public static final int DIR_LEFT = 0; public static final int DIR_RIGHT = 1; diff --git a/desktop/ConstraintLayoutInspector/app/src/androidx/constraintLayout/desktop/ui/timeline/GraphRender.java b/desktop/ConstraintLayoutInspector/app/src/androidx/constraintLayout/desktop/ui/timeline/GraphRender.java index 754bbf385..6af9f8d62 100644 --- a/desktop/ConstraintLayoutInspector/app/src/androidx/constraintLayout/desktop/ui/timeline/GraphRender.java +++ b/desktop/ConstraintLayoutInspector/app/src/androidx/constraintLayout/desktop/ui/timeline/GraphRender.java @@ -22,8 +22,12 @@ import androidx.constraintLayout.desktop.ui.timeline.graph.Oscillator; import androidx.constraintLayout.desktop.ui.ui.MeModel; import androidx.constraintLayout.desktop.ui.utils.Debug; +import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.Stroke; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; @@ -36,6 +40,7 @@ public class GraphRender { private Cycle mCycle = null; private Attribute[] mAttribute = null; private String[] mStartEndString = new String[2]; + private static boolean mShowNewGraph = false; static String[] ourWaveTypes = {"sin", "square", "triangle", "sawtooth", "reverseSawtooth", "cos", "bounce"}; static HashMap ourWaveTypeMap = new HashMap<>(); @@ -67,6 +72,10 @@ public boolean setUp(MeModel model, TimeLineRowData row) { return false; } + public void setShowNewGraph(boolean isNewGraph) { + mShowNewGraph = isNewGraph; + } + public String getValue(MTag kf, String keyProp) { MTag[] tag = kf.getChildTags(); if (tag != null && tag.length > 0) { @@ -120,7 +129,7 @@ public int compare(MTag t1, MTag t2) { } mCycle = new Cycle(); - mCycle.setCycle(pos, period, amp, offset, curveType); + mCycle.setCycle(pos, period, amp, offset, curveType, row); mCycle.fixRange(row.mKeyProp); return true; } @@ -185,13 +194,27 @@ public void draw(Graphics g, TimelineStructure mTimelineStructure, int x, int y, g.drawRect(x, y, w, h - 1); g.setColor(MEUI.Graph.ourG_line); + // with the new graph design, the ticks are drawn before the actual graph. + if (mShowNewGraph) { + g.setColor(MEUI.myGridColor); + TimeLineRow.drawTicks(g, mTimelineStructure, h, y); + } + if (mCycle != null) { - mCycle.plot(g, gx, y, gw, h); + if (mShowNewGraph) { + mCycle.plotAreaGraph(g, gx, y, gw, h); + } else { + mCycle.plot(g, gx, y, gw, h); + } } if (mAttribute != null) { for (int i = 0; i < mAttribute.length; i++) { Attribute attribute = mAttribute[i]; - attribute.plot(g, gx, y, gw, h); + if (mShowNewGraph) { + attribute.plotAreaGraph(g, gx, y, gw, h); + } else { + attribute.plot(g, gx, y, gw, h); + } } } @@ -251,9 +274,11 @@ static class Attribute { int[] yPoints = new int[2000]; double mMin; double mMax; + int colorIndex = 0; public Attribute(TimeLineRowData row, String attr, double startValue, double endValue) { mType = attr; + colorIndex = row.mKeyPropIndex; setup(row, attr, startValue, endValue); } @@ -334,6 +359,29 @@ public void plot(Graphics g, int x, int y, int w, int h) { g.drawPolyline(xPoints, yPoints, count); } } + + // plot the attribute with an Area Graph + public void plotAreaGraph(Graphics g, int x, int y, int w, int h) { + + if (spline != null) { + double steps = 1.0 / w; + // add the starting point for drawing a polygon instead of a ployline + xPoints[0] = x; + yPoints[0] = y + h; + int count = 1; + for (double i = steps; i <= 1; i += steps) { + double yp = spline.getPos(i, 0); + xPoints[count] = (int)(x + i * w); + yPoints[count] = (int)(y + h - (yp - mMin) * h / (mMax - mMin)); + count++; + } + // add the ending point for drawing a polygon instead of a ployline + xPoints[count] = x + w; + yPoints[count] = yPoints[0]; + g.setColor(MEUI.graphColors[colorIndex % MEUI.graphColors.length]); + g.fillPolygon(xPoints, yPoints, count + 1); + } + } } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -346,11 +394,18 @@ static class Cycle { float[] yMax = new float[xpos.length]; float[] yMin = new float[xpos.length]; float mMaxY, mMinY; - int[] xPoints = new int[xpos.length]; - int[] yPoints = new int[xpos.length]; + // add two more points (in start and end) for drawing a polygon + int numPoints = mShowNewGraph ? xpos.length + 2 : xpos.length; + int[] xPoints = new int[numPoints]; + int[] yPoints = new int[numPoints]; + int colorIndex = 0; // index for picking a color for the graph. + String attr = ""; + // Store the waveOffest Y position. + double[] offsetY = new double[xpos.length]; + int[] offsetYPoints = new int[xpos.length]; void setCycle(double[] pos, double[] period, double[] amplitude, double[] offset, - int curveType) { + int curveType, TimeLineRowData row) { if (pos.length == 1) { pos = new double[]{0.0, pos[0], 1.0}; period = new double[]{period[0], period[0], period[0]}; @@ -384,6 +439,8 @@ void setCycle(double[] pos, double[] period, double[] amplitude, double[] offset mOscillator = osc; mMaxY = -Float.MAX_VALUE; mMinY = Float.MAX_VALUE; + colorIndex = row.mKeyPropIndex; + attr = row.mKeyProp; for (int i = 0; i < xpos.length; i++) { xpos[i] = (float)(i / (xpos.length - 1.0f)); @@ -391,6 +448,7 @@ void setCycle(double[] pos, double[] period, double[] amplitude, double[] offset double off = mMonotoneSpline.getPos(xpos[i], 1); try { ypos[i] = mOscillator.getValue(xpos[i]) * amp + off; + offsetY[i] = off; } catch (Exception e) { ypos[i] = Math.random(); // visual hint that it is broken @@ -427,6 +485,52 @@ void plot(Graphics g, int x, int y, int w, int h) { g.drawPolyline(xPoints, yPoints, xPoints.length); } + void plotAreaGraph(Graphics g, int x, int y, int w, int h) { + + ((Graphics2D) g) + .setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + // add the starting point to draw the polygon + xPoints[0] = x; + yPoints[0] = y + (int)(h - h * (0 - mMinY) / (mMaxY - mMinY)); + int count = 1; + for (int i = 0; i < xpos.length; i++) { + int xp = (int)(w * xpos[i] + x); + int yp = y + (int)(h - h * (ypos[i] - mMinY) / (mMaxY - mMinY)); + xPoints[count] = xp; + yPoints[count] = yp; + count++; + + // points for drawing the waveOffset polyline + int offsetYP = y + (int)(h - h * (offsetY[i] - mMinY) / (mMaxY - mMinY)); + offsetYPoints[i] = offsetYP; + } + // add the ending point to draw the polygon + xPoints[count] = x + w; + yPoints[count] = yPoints[0]; + + // Draw a baseline where the y position is 0 + g.setColor(MEUI.myGridColor); + g.drawPolyline(Arrays.copyOfRange(xPoints, 1, xPoints.length - 1), Arrays.copyOfRange(yPoints, 1, yPoints.length - 1), count - 1); + + // Draw a line where the y position is 1 when the attribute is scaleX or scaleY + if (attr.equals("scaleX") || attr.equals("scaleY")) { + int yTop = y + (int)(h - h * (1 - mMinY) / (mMaxY - mMinY)); + g.drawLine(x, yTop, x + w, yTop); + } + + // Draw the animation graph + g.setColor(MEUI.graphColors[colorIndex % MEUI.graphColors.length]); + g.fillPolygon(xPoints, yPoints, count + 1); + + // Draw the waveOffset dashed line + Stroke dashed = new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0f, + new float[]{9}, 0f); + ((Graphics2D) g).setStroke(dashed); + g.setColor(MEUI.ourDashedLineColor); + g.drawPolyline(Arrays.copyOfRange(xPoints, 1, xPoints.length - 1), offsetYPoints, count - 1); + } + float getComputedValue(float v) { if (mMonotoneSpline == null) { return 0; diff --git a/desktop/ConstraintLayoutInspector/app/src/androidx/constraintLayout/desktop/ui/timeline/TimeLineRow.java b/desktop/ConstraintLayoutInspector/app/src/androidx/constraintLayout/desktop/ui/timeline/TimeLineRow.java index deae6818d..e1244b093 100644 --- a/desktop/ConstraintLayoutInspector/app/src/androidx/constraintLayout/desktop/ui/timeline/TimeLineRow.java +++ b/desktop/ConstraintLayoutInspector/app/src/androidx/constraintLayout/desktop/ui/timeline/TimeLineRow.java @@ -49,6 +49,7 @@ public class TimeLineRow extends JPanel { private boolean mHasGraph = true; private boolean mGraphOpen = false; GraphRender mGraph = new GraphRender(); + private boolean mShowNewGraph = true; @Override public void updateUI() { @@ -70,6 +71,7 @@ public void updateUI() { TimeLineRow(TimelineStructure timelineStructure) { setPreferredSize(MEUI.size(100, 20)); mTimelineStructure = timelineStructure; + mGraph.setShowNewGraph(mShowNewGraph); } @Override @@ -186,8 +188,10 @@ public void paint(Graphics g) { int gy = myRowHeight + ((mShowTitle) ? myTitleHeight : 0); mGraph.draw(g, mTimelineStructure, MEUI.ourLeftColumnWidth, gy, w - MEUI.ourLeftColumnWidth, myGraphHeight); } - g.setColor(MEUI.myGridColor); - drawTicks(g, mTimelineStructure, h); + if (!mShowNewGraph) { + g.setColor(MEUI.myGridColor); + TimeLineRow.drawTicks(g, mTimelineStructure, h); + } } public void drawArrow(Graphics g, int y) { @@ -216,9 +220,13 @@ public void drawArrow(Graphics g, int y) { } public static void drawTicks(Graphics g, TimelineStructure mTimelineStructure, int h) { + drawTicks(g, mTimelineStructure, h, 0); + } + + public static void drawTicks(Graphics g, TimelineStructure mTimelineStructure, int h, int y) { for (int i = 0; i < mTimelineStructure.myXTicksPixels.length; i++) { int x = mTimelineStructure.myXTicksPixels[i] + MEUI.ourLeftColumnWidth; - g.fillRect(x, 0, 1, h); + g.fillRect(x, y, 1, h); } } diff --git a/desktop/ConstraintLayoutInspector/app/src/androidx/constraintLayout/desktop/ui/timeline/TimeLineRowData.java b/desktop/ConstraintLayoutInspector/app/src/androidx/constraintLayout/desktop/ui/timeline/TimeLineRowData.java index 4d4d05067..657f9f317 100644 --- a/desktop/ConstraintLayoutInspector/app/src/androidx/constraintLayout/desktop/ui/timeline/TimeLineRowData.java +++ b/desktop/ConstraintLayoutInspector/app/src/androidx/constraintLayout/desktop/ui/timeline/TimeLineRowData.java @@ -32,6 +32,7 @@ public class TimeLineRowData { String mKeyProp; String mKeyPropToolTip; String mType; // Pos, Trig, Att, TCyc, Cyc + int mKeyPropIndex = 0; // used to map a property to a color private static String[] properties = { @@ -78,6 +79,15 @@ public class TimeLineRowData { nameMap.put("KeyAttribute", TYPE_KEY_ATTRIBUTE); } + // map a property to an index that is associated with a specific color + private static HashMap propertyMap = new HashMap<>(); + + static { + for (int i = 0; i < properties.length; i++) { + propertyMap.put(properties[i], i); + } + } + ArrayList mKeyFrames = new ArrayList<>(); MTag mStartConstraintSet; MTag mEndConstraintSet; @@ -108,12 +118,15 @@ public void buildTargetStrings(MTag keyFrame) { Debug.log(count + " " + properties[i]); } mKeyProp = properties[i]; + mKeyPropIndex = propertyMap.getOrDefault(mKeyProp, properties.length - 1); } } if (count > 1) { count = 0; mKeyProp = ""; mKeyPropToolTip = ""; + // show the a specific color for a composite of propierties + mKeyPropIndex = properties.length - 1; for (int i = 0; i < properties.length; i++) { if ((mask & (1 << i)) != 0) { if (count > 0) {