From cbbaca635e1d23fc8806fbb45350f15b9ae8d501 Mon Sep 17 00:00:00 2001 From: John Hoford Date: Fri, 26 May 2023 13:07:22 -0700 Subject: [PATCH 1/2] vp in composeConstraintLayout --- .../extension/util/UiDelegate.java | 13 + .../constraintlayout/extension/util/Vp.java | 324 ++++++++++++ .../extension/util/VpGraphCompose.kt | 108 ++++ .../extension/util/VpGraphCore.kt | 451 ++++++++++++++++ .../extension/util/VpGraphCoreOrig.java | 483 ++++++++++++++++++ .../java/com/example/constraintlayout/test.kt | 70 ++- 6 files changed, 1434 insertions(+), 15 deletions(-) create mode 100644 projects/ComposeConstraintLayout/app/src/main/java/androidx/constraintlayout/extension/util/UiDelegate.java create mode 100644 projects/ComposeConstraintLayout/app/src/main/java/androidx/constraintlayout/extension/util/Vp.java create mode 100644 projects/ComposeConstraintLayout/app/src/main/java/androidx/constraintlayout/extension/util/VpGraphCompose.kt create mode 100644 projects/ComposeConstraintLayout/app/src/main/java/androidx/constraintlayout/extension/util/VpGraphCore.kt create mode 100644 projects/ComposeConstraintLayout/app/src/main/java/androidx/constraintlayout/extension/util/VpGraphCoreOrig.java diff --git a/projects/ComposeConstraintLayout/app/src/main/java/androidx/constraintlayout/extension/util/UiDelegate.java b/projects/ComposeConstraintLayout/app/src/main/java/androidx/constraintlayout/extension/util/UiDelegate.java new file mode 100644 index 000000000..a655f174b --- /dev/null +++ b/projects/ComposeConstraintLayout/app/src/main/java/androidx/constraintlayout/extension/util/UiDelegate.java @@ -0,0 +1,13 @@ +package androidx.constraintlayout.extension.util; + +public interface UiDelegate { + public boolean post(Runnable runnable); + + public boolean postDelayed(Runnable runnable, long delayMillis); + + public void invalidate(); + + public int getWidth(); + + public int getHeight(); +} diff --git a/projects/ComposeConstraintLayout/app/src/main/java/androidx/constraintlayout/extension/util/Vp.java b/projects/ComposeConstraintLayout/app/src/main/java/androidx/constraintlayout/extension/util/Vp.java new file mode 100644 index 000000000..1022f40d7 --- /dev/null +++ b/projects/ComposeConstraintLayout/app/src/main/java/androidx/constraintlayout/extension/util/Vp.java @@ -0,0 +1,324 @@ +package androidx.constraintlayout.extension.util; + +import java.util.HashMap; + +public class Vp { + static HashMap channels = new HashMap<>(); + + static class MSChannel { + int mSize = 32000; + String mName; + int mLast = 0; + int mWritten = 0; + long[] mTime = new long[mSize]; + + private MSChannel() { + + } + + private MSChannel(int size) { + mSize = size; + mTime = new long[mSize]; + } + + int getLatest() { + if (mWritten == 0) { + return -1; + } + return (mLast == 0 ? mSize : mLast) - 1; + } + + long getPos() { + int written; + int last; + synchronized (this) { + written = mWritten; + last = mLast; + } + return ((long) written) << 32 | last; + } + + void increment() { + synchronized (this) { + mLast++; + if (mLast == mSize) { + mLast = 0; + } + mWritten++; + } + } + + synchronized int getLast() { + return mLast; + } + } + + // ======================== Support for float ============================== + static class MSChannelFloat extends MSChannel { + float[] mData = new float[mSize]; + + MSChannelFloat(String name) { + this.mName = name; + } + + MSChannelFloat(String name, int size) { + super(size); + this.mName = name; + mData = new float[size]; + } + + void add(long time, float value) { + int last = getLast(); + mTime[last] = time; + mData[last] = value; + increment(); + } + + + public int get(long start, long[] time, float[] value) { + int count = 0; + + int written; + int last, first; + long firstTime; + long lastTime; + synchronized (this) { + written = mWritten; + last = mLast; + + if (written > mSize) { + firstTime = mTime[(last + 1) % mSize]; + } else { + firstTime = mTime[last - written]; + } + int lt = last - 1; + lastTime = mTime[lt >= 0 ? lt : lt + mSize]; + } + int startIndex = -1; + if (mWritten > mSize && mLast != mSize - 1) { // it has wrapped + if (mTime[mSize - 1] > start) { + startIndex = search(mTime, last, mSize - 1, start); + } else { + startIndex = search(mTime, 0, last, start); + } + } else { + startIndex = search(mTime, 0, last, start); + } + return getIndex(startIndex, time, value); + } + + static int search(long arr[], int low, int high, long key) { + int pos = -1; + while (low <= high) { + pos = low + (high - low) / 2; + if (arr[pos] == key) + return pos; + + if (arr[pos] < key) + low = pos + 1; + else + high = pos - 1; + } + return pos; + } + + public void findIndex(long[] time, int start, int end, int key) { + + } + + + public int getIndex(int startIndex, long[] time, float[] value) { + int len = time.length; + if (len + startIndex < mSize) { + System.arraycopy(mTime, startIndex, time, 0, len); + System.arraycopy(mData, startIndex, value, 0, len); + return (len + startIndex > mLast) ? mLast - startIndex : len; + } else { + int block1len = mSize - startIndex; + System.arraycopy(mTime, startIndex, time, 0, block1len); + System.arraycopy(mData, startIndex, value, 0, block1len); + int copyLen = (mLast > len - block1len) ? len - block1len : mLast; + System.arraycopy(mTime, 0, time, block1len, copyLen); + System.arraycopy(mData, 0, value, block1len, copyLen); + return block1len + copyLen; + } + + } + + int getLatest(long[] time, float[] value) { + int written; + int last; + synchronized (this) { + written = mWritten; + last = mLast; + } + int maxLen = time.length; + if (written == 0) return -1; + if (maxLen > written) { + maxLen = (int) mWritten; + } + if (maxLen > mSize) { + maxLen = mSize; + } + if (last - maxLen < 0) { // copy [0....last, size-(maxLen-last)...size-1] + int copy1src = 0; + int copy1Dest = maxLen - last; + int copy1len = last; + + System.arraycopy(mTime, copy1src, time, copy1Dest, copy1len); + System.arraycopy(mData, copy1src, value, copy1Dest, copy1len); + int copy2src = mSize - (maxLen - last); + int copy2Dest = 0; + int copy2len = maxLen - last; + + System.arraycopy(mTime, copy2src, time, copy2Dest, copy2len); + System.arraycopy(mData, copy2src, value, copy2Dest, copy2len); + } else { + int copy1src = last - maxLen; + int copy1Dest = 0; + int copy1len = maxLen; + System.arraycopy(mTime, copy1src, time, copy1Dest, copy1len); + System.arraycopy(mData, copy1src, value, copy1Dest, copy1len); + } + + return maxLen; + } + } + + public static void initChannel(String channel, int size) { + channels.put(channel, new MSChannelFloat(channel, size)); + } + + + public static void send(String channel, float value) { + send(channel, System.nanoTime(), value); + } + + public static void send(String channel, long time, float value) { + MSChannelFloat c = (MSChannelFloat) channels.get(channel); + if (c == null) { + c = new MSChannelFloat(channel); + channels.put(channel, c); + } + c.add(time, value); + } + + public static int getLatest(String channel, long[] times, float[] values) { + MSChannelFloat c = (MSChannelFloat) channels.get(channel); + if (c == null) { + return -1; + } + return c.getLatest(times, values); + } + + public static int totalWritten(String channel) { + MSChannelFloat c = (MSChannelFloat) channels.get(channel); + if (c == null) { + return -1; + } + return c.mWritten; + } + + public static int getAfter(String channel, long time, long[] times, float[] values) { + MSChannelFloat c = (MSChannelFloat) channels.get(channel); + if (c == null) { + return -1; + } + return c.get(time, times, values); + } + + public static void sinWaveGen(String channel) { + System.out.println("================ sinWaveGen ================="); + Thread t = new Thread() { + @Override + public void run() { + + long[] time = new long[100]; + float[] value = new float[100]; + for (int i = 0; i < 100000000; i++) { + long nt = System.nanoTime(); + float v = (float) (Math.sin(nt * 1E-9 * Math.PI) * Math.sin(nt * 1E-9 * Math.PI / 40)); + Vp.send(channel, nt, v); + + try { + Thread.sleep(10); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + }; + t.setDaemon(false); + t.start(); + } + + private static final HashMap last_fps_time = new HashMap<>(); + + public static void fps(String channel) { + long[] ans = last_fps_time.get(channel); + if (ans == null) { + ans = new long[1]; + last_fps_time.put(channel, ans); + ans[0] = System.nanoTime(); + } + long now = System.nanoTime(); + float duration = (now - ans[0]) * 1E-9f; + + if (duration < 1 / 500f) { + send(channel, now, 0); + } else { + send(channel, now, 1 / duration); + } + ans[0] = now; + + } + + // ======================= END Support for Floats================================== + + public static void main(String[] args) throws InterruptedException { + long last = System.nanoTime(); + Thread t = new Thread() { + @Override + public void run() { + + long[] time = new long[100]; + float[] value = new float[100]; + for (int i = 0; i < 100000000; i++) { + int len = getLatest("bob", time, value); + if (len == -1) { + System.out.println("skip"); + try { + Thread.sleep(0, 100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + continue; + } + long current = System.nanoTime(); + System.out.println(" " + len + " " + (current - time[len - 1]) * 1E-6f + "ms " + value[len - 1]); + + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + }; + t.setDaemon(false); + t.start(); + int sample = 10000000; + for (long i = 0; i < 100000000000L; i++) { + send("bob", i); + // Thread.sleep(0,100); + if (i % sample == 0) { + long now = System.nanoTime(); + + System.out.println(i + " per sec =" + sample / ((now - last) * 1E-9)); + last = now; + } + + } + + } +} diff --git a/projects/ComposeConstraintLayout/app/src/main/java/androidx/constraintlayout/extension/util/VpGraphCompose.kt b/projects/ComposeConstraintLayout/app/src/main/java/androidx/constraintlayout/extension/util/VpGraphCompose.kt new file mode 100644 index 000000000..57d698a10 --- /dev/null +++ b/projects/ComposeConstraintLayout/app/src/main/java/androidx/constraintlayout/extension/util/VpGraphCompose.kt @@ -0,0 +1,108 @@ +package androidx.constraintlayout.extension.util + +import androidx.compose.foundation.Canvas +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.neverEqualPolicy +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clipToBounds +import androidx.compose.ui.graphics.nativeCanvas +import androidx.compose.ui.input.pointer.motionEventSpy +import androidx.compose.ui.layout.onPlaced +import androidx.compose.ui.node.Ref +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch + + + +fun vpSend(channel:String, value:Float) { + Vp.send(channel,value) +} + +fun vpSend(channel:String,time:Long, value:Float) { + Vp.send(channel,time,value) +} + +fun vpFps(channel: String){ + Vp.fps(channel); +} + +@OptIn(ExperimentalComposeUiApi::class) +@Composable +fun VpGraph( + modifier: Modifier = Modifier, + vararg channels: String, +) { + val width = remember { + Ref().apply { 0 } + } + val height = remember { + Ref().apply { 0 } + } + + val invalidator = remember { mutableStateOf(Unit, neverEqualPolicy()) } + + val scope = rememberCoroutineScope() + + val uiDelegate = remember { + object : UiDelegate{ + override fun post(runnable: Runnable?): Boolean { + scope.launch { + runnable?.run() + } + return true + } + + override fun postDelayed(runnable: Runnable?, delayMillis: Long): Boolean { + scope.launch { + delay(delayMillis) + runnable?.run() + } + return true + } + + override fun invalidate() { + invalidator.value = Unit + } + + override fun getWidth(): Int { + return width.value ?: 0 + } + + override fun getHeight(): Int { + return height.value ?: 0 + } + } + } + + val graphCore = remember { + VpGraphCore(uiDelegate) + } + + LaunchedEffect(channels) { + channels.forEach { + graphCore.addChannel(it) + } + } + + Canvas( + modifier = modifier + .clipToBounds() + .onPlaced { + width.value = it.size.width + height.value = it.size.height + } + .motionEventSpy { + graphCore.onTouchEvent(it) + } + , + onDraw = { + invalidator.value + graphCore.onDraw(this.drawContext.canvas.nativeCanvas) + } + ) +} diff --git a/projects/ComposeConstraintLayout/app/src/main/java/androidx/constraintlayout/extension/util/VpGraphCore.kt b/projects/ComposeConstraintLayout/app/src/main/java/androidx/constraintlayout/extension/util/VpGraphCore.kt new file mode 100644 index 000000000..5dd8c1555 --- /dev/null +++ b/projects/ComposeConstraintLayout/app/src/main/java/androidx/constraintlayout/extension/util/VpGraphCore.kt @@ -0,0 +1,451 @@ +package androidx.constraintlayout.extension.util + +import android.graphics.* +import android.util.Log +import android.view.MotionEvent +import java.text.DecimalFormat +import kotlin.math.floor + +class VpGraphCore(private val mUiDelegate: UiDelegate) { + private var mPlots = ArrayList() + private val mTime = LongArray(MAX_BUFF) + private val mValue = FloatArray(MAX_BUFF) + var duration = 10f // seconds + var mMinY = 0f + var mMaxY = 1f + var mMinX = 0f + var mMaxX = duration + mMinX + var axisLeft = 100f + var axisTop = 100f + var axisRight = 100f + var axisBottom = 100f + var mAxisPaint = Paint() + var mLinePaint = Paint() + var mGridPaint = Paint() + var mBounds = Rect() + var start: Long = -1 // show the latest; + var mGraphSelfFps = false + var sampleDelay = 15 // ms between samples. + private var mLiveSample = true + private var mStartTime: Long = 0 + private var mLineX = Float.NaN + var debug = false + + internal class Data(var mTitle: String) { + var mX = FloatArray(MAX_BUFF) + var mY = FloatArray(MAX_BUFF) + var paint = Paint() + var path = Path() + var mLength: Int + var lastLabelYPos = Float.NaN + + init { + mLength = -1 + paint.style = Paint.Style.STROKE + } + + fun plot(canvas: Canvas, graph: VpGraphCore, w: Int, h: Int) { + path.reset() + val scaleX = graph.getScaleX(w, h) + val scaleY = graph.getScaleY(w, h) + val offX = graph.getOffsetX(w, h) + val offY = graph.getOffsetY(w, h) + var first = true + for (i in 0 until mLength) { + if ((i == mLength - 1 || mX[i + 1] >= graph.mMinX) && mX[i] <= graph.mMaxX) { + val x = mX[i] * scaleX + offX + val y = mY[i] * scaleY + offY + if (first) { + path.moveTo(x, y) + first = false + } else { + path.lineTo(x, y) + } + } + } + canvas.drawPath(path, paint) + } + + fun findClosestX(x: Float): Int { + var low = 0 + var high = mLength - 1 + var pos = -1 + while (low <= high) { + pos = low + (high - low) / 2 + if (mX[pos] == x) return pos + if (mX[pos] < x) low = pos + 1 else high = pos - 1 + } + return pos + } + } + + fun init() { + mUiDelegate.post { listenToChannels() } + mAxisPaint.textSize = 32f + mAxisPaint.strokeWidth = 3f + mAxisPaint.color = Color.BLUE + mGridPaint.textSize = 32f + mGridPaint.strokeWidth = 1f + mLinePaint.color = Color.RED + mLinePaint.strokeWidth = 3f + mLinePaint.textSize = 64f + } + + var mDownX = 0f + fun onTouchEvent(event: MotionEvent): Boolean { + val action = event.action and MotionEvent.ACTION_MASK + val count = event.pointerCount + when (action) { + MotionEvent.ACTION_MOVE -> if (count == 2) { + var drag = event.getX(0) + event.getX(1) + drag = (drag + mDownX) / 2 + mStartTime += (drag / getScaleX(mUiDelegate.width, mUiDelegate.height)).toLong() + listenToChannels() + Log.v("Main", ">>>> drag $drag") + mLineX = Float.NaN + } else { + mLineX = event.x + Log.v("Main", ">>>> ACTION_MOVE " + event.x + " ") + mUiDelegate.invalidate() + } + MotionEvent.ACTION_DOWN -> if (count == 2) { + mLiveSample = false + mDownX = event.getX(0) + event.getX(1) + Log.v("Main", "$count>>>> drag on$mDownX ") + mLineX = Float.NaN + } else { + mLineX = event.x + Log.v("Main", count.toString() + ">>>> ACTION_DOWN " + event.x) + mUiDelegate.invalidate() + } + MotionEvent.ACTION_UP -> { + Log.v("Main", ">>>> ACTION_UP " + event.x) + mLineX = Float.NaN + for (mPlot in mPlots) { + mPlot.lastLabelYPos = Float.NaN + } + mUiDelegate.invalidate() + } + MotionEvent.ACTION_POINTER_DOWN -> if (count == 2) { + mLiveSample = false + mDownX = event.getX(0) + event.getX(1) + Log.v("Main", "$count>>>> ACTION_POINTER_DOWN$mDownX $mLiveSample") + mLineX = Float.NaN + } + MotionEvent.ACTION_POINTER_UP -> { + Log.v("Main", ">>>> ACTION_POINTER_UP" + event.x) + if (event.eventTime - event.downTime < 400) { + Log.v("Main", ">>>> false") + mLiveSample = true + listenToChannels() + } + Log.v("Main", ">>>> def " + event.eventTime) + } + else -> Log.v("Main", ">>>> def " + event.eventTime) + } + return true + //return super.onTouchEvent(event); + } + + fun addChannel(str: String) { + for (plot in mPlots) { + if (plot.mTitle == str) { + return + } + } + mPlots.add(Data(str)) + } + + fun setGraphFPS(on: Boolean) { + mGraphSelfFps = on + if (on) { + mPlots.add(Data(FPS_STRING)) + // mPlots.add(new Data("read")); + } else { + var remove: Data? = null + for (i in mPlots.indices) { + if (mPlots[i].mTitle === FPS_STRING) { + remove = mPlots[i] + break + } + } + if (remove != null) { + mPlots.remove(remove) + } + } + } + + private fun listenToChannels() { + // Vp.fps("read"); + if (mLiveSample) { + listenLive() + return + } + val count = mPlots.size + var minX = Float.MAX_VALUE + var minY = Float.MAX_VALUE + var maxX = -Float.MAX_VALUE + var maxY = -Float.MAX_VALUE + for (i in 0 until count) { + val p = mPlots[i] + val channel = p.mTitle + p.mLength = Vp.getAfter(channel, mStartTime, mTime, mValue) + if (p.mLength == -1) { + continue + } + for (j in 0 until p.mLength) { + val x = (mTime[j] - mStartTime) * 1E-9f + p.mX[j] = x + minX = Math.min(x, minX) + maxX = Math.max(x, maxX) + val y = mValue[j] + minY = Math.min(y, minY) + maxY = Math.max(y, maxY) + p.mY[j] = y + } + Log.v("main", p.mTitle + " " + minX + " -> " + maxX) + } + minX = 0f + maxX = minX + duration + Log.v("main", "Total $minX -> $maxX") + updateDataRange(minX, maxX, minY, maxY) + mUiDelegate.invalidate() + } + + private fun listenLive() { + val count = mPlots.size + mStartTime = System.nanoTime() - (duration.toDouble() * 1000000000L).toLong() + var minX = Float.MAX_VALUE + var minY = Float.MAX_VALUE + var maxX = -Float.MAX_VALUE + var maxY = -Float.MAX_VALUE + for (i in 0 until count) { + val p = mPlots[i] + val channel = p.mTitle + p.mLength = Vp.getLatest(channel, mTime, mValue) + if (p.mLength == -1) { + continue + } + for (j in 0 until p.mLength) { + val x = (mTime[j] - mStartTime) * 1E-9f + p.mX[j] = x + minX = Math.min(x, minX) + maxX = Math.max(x, maxX) + val y = mValue[j] + minY = Math.min(y, minY) + maxY = Math.max(y, maxY) + p.mY[j] = y + } + } + if (minX == Float.MAX_VALUE || java.lang.Float.isNaN(minX)) { + updateDataRange(0f, 10f, -1f, 1f) + } else { + updateDataRange(minX, maxX, minY, maxY) + } + mUiDelegate.invalidate() + mUiDelegate.postDelayed({ listenToChannels() }, sampleDelay.toLong()) + } + + private fun updateDataRange(minX: Float, maxX: Float, minY: Float, maxY: Float) { + var minX = minX + minX = maxX - duration + // fast to expand slow to contract + val factor = 10f + mMaxY = if (mMaxY > maxY) (mMaxY + maxY) / 2 else (mMaxY * factor + maxY) / (factor + 1) + mMinY = if (mMinY < minY) (mMinY + minY) / 2 else (mMinY * factor + minY) / (factor + 1) + mMinX = (mMinX + minX) / 2 + mMaxX = duration + mMinX + } + + fun onDraw(canvas: Canvas) { + val w = mUiDelegate.width + val h = mUiDelegate.height + drawAxis(canvas, w, h) + drawGrid(canvas, w, h) + for (p in mPlots) { + p.plot(canvas, this, w, h) + } + if (mGraphSelfFps || debug) { + Vp.fps(FPS_STRING) + } + drawTouchLine(canvas, w, h) + } + + private fun drawGrid(canvas: Canvas, w: Int, h: Int) { + val ticksX = calcTick(w, (mMaxX - mMinX).toDouble()) + val ticksY = calcTick(h, (mMaxY - mMinY).toDouble()) + val minX = (ticksX * Math.ceil((mMinX + ticksX / 100) / ticksX)).toFloat() + val maxX = (ticksX * Math.floor(mMaxX / ticksX)).toFloat() + val scaleX = getScaleX(w, h) + val offX = getOffsetX(w, h) + var x = minX + var count = 0 + val txtPad = 4 + + while (x <= maxX) { + val xp = scaleX * x + offX + canvas.drawLine(xp, axisTop, xp, h - axisBottom, mGridPaint) + x += ticksX.toFloat() + if (floor(x) == x) { + val str = df.format(x) + mAxisPaint.getTextBounds(str, 0, str.length, mBounds) + canvas.drawText( + str, + xp - mBounds.width() / 2, + h - axisBottom + txtPad + mBounds.height(), + mGridPaint + ) + } + count++ + } + val minY = (ticksY * Math.ceil((mMinY + ticksY / 100) / ticksY)).toFloat() + val maxY = (ticksY * Math.floor(mMaxY / ticksY)).toFloat() + val offY = getOffsetY(w, h) + val scaleY = getScaleY(w, h) + var y = minY + while (y <= maxY) { + val yp = scaleY * y + offY + canvas.drawLine(axisLeft, yp, w - axisRight, yp, mGridPaint) + if (count and 1 == 1 && y + ticksY < maxY) { + val str = df.format(y) + mAxisPaint.getTextBounds(str, 0, str.length, mBounds) + canvas.drawText(str, axisLeft - mBounds.width() - txtPad * 2, yp, mGridPaint) + } + count++ + y += ticksY.toFloat() + } + } + + var df = DecimalFormat("0.0") + + init { + init() + mAxisPaint.color = Color.BLACK + } + + fun drawTouchLine(canvas: Canvas, w: Int, h: Int) { + if (java.lang.Float.isNaN(mLineX)) { + return + } + if (mLineX < axisLeft) { + mLineX = axisLeft + } else if (mLineX > w - axisRight) { + mLineX = w - axisRight + } + val dataPos = (mLineX - getOffsetX(w, h)) / getScaleX(w, h) + val yOffset = getOffsetY(w, h) + val yScale = getScaleY(w, h) + val rad = 10f + canvas.drawLine(mLineX, axisTop, mLineX, h - axisBottom, mLinePaint) + var bottom_count = 0 + var top_count = 0 + val pad = 5 + val right = mLineX < w / 2 + val no_of_plots = mPlots.size + for (i in 0 until no_of_plots) { + val plot = mPlots[i] + val index = plot.findClosestX(dataPos) + if (index == -1) continue + val value = plot.mY[index] + val y = yScale * value + yOffset + canvas.drawRoundRect(mLineX - rad, y - rad, mLineX + rad, y + rad, rad, rad, mLinePaint) + val vString = plot.mTitle + ":" + df.format(value.toDouble()) + mLinePaint.getTextBounds(vString, 0, vString.length, mBounds) + var yPos = (w / 2).toFloat() + val gap = 60 + if (y > h / 2) { + yPos = y - gap + bottom_count++ + } else { + top_count++ + yPos = y + gap + } + val xPos = if (right) mLineX + gap else mLineX - gap + val minVGap = mBounds.height().toFloat() + var force = 0f + for (j in 0 until no_of_plots) { + if (i == j) { + continue + } + val yOther = mPlots[j].lastLabelYPos + val dist = Math.abs(plot.lastLabelYPos - yOther) + if (dist < minVGap * 2) { + val dir = Math.signum(yPos - yOther) + force = (if (dir > 0) minVGap else -minVGap) + minVGap / (0.1f + dist) + } + } + if (java.lang.Float.isNaN(plot.lastLabelYPos)) { + plot.lastLabelYPos = yPos + } else { + plot.lastLabelYPos = (plot.lastLabelYPos * 49 + yPos + force) / 50 + } + canvas.drawLine(mLineX, y, xPos, plot.lastLabelYPos, mLinePaint) + canvas.drawText( + vString, + if (right) xPos else xPos - mBounds.width() - pad, + plot.lastLabelYPos, + mLinePaint + ) + } + } + + fun drawAxis(canvas: Canvas, w: Int, h: Int) { + val txtPad = 4 + canvas.drawRGB(200, 230, 255) + canvas.drawLine(axisLeft, axisTop, axisLeft, h - axisBottom, mAxisPaint) + canvas.drawLine(axisLeft, h - axisBottom, w - axisRight, h - axisBottom, mAxisPaint) + val y0 = getOffsetY(w, h) + canvas.drawLine(axisLeft, y0, w - axisRight, y0, mAxisPaint) + var str = df.format(mMaxY.toDouble()) + mAxisPaint.getTextBounds(str, 0, str.length, mBounds) + canvas.drawText(str, axisLeft - mBounds.width() - txtPad, axisTop, mAxisPaint) + str = df.format(mMinY.toDouble()) + mAxisPaint.getTextBounds(str, 0, str.length, mBounds) + canvas.drawText(str, axisLeft - mBounds.width() - txtPad, h - axisBottom, mAxisPaint) + } + + fun getScaleX(w: Int, h: Int): Float { + val rangeX = mMaxX - mMinX + val graphSpanX = w - axisLeft - axisRight + return graphSpanX / rangeX + } + + fun getScaleY(w: Int, h: Int): Float { + val rangeY = mMaxY - mMinY + val graphSpanY = h - axisTop - axisBottom + return -graphSpanY / rangeY + } + + fun getOffsetX(w: Int, h: Int): Float { + return axisLeft - mMinX * getScaleX(w, h) + } + + fun getOffsetY(w: Int, h: Int): Float { + return h - axisBottom - mMinY * getScaleY(w, h) + } + + companion object { + private const val FPS_STRING = "onDraw" + const val MAX_BUFF = 2000 + fun calcTick(scr: Int, range: Double): Double { + val aprox_x_ticks = scr / 100 + var type = 1 + var best = Math.log10(range / aprox_x_ticks) + var n = Math.log10(range / (aprox_x_ticks * 2)) + if (fraction(n) < fraction(best)) { + best = n + type = 2 + } + n = Math.log10(range / (aprox_x_ticks * 5)) + if (fraction(n) < fraction(best)) { + best = n + type = 5 + } + return type * Math.pow(10.0, Math.floor(best)) + } + + fun fraction(x: Double): Double { + return x - Math.floor(x) + } + } +} \ No newline at end of file diff --git a/projects/ComposeConstraintLayout/app/src/main/java/androidx/constraintlayout/extension/util/VpGraphCoreOrig.java b/projects/ComposeConstraintLayout/app/src/main/java/androidx/constraintlayout/extension/util/VpGraphCoreOrig.java new file mode 100644 index 000000000..726c62b20 --- /dev/null +++ b/projects/ComposeConstraintLayout/app/src/main/java/androidx/constraintlayout/extension/util/VpGraphCoreOrig.java @@ -0,0 +1,483 @@ +package androidx.constraintlayout.extension.util; + +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.Rect; +import android.util.Log; +import android.view.MotionEvent; + +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.List; + +public class VpGraphCoreOrig { + private static final String FPS_STRING = "onDraw"; + + List mPlots = new ArrayList<>(); + final static int MAX_BUFF = 2000; + private long[] mTime = new long[MAX_BUFF]; + private float[] mValue = new float[MAX_BUFF]; + float duration = 10; // seconds + float mMinY = 0; + float mMaxY = 1; + float mMinX = 0; + float mMaxX = duration + mMinX; + float axisLeft = 100; + float axisTop = 100; + float axisRight = 100; + float axisBottom = 100; + Paint mAxisPaint = new Paint(); + Paint mLinePaint = new Paint(); + Paint mGridPaint = new Paint(); + Rect mBounds = new Rect(); + long start = -1; // show the latest; + boolean mGraphSelfFps = false; + int sampleDelay = 15; // ms between samples. + private boolean mLiveSample = true; + private long mStartTime; + private float mLineX = Float.NaN; + public boolean debug = false; + + static class Data { + float[] mX = new float[MAX_BUFF]; + float[] mY = new float[MAX_BUFF]; + Paint paint = new Paint(); + Path path = new Path(); + int mLength; + String mTitle; + float lastLabelYPos = Float.NaN; + + Data(String title) { + mTitle = title; + mLength = -1; + paint.setStyle(Paint.Style.STROKE); + } + + void plot(Canvas canvas, VpGraphCoreOrig graph, int w, int h) { + path.reset(); + float scaleX = graph.getScaleX(w, h); + float scaleY = graph.getScaleY(w, h); + float offX = graph.getOffsetX(w, h); + float offY = graph.getOffsetY(w, h); + boolean first = true; + for (int i = 0; i < mLength; i++) { + if ((i == mLength - 1 || mX[i + 1] >= graph.mMinX) && mX[i] <= graph.mMaxX) { + + float x = mX[i] * scaleX + offX; + float y = mY[i] * scaleY + offY; + if (first) { + path.moveTo(x, y); + first = false; + } else { + path.lineTo(x, y); + } + } + } + canvas.drawPath(path, paint); + } + + public int findClosestX(float x) { + int low = 0; + int high = mLength - 1; + int pos = -1; + while (low <= high) { + pos = low + (high - low) / 2; + if (mX[pos] == x) + return pos; + + if (mX[pos] < x) + low = pos + 1; + else + high = pos - 1; + } + return pos; + } + + + } + + private final UiDelegate mUiDelegate; + + public VpGraphCoreOrig(UiDelegate uiDelegate) { + mUiDelegate = uiDelegate; + init(); + mAxisPaint.setColor(Color.BLACK); + } + + public void init() { + mUiDelegate.post(this::listenToChannels); + mAxisPaint.setTextSize(32); + mAxisPaint.setStrokeWidth(3); + mAxisPaint.setColor(Color.BLUE); + mGridPaint.setTextSize(32); + mGridPaint.setStrokeWidth(1); + mLinePaint.setColor(Color.RED); + mLinePaint.setStrokeWidth(3); + mLinePaint.setTextSize(64); + + } + + float mDownX; + + public boolean onTouchEvent(MotionEvent event) { + int action = event.getAction() & MotionEvent.ACTION_MASK; + int count = event.getPointerCount(); + // Log.v("Main", ">>>> count " + count + " " +event); + switch (action) { + case MotionEvent.ACTION_MOVE: + if (count == 2) { + float drag = event.getX(0) + event.getX(1); + drag = (drag + mDownX) / 2; + mStartTime += drag / getScaleX(mUiDelegate.getWidth(), mUiDelegate.getHeight()); + listenToChannels(); + Log.v("Main", ">>>> drag " + drag); + mLineX = Float.NaN; + } else { + mLineX = event.getX(); + Log.v("Main", ">>>> ACTION_MOVE " + event.getX() + " "); + mUiDelegate.invalidate(); + } + break; + case MotionEvent.ACTION_DOWN: + if (count == 2) { + mLiveSample = false; + mDownX = (event.getX(0) + event.getX(1)); + Log.v("Main", count + ">>>> drag on" + mDownX + " "); + + mLineX = Float.NaN; + } else { + mLineX = event.getX(); + Log.v("Main", count + ">>>> ACTION_DOWN " + event.getX()); + mUiDelegate.invalidate(); + } + break; + case MotionEvent.ACTION_UP: + Log.v("Main", ">>>> ACTION_UP " + event.getX()); + + mLineX = Float.NaN; + for (Data mPlot : mPlots) { + mPlot.lastLabelYPos = Float.NaN; + } + mUiDelegate.invalidate(); + break; + case MotionEvent.ACTION_POINTER_DOWN: + if (count == 2) { + + mLiveSample = false; + mDownX = (event.getX(0) + event.getX(1)); + Log.v("Main", count + ">>>> ACTION_POINTER_DOWN" + mDownX + " " + mLiveSample); + + mLineX = Float.NaN; + } + break; + case MotionEvent.ACTION_POINTER_UP: + Log.v("Main", ">>>> ACTION_POINTER_UP" + event.getX()); + if (event.getEventTime() - event.getDownTime() < 400) { + Log.v("Main", ">>>> false"); + mLiveSample = true; + listenToChannels(); + } + + default: + Log.v("Main", ">>>> def " + event.getEventTime()); + } + return true; + //return super.onTouchEvent(event); + } + + public void addChannel(String str) { + mPlots.add(new Data(str)); + } + + public void setGraphFPS(boolean on) { + mGraphSelfFps = on; + if (on) { + mPlots.add(new Data(FPS_STRING)); + // mPlots.add(new Data("read")); + } else { + Data remove = null; + for (int i = 0; i < mPlots.size(); i++) { + if (mPlots.get(i).mTitle == FPS_STRING) { + remove = mPlots.get(i); + break; + } + } + if (remove != null) { + mPlots.remove(remove); + } + } + } + + + private void listenToChannels() { + // Vp.fps("read"); + if (mLiveSample) { + listenLive(); + return; + } + int count = mPlots.size(); + + float minX = Float.MAX_VALUE; + float minY = Float.MAX_VALUE; + float maxX = -Float.MAX_VALUE; + float maxY = -Float.MAX_VALUE; + for (int i = 0; i < count; i++) { + Data p = mPlots.get(i); + String channel = p.mTitle; + + + p.mLength = Vp.getAfter(channel, mStartTime, mTime, mValue); + + if (p.mLength == -1) { + continue; + } + + for (int j = 0; j < p.mLength; j++) { + float x = (mTime[j] - mStartTime) * 1E-9f; + p.mX[j] = x; + minX = Math.min(x, minX); + maxX = Math.max(x, maxX); + + float y = mValue[j]; + minY = Math.min(y, minY); + maxY = Math.max(y, maxY); + p.mY[j] = y; + } + Log.v("main", p.mTitle + " " + minX + " -> " + maxX); + } + + minX = 0; + + maxX = minX + duration; + Log.v("main", "Total " + minX + " -> " + maxX); + + updateDataRange(minX, maxX, minY, maxY); + + mUiDelegate.invalidate(); + } + + private void listenLive() { + + int count = mPlots.size(); + + mStartTime = System.nanoTime() - (long) (((double) duration) * 1000000000L); + + float minX = Float.MAX_VALUE; + float minY = Float.MAX_VALUE; + float maxX = -Float.MAX_VALUE; + float maxY = -Float.MAX_VALUE; + for (int i = 0; i < count; i++) { + Data p = mPlots.get(i); + String channel = p.mTitle; + + p.mLength = Vp.getLatest(channel, mTime, mValue); + + if (p.mLength == -1) { + continue; + } + + for (int j = 0; j < p.mLength; j++) { + float x = (mTime[j] - mStartTime) * 1E-9f; + p.mX[j] = x; + minX = Math.min(x, minX); + maxX = Math.max(x, maxX); + + float y = mValue[j]; + minY = Math.min(y, minY); + maxY = Math.max(y, maxY); + p.mY[j] = y; + } + } + if (minX == Float.MAX_VALUE || Float.isNaN(minX)) { + updateDataRange(0, 10, -1, 1); + } else { + updateDataRange(minX, maxX, minY, maxY); + } + + mUiDelegate.invalidate(); + mUiDelegate.postDelayed(this::listenToChannels, sampleDelay); + } + + private void updateDataRange(float minX, float maxX, float minY, float maxY) { + minX = maxX - duration; + // fast to expand slow to contract + float factor = 10; + mMaxY = (mMaxY > maxY) ? (mMaxY + maxY) / 2 : (mMaxY * factor + maxY) / (factor + 1); + mMinY = (mMinY < minY) ? (mMinY + minY) / 2 : (mMinY * factor + minY) / (factor + 1); + mMinX = (mMinX + minX) / 2; + mMaxX = duration + mMinX; + } + + public void onDraw(Canvas canvas) { + int w = mUiDelegate.getWidth(); + int h = mUiDelegate.getHeight(); + drawAxis(canvas, w, h); + drawGrid(canvas, w, h); + for (Data p : mPlots) { + p.plot(canvas, this, w, h); + } + if (mGraphSelfFps || debug) { + Vp.fps(FPS_STRING); + } + + drawTouchLine(canvas, w, h); + } + + private void drawGrid(Canvas canvas, int w, int h) { + double ticksX = calcTick(w, mMaxX - mMinX); + double ticksY = calcTick(h, mMaxY - mMinY); + float minX = (float) (ticksX * Math.ceil((mMinX + ticksX / 100) / ticksX)); + float maxX = (float) (ticksX * Math.floor(mMaxX / ticksX)); + + float scaleX = getScaleX(w, h); + float offX = getOffsetX(w, h); + for (float x = minX; x <= maxX; x += ticksX) { + float xp = scaleX * x + offX; + canvas.drawLine(xp, axisTop, xp, h - axisBottom, mGridPaint); + } + + float minY = (float) (ticksY * Math.ceil((mMinY + ticksY / 100) / ticksY)); + float maxY = (float) (ticksY * Math.floor(mMaxY / ticksY)); + + float offY = getOffsetY(w, h); + float scaleY = getScaleY(w, h); + int count = 0; + int txtPad = 4; + for (float y = minY; y <= maxY; y += ticksY) { + float yp = scaleY * y + offY; + canvas.drawLine(axisLeft, yp, w - axisRight, yp, mGridPaint); + + if ((count & 1) == 1 && (y + ticksY) < maxY) { + String str = df.format(y); + mAxisPaint.getTextBounds(str, 0, str.length(), mBounds); + canvas.drawText(str, axisLeft - mBounds.width() - txtPad, yp, mGridPaint); + } + count++; + } + } + + DecimalFormat df = new DecimalFormat("0.0"); + + void drawTouchLine(Canvas canvas, int w, int h) { + if (Float.isNaN(mLineX)) { + return; + } + if (mLineX < axisLeft) { + mLineX = axisLeft; + } else if (mLineX > (w - axisRight)) { + mLineX = w - axisRight; + } + float dataPos = (mLineX - getOffsetX(w, h)) / getScaleX(w, h); + float yOffset = getOffsetY(w, h); + float yScale = getScaleY(w, h); + float rad = 10; + canvas.drawLine(mLineX, axisTop, mLineX, h - axisBottom, mLinePaint); + int bottom_count = 0; + int top_count = 0; + int pad = 5; + + boolean right = (mLineX < w / 2); + int no_of_plots = mPlots.size(); + for (int i = 0; i < no_of_plots; i++) { + Data plot = mPlots.get(i); + + + int index = plot.findClosestX(dataPos); + if (index == -1) continue; + float value = plot.mY[index]; + float y = yScale * value + yOffset; + canvas.drawRoundRect(mLineX - rad, y - rad, mLineX + rad, y + rad, rad, rad, mLinePaint); + String vString = plot.mTitle + ":" + df.format(value); + mLinePaint.getTextBounds(vString, 0, vString.length(), mBounds); + float yPos = w / 2; + int gap = 60; + if (y > h / 2) { + yPos = y - gap; + bottom_count++; + } else { + top_count++; + yPos = y + gap; + } + float xPos = (right) ? mLineX + gap : mLineX - gap; + float minVGap = mBounds.height(); + float force = 0; + for (int j = 0; j < no_of_plots; j++) { + if (i == j) { + continue; + } + float yOther = mPlots.get(j).lastLabelYPos; + float dist = Math.abs(plot.lastLabelYPos - yOther); + if (dist < minVGap * 2) { + float dir = Math.signum(yPos - yOther); + force = ((dir > 0) ? minVGap : -minVGap) + minVGap / (0.1f + dist); + } + } + if (Float.isNaN(plot.lastLabelYPos)) { + plot.lastLabelYPos = yPos; + } else { + plot.lastLabelYPos = (plot.lastLabelYPos * 49 + yPos+force) / 50; + } + + canvas.drawLine(mLineX, y, xPos, plot.lastLabelYPos, mLinePaint); + canvas.drawText(vString, right ? xPos : xPos - mBounds.width() - pad, plot.lastLabelYPos, mLinePaint); + } + } + + void drawAxis(Canvas canvas, int w, int h) { + int txtPad = 4; + canvas.drawRGB(200, 230, 255); + canvas.drawLine(axisLeft, axisTop, axisLeft, h - axisBottom, mAxisPaint); + canvas.drawLine(axisLeft, h - axisBottom, w - axisRight, h - axisBottom, mAxisPaint); + float y0 = getOffsetY(w, h); + canvas.drawLine(axisLeft, y0, w - axisRight, y0, mAxisPaint); + String str = df.format(mMaxY); + mAxisPaint.getTextBounds(str, 0, str.length(), mBounds); + canvas.drawText(str, axisLeft - mBounds.width() - txtPad, axisTop, mAxisPaint); + str = df.format(mMinY); + mAxisPaint.getTextBounds(str, 0, str.length(), mBounds); + canvas.drawText(str, axisLeft - mBounds.width() - txtPad, h - axisBottom, mAxisPaint); + } + + public float getScaleX(int w, int h) { + float rangeX = mMaxX - mMinX; + float graphSpanX = w - axisLeft - axisRight; + return graphSpanX / rangeX; + } + + public float getScaleY(int w, int h) { + float rangeY = mMaxY - mMinY; + float graphSpanY = h - axisTop - axisBottom; + return -graphSpanY / rangeY; + } + + public float getOffsetX(int w, int h) { + return axisLeft - mMinX * getScaleX(w, h); + } + + public float getOffsetY(int w, int h) { + return h - axisBottom - mMinY * getScaleY(w, h); + } + + static public double calcTick(int scr, double range) { + int aprox_x_ticks = scr / 100; + int type = 1; + double best = Math.log10(range / (aprox_x_ticks)); + double n = Math.log10(range / (aprox_x_ticks * 2)); + if (fraction(n) < fraction(best)) { + best = n; + type = 2; + } + n = Math.log10(range / (aprox_x_ticks * 5)); + if (fraction(n) < fraction(best)) { + best = n; + type = 5; + } + return type * Math.pow(10, Math.floor(best)); + } + + static double fraction(double x) { + return x - Math.floor(x); + } +} diff --git a/projects/ComposeConstraintLayout/app/src/main/java/com/example/constraintlayout/test.kt b/projects/ComposeConstraintLayout/app/src/main/java/com/example/constraintlayout/test.kt index a8bf9f3f6..37b756be8 100644 --- a/projects/ComposeConstraintLayout/app/src/main/java/com/example/constraintlayout/test.kt +++ b/projects/ComposeConstraintLayout/app/src/main/java/com/example/constraintlayout/test.kt @@ -40,13 +40,20 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Matrix import androidx.compose.ui.layout.layoutId +import androidx.compose.ui.layout.onPlaced +import androidx.compose.ui.layout.positionInParent import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.constraintlayout.compose.* +import androidx.constraintlayout.extension.util.VpGraph +import androidx.constraintlayout.extension.util.vpSend import java.util.EnumSet +import kotlin.math.PI +import kotlin.math.atan2 @Preview @Composable @@ -267,7 +274,8 @@ public fun Screen4() { painter = painterResource(id = R.drawable.intercom_snooze), contentDescription = null ) - Text(modifier = Modifier.layoutId("header"), + Text( + modifier = Modifier.layoutId("header"), text = stringResource(id = R.string.welcome_header), style = MaterialTheme.typography.h5, ) @@ -285,12 +293,14 @@ public fun Screen4() { ) { Text(text = stringResource(id = R.string.sign_up)) } - Button(modifier = Modifier.layoutId("bLogin"), + Button( + modifier = Modifier.layoutId("bLogin"), onClick = {}, ) { Text(text = stringResource(id = R.string.log_in)) } - Text(modifier = Modifier.layoutId("disclaimer"), + Text( + modifier = Modifier.layoutId("disclaimer"), text = stringResource(id = R.string.trial_disclaimer), style = MaterialTheme.typography.caption, ) @@ -355,7 +365,8 @@ public fun Screen5() { painter = painterResource(id = R.drawable.intercom_snooze), contentDescription = null ) - Text(modifier = Modifier.layoutId("header"), + Text( + modifier = Modifier.layoutId("header"), text = stringResource(id = R.string.welcome_header), style = MaterialTheme.typography.h5, ) @@ -373,12 +384,14 @@ public fun Screen5() { ) { Text(text = stringResource(id = R.string.sign_up)) } - Button(modifier = Modifier.layoutId("bLogin"), + Button( + modifier = Modifier.layoutId("bLogin"), onClick = {}, ) { Text(text = stringResource(id = R.string.log_in)) } - Text(modifier = Modifier.layoutId("disclaimer"), + Text( + modifier = Modifier.layoutId("disclaimer"), text = stringResource(id = R.string.trial_disclaimer), style = MaterialTheme.typography.caption, ) @@ -404,10 +417,11 @@ public fun ScreenExample() { ) { Text(text = stringResource(id = R.string.log_in)) } - Text(modifier = Modifier.constrainAs(title) { - centerVerticallyTo(parent) - start.linkTo(g1) - }, + Text( + modifier = Modifier.constrainAs(title) { + centerVerticallyTo(parent) + start.linkTo(g1) + }, text = stringResource(id = R.string.welcome_header), style = MaterialTheme.typography.h2, ) @@ -440,7 +454,8 @@ public fun ScreenExample2() { ) { Text(text = stringResource(id = R.string.log_in)) } - Text(modifier = Modifier.layoutId("title"), + Text( + modifier = Modifier.layoutId("title"), text = stringResource(id = R.string.welcome_header), style = MaterialTheme.typography.h2, ) @@ -473,7 +488,8 @@ public fun ScreenExample3() { ) { Text(text = stringResource(id = R.string.log_in)) } - Text(modifier = Modifier.layoutId("title"), + Text( + modifier = Modifier.layoutId("title"), text = stringResource(id = R.string.welcome_header), style = MaterialTheme.typography.h2, ) @@ -1263,6 +1279,17 @@ public fun ScreenExample15() { debug = EnumSet.of(MotionLayoutDebugFlags.SHOW_ALL), progress = progress) { Box(modifier = Modifier + .onPlaced { + vpSend("width", it.size.width.toFloat()) + vpSend("x", it.positionInParent().x) + + val matrix = Matrix() + + it.transformFrom(it.parentCoordinates!!, matrix); + val rot = 180.0f*calculateZRotation(matrix.values)/ PI.toFloat() + println(rot) + vpSend("rot", rot) + } .layoutId("a") .background(Color.Red)) } @@ -1270,9 +1297,20 @@ public fun ScreenExample15() { Button(onClick = { animateToEnd = !animateToEnd }) { Text(text = "Run ScreenExample15") } + vpSend("progress",progress*1000) + VpGraph(modifier = Modifier.fillMaxSize(),"rot","progress","x","width") } } - +fun calculateZRotation(matrix: FloatArray): Float { + // Extract the values from the matrix + val m00 = matrix[0] + val m01 = matrix[1] + val m10 = matrix[1*3+0] + val m11 = matrix[1*3+1] + + // Calculate the z-rotation angle + return -atan2(m01, m00) +} @Preview(group = "motion8") @Composable public fun ScreenExample16() { @@ -1928,7 +1966,8 @@ public fun ChainNew3() { """ ), modifier = Modifier - .fillMaxSize().background(Color.LightGray) + .fillMaxSize() + .background(Color.LightGray) ) { Text(text = "chain2.3!") for (k in 1..5) { @@ -2033,7 +2072,8 @@ public fun ChainNew4() { """ ), modifier = Modifier - .fillMaxSize().background(Color.LightGray) + .fillMaxSize() + .background(Color.LightGray) ) { Text(text = "chain2.3!") for (k in 1..5) { From 6786b7d086bcaf097409661be0da12fb46cb7a86 Mon Sep 17 00:00:00 2001 From: John Hoford Date: Fri, 26 May 2023 13:59:37 -0700 Subject: [PATCH 2/2] small cleanup --- .../constraintlayout/extension/util/Vp.java | 16 +- .../extension/util/VpGraphCompose.kt | 16 +- .../extension/util/VpGraphCore.kt | 6 +- .../extension/util/VpGraphCoreOrig.java | 483 ---------------- .../java/com/example/constraintlayout/test.kt | 514 +++++++++++------- 5 files changed, 338 insertions(+), 697 deletions(-) delete mode 100644 projects/ComposeConstraintLayout/app/src/main/java/androidx/constraintlayout/extension/util/VpGraphCoreOrig.java diff --git a/projects/ComposeConstraintLayout/app/src/main/java/androidx/constraintlayout/extension/util/Vp.java b/projects/ComposeConstraintLayout/app/src/main/java/androidx/constraintlayout/extension/util/Vp.java index 1022f40d7..9b8213f9b 100644 --- a/projects/ComposeConstraintLayout/app/src/main/java/androidx/constraintlayout/extension/util/Vp.java +++ b/projects/ComposeConstraintLayout/app/src/main/java/androidx/constraintlayout/extension/util/Vp.java @@ -68,7 +68,7 @@ static class MSChannelFloat extends MSChannel { } void add(long time, float value) { - int last = getLast(); + int last = getLast(); mTime[last] = time; mData[last] = value; increment(); @@ -111,13 +111,15 @@ static int search(long arr[], int low, int high, long key) { int pos = -1; while (low <= high) { pos = low + (high - low) / 2; - if (arr[pos] == key) + if (arr[pos] == key) { return pos; + } - if (arr[pos] < key) + if (arr[pos] < key) { low = pos + 1; - else + } else { high = pos - 1; + } } return pos; } @@ -237,7 +239,8 @@ public void run() { float[] value = new float[100]; for (int i = 0; i < 100000000; i++) { long nt = System.nanoTime(); - float v = (float) (Math.sin(nt * 1E-9 * Math.PI) * Math.sin(nt * 1E-9 * Math.PI / 40)); + float v = (float) (Math.sin(nt * 1E-9 * Math.PI) * Math.sin( + nt * 1E-9 * Math.PI / 40)); Vp.send(channel, nt, v); try { @@ -295,7 +298,8 @@ public void run() { continue; } long current = System.nanoTime(); - System.out.println(" " + len + " " + (current - time[len - 1]) * 1E-6f + "ms " + value[len - 1]); + System.out.println(" " + len + " " + + (current - time[len - 1]) * 1E-6f + "ms " + value[len - 1]); try { Thread.sleep(1000); diff --git a/projects/ComposeConstraintLayout/app/src/main/java/androidx/constraintlayout/extension/util/VpGraphCompose.kt b/projects/ComposeConstraintLayout/app/src/main/java/androidx/constraintlayout/extension/util/VpGraphCompose.kt index 57d698a10..054a17c2f 100644 --- a/projects/ComposeConstraintLayout/app/src/main/java/androidx/constraintlayout/extension/util/VpGraphCompose.kt +++ b/projects/ComposeConstraintLayout/app/src/main/java/androidx/constraintlayout/extension/util/VpGraphCompose.kt @@ -18,16 +18,15 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.launch - -fun vpSend(channel:String, value:Float) { - Vp.send(channel,value) +fun vpSend(channel: String, value: Float) { + Vp.send(channel, value) } -fun vpSend(channel:String,time:Long, value:Float) { - Vp.send(channel,time,value) +fun vpSend(channel: String, time: Long, value: Float) { + Vp.send(channel, time, value) } -fun vpFps(channel: String){ +fun vpFps(channel: String) { Vp.fps(channel); } @@ -49,7 +48,7 @@ fun VpGraph( val scope = rememberCoroutineScope() val uiDelegate = remember { - object : UiDelegate{ + object : UiDelegate { override fun post(runnable: Runnable?): Boolean { scope.launch { runnable?.run() @@ -98,8 +97,7 @@ fun VpGraph( } .motionEventSpy { graphCore.onTouchEvent(it) - } - , + }, onDraw = { invalidator.value graphCore.onDraw(this.drawContext.canvas.nativeCanvas) diff --git a/projects/ComposeConstraintLayout/app/src/main/java/androidx/constraintlayout/extension/util/VpGraphCore.kt b/projects/ComposeConstraintLayout/app/src/main/java/androidx/constraintlayout/extension/util/VpGraphCore.kt index 5dd8c1555..a9c556077 100644 --- a/projects/ComposeConstraintLayout/app/src/main/java/androidx/constraintlayout/extension/util/VpGraphCore.kt +++ b/projects/ComposeConstraintLayout/app/src/main/java/androidx/constraintlayout/extension/util/VpGraphCore.kt @@ -1,6 +1,10 @@ package androidx.constraintlayout.extension.util -import android.graphics.* +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.graphics.Path +import android.graphics.Rect import android.util.Log import android.view.MotionEvent import java.text.DecimalFormat diff --git a/projects/ComposeConstraintLayout/app/src/main/java/androidx/constraintlayout/extension/util/VpGraphCoreOrig.java b/projects/ComposeConstraintLayout/app/src/main/java/androidx/constraintlayout/extension/util/VpGraphCoreOrig.java deleted file mode 100644 index 726c62b20..000000000 --- a/projects/ComposeConstraintLayout/app/src/main/java/androidx/constraintlayout/extension/util/VpGraphCoreOrig.java +++ /dev/null @@ -1,483 +0,0 @@ -package androidx.constraintlayout.extension.util; - -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Path; -import android.graphics.Rect; -import android.util.Log; -import android.view.MotionEvent; - -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.List; - -public class VpGraphCoreOrig { - private static final String FPS_STRING = "onDraw"; - - List mPlots = new ArrayList<>(); - final static int MAX_BUFF = 2000; - private long[] mTime = new long[MAX_BUFF]; - private float[] mValue = new float[MAX_BUFF]; - float duration = 10; // seconds - float mMinY = 0; - float mMaxY = 1; - float mMinX = 0; - float mMaxX = duration + mMinX; - float axisLeft = 100; - float axisTop = 100; - float axisRight = 100; - float axisBottom = 100; - Paint mAxisPaint = new Paint(); - Paint mLinePaint = new Paint(); - Paint mGridPaint = new Paint(); - Rect mBounds = new Rect(); - long start = -1; // show the latest; - boolean mGraphSelfFps = false; - int sampleDelay = 15; // ms between samples. - private boolean mLiveSample = true; - private long mStartTime; - private float mLineX = Float.NaN; - public boolean debug = false; - - static class Data { - float[] mX = new float[MAX_BUFF]; - float[] mY = new float[MAX_BUFF]; - Paint paint = new Paint(); - Path path = new Path(); - int mLength; - String mTitle; - float lastLabelYPos = Float.NaN; - - Data(String title) { - mTitle = title; - mLength = -1; - paint.setStyle(Paint.Style.STROKE); - } - - void plot(Canvas canvas, VpGraphCoreOrig graph, int w, int h) { - path.reset(); - float scaleX = graph.getScaleX(w, h); - float scaleY = graph.getScaleY(w, h); - float offX = graph.getOffsetX(w, h); - float offY = graph.getOffsetY(w, h); - boolean first = true; - for (int i = 0; i < mLength; i++) { - if ((i == mLength - 1 || mX[i + 1] >= graph.mMinX) && mX[i] <= graph.mMaxX) { - - float x = mX[i] * scaleX + offX; - float y = mY[i] * scaleY + offY; - if (first) { - path.moveTo(x, y); - first = false; - } else { - path.lineTo(x, y); - } - } - } - canvas.drawPath(path, paint); - } - - public int findClosestX(float x) { - int low = 0; - int high = mLength - 1; - int pos = -1; - while (low <= high) { - pos = low + (high - low) / 2; - if (mX[pos] == x) - return pos; - - if (mX[pos] < x) - low = pos + 1; - else - high = pos - 1; - } - return pos; - } - - - } - - private final UiDelegate mUiDelegate; - - public VpGraphCoreOrig(UiDelegate uiDelegate) { - mUiDelegate = uiDelegate; - init(); - mAxisPaint.setColor(Color.BLACK); - } - - public void init() { - mUiDelegate.post(this::listenToChannels); - mAxisPaint.setTextSize(32); - mAxisPaint.setStrokeWidth(3); - mAxisPaint.setColor(Color.BLUE); - mGridPaint.setTextSize(32); - mGridPaint.setStrokeWidth(1); - mLinePaint.setColor(Color.RED); - mLinePaint.setStrokeWidth(3); - mLinePaint.setTextSize(64); - - } - - float mDownX; - - public boolean onTouchEvent(MotionEvent event) { - int action = event.getAction() & MotionEvent.ACTION_MASK; - int count = event.getPointerCount(); - // Log.v("Main", ">>>> count " + count + " " +event); - switch (action) { - case MotionEvent.ACTION_MOVE: - if (count == 2) { - float drag = event.getX(0) + event.getX(1); - drag = (drag + mDownX) / 2; - mStartTime += drag / getScaleX(mUiDelegate.getWidth(), mUiDelegate.getHeight()); - listenToChannels(); - Log.v("Main", ">>>> drag " + drag); - mLineX = Float.NaN; - } else { - mLineX = event.getX(); - Log.v("Main", ">>>> ACTION_MOVE " + event.getX() + " "); - mUiDelegate.invalidate(); - } - break; - case MotionEvent.ACTION_DOWN: - if (count == 2) { - mLiveSample = false; - mDownX = (event.getX(0) + event.getX(1)); - Log.v("Main", count + ">>>> drag on" + mDownX + " "); - - mLineX = Float.NaN; - } else { - mLineX = event.getX(); - Log.v("Main", count + ">>>> ACTION_DOWN " + event.getX()); - mUiDelegate.invalidate(); - } - break; - case MotionEvent.ACTION_UP: - Log.v("Main", ">>>> ACTION_UP " + event.getX()); - - mLineX = Float.NaN; - for (Data mPlot : mPlots) { - mPlot.lastLabelYPos = Float.NaN; - } - mUiDelegate.invalidate(); - break; - case MotionEvent.ACTION_POINTER_DOWN: - if (count == 2) { - - mLiveSample = false; - mDownX = (event.getX(0) + event.getX(1)); - Log.v("Main", count + ">>>> ACTION_POINTER_DOWN" + mDownX + " " + mLiveSample); - - mLineX = Float.NaN; - } - break; - case MotionEvent.ACTION_POINTER_UP: - Log.v("Main", ">>>> ACTION_POINTER_UP" + event.getX()); - if (event.getEventTime() - event.getDownTime() < 400) { - Log.v("Main", ">>>> false"); - mLiveSample = true; - listenToChannels(); - } - - default: - Log.v("Main", ">>>> def " + event.getEventTime()); - } - return true; - //return super.onTouchEvent(event); - } - - public void addChannel(String str) { - mPlots.add(new Data(str)); - } - - public void setGraphFPS(boolean on) { - mGraphSelfFps = on; - if (on) { - mPlots.add(new Data(FPS_STRING)); - // mPlots.add(new Data("read")); - } else { - Data remove = null; - for (int i = 0; i < mPlots.size(); i++) { - if (mPlots.get(i).mTitle == FPS_STRING) { - remove = mPlots.get(i); - break; - } - } - if (remove != null) { - mPlots.remove(remove); - } - } - } - - - private void listenToChannels() { - // Vp.fps("read"); - if (mLiveSample) { - listenLive(); - return; - } - int count = mPlots.size(); - - float minX = Float.MAX_VALUE; - float minY = Float.MAX_VALUE; - float maxX = -Float.MAX_VALUE; - float maxY = -Float.MAX_VALUE; - for (int i = 0; i < count; i++) { - Data p = mPlots.get(i); - String channel = p.mTitle; - - - p.mLength = Vp.getAfter(channel, mStartTime, mTime, mValue); - - if (p.mLength == -1) { - continue; - } - - for (int j = 0; j < p.mLength; j++) { - float x = (mTime[j] - mStartTime) * 1E-9f; - p.mX[j] = x; - minX = Math.min(x, minX); - maxX = Math.max(x, maxX); - - float y = mValue[j]; - minY = Math.min(y, minY); - maxY = Math.max(y, maxY); - p.mY[j] = y; - } - Log.v("main", p.mTitle + " " + minX + " -> " + maxX); - } - - minX = 0; - - maxX = minX + duration; - Log.v("main", "Total " + minX + " -> " + maxX); - - updateDataRange(minX, maxX, minY, maxY); - - mUiDelegate.invalidate(); - } - - private void listenLive() { - - int count = mPlots.size(); - - mStartTime = System.nanoTime() - (long) (((double) duration) * 1000000000L); - - float minX = Float.MAX_VALUE; - float minY = Float.MAX_VALUE; - float maxX = -Float.MAX_VALUE; - float maxY = -Float.MAX_VALUE; - for (int i = 0; i < count; i++) { - Data p = mPlots.get(i); - String channel = p.mTitle; - - p.mLength = Vp.getLatest(channel, mTime, mValue); - - if (p.mLength == -1) { - continue; - } - - for (int j = 0; j < p.mLength; j++) { - float x = (mTime[j] - mStartTime) * 1E-9f; - p.mX[j] = x; - minX = Math.min(x, minX); - maxX = Math.max(x, maxX); - - float y = mValue[j]; - minY = Math.min(y, minY); - maxY = Math.max(y, maxY); - p.mY[j] = y; - } - } - if (minX == Float.MAX_VALUE || Float.isNaN(minX)) { - updateDataRange(0, 10, -1, 1); - } else { - updateDataRange(minX, maxX, minY, maxY); - } - - mUiDelegate.invalidate(); - mUiDelegate.postDelayed(this::listenToChannels, sampleDelay); - } - - private void updateDataRange(float minX, float maxX, float minY, float maxY) { - minX = maxX - duration; - // fast to expand slow to contract - float factor = 10; - mMaxY = (mMaxY > maxY) ? (mMaxY + maxY) / 2 : (mMaxY * factor + maxY) / (factor + 1); - mMinY = (mMinY < minY) ? (mMinY + minY) / 2 : (mMinY * factor + minY) / (factor + 1); - mMinX = (mMinX + minX) / 2; - mMaxX = duration + mMinX; - } - - public void onDraw(Canvas canvas) { - int w = mUiDelegate.getWidth(); - int h = mUiDelegate.getHeight(); - drawAxis(canvas, w, h); - drawGrid(canvas, w, h); - for (Data p : mPlots) { - p.plot(canvas, this, w, h); - } - if (mGraphSelfFps || debug) { - Vp.fps(FPS_STRING); - } - - drawTouchLine(canvas, w, h); - } - - private void drawGrid(Canvas canvas, int w, int h) { - double ticksX = calcTick(w, mMaxX - mMinX); - double ticksY = calcTick(h, mMaxY - mMinY); - float minX = (float) (ticksX * Math.ceil((mMinX + ticksX / 100) / ticksX)); - float maxX = (float) (ticksX * Math.floor(mMaxX / ticksX)); - - float scaleX = getScaleX(w, h); - float offX = getOffsetX(w, h); - for (float x = minX; x <= maxX; x += ticksX) { - float xp = scaleX * x + offX; - canvas.drawLine(xp, axisTop, xp, h - axisBottom, mGridPaint); - } - - float minY = (float) (ticksY * Math.ceil((mMinY + ticksY / 100) / ticksY)); - float maxY = (float) (ticksY * Math.floor(mMaxY / ticksY)); - - float offY = getOffsetY(w, h); - float scaleY = getScaleY(w, h); - int count = 0; - int txtPad = 4; - for (float y = minY; y <= maxY; y += ticksY) { - float yp = scaleY * y + offY; - canvas.drawLine(axisLeft, yp, w - axisRight, yp, mGridPaint); - - if ((count & 1) == 1 && (y + ticksY) < maxY) { - String str = df.format(y); - mAxisPaint.getTextBounds(str, 0, str.length(), mBounds); - canvas.drawText(str, axisLeft - mBounds.width() - txtPad, yp, mGridPaint); - } - count++; - } - } - - DecimalFormat df = new DecimalFormat("0.0"); - - void drawTouchLine(Canvas canvas, int w, int h) { - if (Float.isNaN(mLineX)) { - return; - } - if (mLineX < axisLeft) { - mLineX = axisLeft; - } else if (mLineX > (w - axisRight)) { - mLineX = w - axisRight; - } - float dataPos = (mLineX - getOffsetX(w, h)) / getScaleX(w, h); - float yOffset = getOffsetY(w, h); - float yScale = getScaleY(w, h); - float rad = 10; - canvas.drawLine(mLineX, axisTop, mLineX, h - axisBottom, mLinePaint); - int bottom_count = 0; - int top_count = 0; - int pad = 5; - - boolean right = (mLineX < w / 2); - int no_of_plots = mPlots.size(); - for (int i = 0; i < no_of_plots; i++) { - Data plot = mPlots.get(i); - - - int index = plot.findClosestX(dataPos); - if (index == -1) continue; - float value = plot.mY[index]; - float y = yScale * value + yOffset; - canvas.drawRoundRect(mLineX - rad, y - rad, mLineX + rad, y + rad, rad, rad, mLinePaint); - String vString = plot.mTitle + ":" + df.format(value); - mLinePaint.getTextBounds(vString, 0, vString.length(), mBounds); - float yPos = w / 2; - int gap = 60; - if (y > h / 2) { - yPos = y - gap; - bottom_count++; - } else { - top_count++; - yPos = y + gap; - } - float xPos = (right) ? mLineX + gap : mLineX - gap; - float minVGap = mBounds.height(); - float force = 0; - for (int j = 0; j < no_of_plots; j++) { - if (i == j) { - continue; - } - float yOther = mPlots.get(j).lastLabelYPos; - float dist = Math.abs(plot.lastLabelYPos - yOther); - if (dist < minVGap * 2) { - float dir = Math.signum(yPos - yOther); - force = ((dir > 0) ? minVGap : -minVGap) + minVGap / (0.1f + dist); - } - } - if (Float.isNaN(plot.lastLabelYPos)) { - plot.lastLabelYPos = yPos; - } else { - plot.lastLabelYPos = (plot.lastLabelYPos * 49 + yPos+force) / 50; - } - - canvas.drawLine(mLineX, y, xPos, plot.lastLabelYPos, mLinePaint); - canvas.drawText(vString, right ? xPos : xPos - mBounds.width() - pad, plot.lastLabelYPos, mLinePaint); - } - } - - void drawAxis(Canvas canvas, int w, int h) { - int txtPad = 4; - canvas.drawRGB(200, 230, 255); - canvas.drawLine(axisLeft, axisTop, axisLeft, h - axisBottom, mAxisPaint); - canvas.drawLine(axisLeft, h - axisBottom, w - axisRight, h - axisBottom, mAxisPaint); - float y0 = getOffsetY(w, h); - canvas.drawLine(axisLeft, y0, w - axisRight, y0, mAxisPaint); - String str = df.format(mMaxY); - mAxisPaint.getTextBounds(str, 0, str.length(), mBounds); - canvas.drawText(str, axisLeft - mBounds.width() - txtPad, axisTop, mAxisPaint); - str = df.format(mMinY); - mAxisPaint.getTextBounds(str, 0, str.length(), mBounds); - canvas.drawText(str, axisLeft - mBounds.width() - txtPad, h - axisBottom, mAxisPaint); - } - - public float getScaleX(int w, int h) { - float rangeX = mMaxX - mMinX; - float graphSpanX = w - axisLeft - axisRight; - return graphSpanX / rangeX; - } - - public float getScaleY(int w, int h) { - float rangeY = mMaxY - mMinY; - float graphSpanY = h - axisTop - axisBottom; - return -graphSpanY / rangeY; - } - - public float getOffsetX(int w, int h) { - return axisLeft - mMinX * getScaleX(w, h); - } - - public float getOffsetY(int w, int h) { - return h - axisBottom - mMinY * getScaleY(w, h); - } - - static public double calcTick(int scr, double range) { - int aprox_x_ticks = scr / 100; - int type = 1; - double best = Math.log10(range / (aprox_x_ticks)); - double n = Math.log10(range / (aprox_x_ticks * 2)); - if (fraction(n) < fraction(best)) { - best = n; - type = 2; - } - n = Math.log10(range / (aprox_x_ticks * 5)); - if (fraction(n) < fraction(best)) { - best = n; - type = 5; - } - return type * Math.pow(10, Math.floor(best)); - } - - static double fraction(double x) { - return x - Math.floor(x); - } -} diff --git a/projects/ComposeConstraintLayout/app/src/main/java/com/example/constraintlayout/test.kt b/projects/ComposeConstraintLayout/app/src/main/java/com/example/constraintlayout/test.kt index 37b756be8..fdf68b3a0 100644 --- a/projects/ComposeConstraintLayout/app/src/main/java/com/example/constraintlayout/test.kt +++ b/projects/ComposeConstraintLayout/app/src/main/java/com/example/constraintlayout/test.kt @@ -270,7 +270,8 @@ public fun Screen4() { modifier = Modifier .fillMaxSize() ) { - Image(modifier = Modifier.layoutId("image"), + Image( + modifier = Modifier.layoutId("image"), painter = painterResource(id = R.drawable.intercom_snooze), contentDescription = null ) @@ -279,13 +280,16 @@ public fun Screen4() { text = stringResource(id = R.string.welcome_header), style = MaterialTheme.typography.h5, ) - Text(modifier = Modifier.layoutId("tag1"), + Text( + modifier = Modifier.layoutId("tag1"), text = stringResource(id = R.string.welcome_tagline1) ) - Text(modifier = Modifier.layoutId("tag2"), + Text( + modifier = Modifier.layoutId("tag2"), text = stringResource(id = R.string.welcome_tagline2) ) - Text(modifier = Modifier.layoutId("tag3"), + Text( + modifier = Modifier.layoutId("tag3"), text = stringResource(id = R.string.welcome_tagline3) ) Button(modifier = Modifier.layoutId("bSignup"), @@ -361,7 +365,8 @@ public fun Screen5() { modifier = Modifier .fillMaxSize() ) { - Image(modifier = Modifier.layoutId("image"), + Image( + modifier = Modifier.layoutId("image"), painter = painterResource(id = R.drawable.intercom_snooze), contentDescription = null ) @@ -370,13 +375,16 @@ public fun Screen5() { text = stringResource(id = R.string.welcome_header), style = MaterialTheme.typography.h5, ) - Text(modifier = Modifier.layoutId("tag1"), + Text( + modifier = Modifier.layoutId("tag1"), text = stringResource(id = R.string.welcome_tagline1) ) - Text(modifier = Modifier.layoutId("tag2"), + Text( + modifier = Modifier.layoutId("tag2"), text = stringResource(id = R.string.welcome_tagline2) ) - Text(modifier = Modifier.layoutId("tag3"), + Text( + modifier = Modifier.layoutId("tag3"), text = stringResource(id = R.string.welcome_tagline3) ) Button(modifier = Modifier.layoutId("bSignup"), @@ -466,7 +474,8 @@ public fun ScreenExample2() { @Composable public fun ScreenExample3() { ConstraintLayout( - ConstraintSet(""" + ConstraintSet( + """ { Header: { exportAs: 'example 3'}, g1: { type: 'vGuideline', start: 80 }, @@ -479,7 +488,8 @@ public fun ScreenExample3() { start: ['g1', 'start'] } } - """), + """ + ), modifier = Modifier.fillMaxSize() ) { Button( @@ -500,7 +510,8 @@ public fun ScreenExample3() { @Composable public fun ScreenExample4() { ConstraintLayout( - ConstraintSet(""" + ConstraintSet( + """ { Header: { exportAs: 'example 4'}, g1: { type: 'vGuideline', percent: 0.5 }, @@ -508,7 +519,8 @@ public fun ScreenExample4() { start: ['g1', 'start'] } } - """), + """ + ), modifier = Modifier .fillMaxSize() .background(Color.White) @@ -526,7 +538,8 @@ public fun ScreenExample4() { @Composable public fun ScreenExample5() { ConstraintLayout( - ConstraintSet(""" + ConstraintSet( + """ { m1 : 100, barrier: { type: 'barrier', direction: 'end', contains: ['a','b'] }, @@ -545,7 +558,8 @@ public fun ScreenExample5() { top : ['b', 'bottom', 'm1' ] } } - """), + """ + ), modifier = Modifier .fillMaxSize() ) { @@ -574,7 +588,8 @@ public fun ScreenExample5() { @Composable public fun ScreenExample6() { ConstraintLayout( - ConstraintSet(""" + ConstraintSet( + """ { Variables: { bottom: 20 @@ -607,7 +622,8 @@ public fun ScreenExample6() { top: ['b', 'bottom'] } } - """), + """ + ), modifier = Modifier .fillMaxSize() ) { @@ -659,7 +675,7 @@ public fun ScreenExample7() { animationSpec = tween(2000) ) Column { - MotionLayout( + MotionLayout( modifier = Modifier .fillMaxWidth() .height(400.dp), @@ -725,7 +741,8 @@ public fun ScreenExample7() { @Composable public fun ScreenExample8() { - var cs1 = ConstraintSet(""" + var cs1 = ConstraintSet( + """ { a: { rotationZ: -30, @@ -734,8 +751,10 @@ public fun ScreenExample8() { top: ['parent', 'top', 16] } } - """) - var cs2 = ConstraintSet(""" + """ + ) + var cs2 = ConstraintSet( + """ { a: { rotationZ: 30, @@ -743,23 +762,28 @@ public fun ScreenExample8() { top: ['parent', 'top', 16] } } - """) - var cs3 = ConstraintSet(""" + """ + ) + var cs3 = ConstraintSet( + """ { a: { end: ['parent', 'end', 16], bottom: ['parent', 'bottom', 16] } } - """) - var cs4 = ConstraintSet(""" + """ + ) + var cs4 = ConstraintSet( + """ { a: { start: ['parent', 'start', 16], bottom: ['parent', 'bottom', 16] } } - """) + """ + ) var start by remember { mutableStateOf(cs1) } var end by remember { mutableStateOf(cs2) } @@ -842,7 +866,8 @@ public fun ScreenExample8() { @Composable public fun ScreenExample9() { ConstraintLayout( - ConstraintSet(""" + ConstraintSet( + """ { center: { center: 'parent' @@ -860,7 +885,8 @@ public fun ScreenExample9() { h11: { circular: ['center', 330, 100] }, h12: { circular: ['center', 0, 100] } } - """), + """ + ), modifier = Modifier .fillMaxSize() ) { @@ -884,7 +910,8 @@ public fun ScreenExample9() { @Composable public fun ScreenExample10() { ConstraintLayout( - ConstraintSet(""" + ConstraintSet( + """ { h1: { circular: ['parent', 0, 100] }, h2: { circular: ['parent', 40, 100], rotationZ: 40 }, @@ -896,55 +923,74 @@ public fun ScreenExample10() { h8: { circular: ['parent', 280, 100], rotationZ: 280 }, h9: { circular: ['parent', 320, 100], rotationZ: 320 } } - """), + """ + ), modifier = Modifier .fillMaxSize() ) { - Box(modifier = Modifier - .layoutId("h1") - .width(100.dp) - .height(60.dp) - .background(Color.Red)) - Box(modifier = Modifier - .layoutId("h2") - .width(100.dp) - .height(60.dp) - .background(Color.Green)) - Box(modifier = Modifier - .layoutId("h3") - .width(100.dp) - .height(60.dp) - .background(Color.Blue)) - Box(modifier = Modifier - .layoutId("h4") - .width(100.dp) - .height(60.dp) - .background(Color.Gray)) - Box(modifier = Modifier - .layoutId("h5") - .width(100.dp) - .height(60.dp) - .background(Color.Yellow)) - Box(modifier = Modifier - .layoutId("h6") - .width(100.dp) - .height(60.dp) - .background(Color.Cyan)) - Box(modifier = Modifier - .layoutId("h7") - .width(100.dp) - .height(60.dp) - .background(Color.Magenta)) - Box(modifier = Modifier - .layoutId("h8") - .width(100.dp) - .height(60.dp) - .background(Color.Red)) - Box(modifier = Modifier - .layoutId("h9") - .width(100.dp) - .height(60.dp) - .background(Color.DarkGray)) + Box( + modifier = Modifier + .layoutId("h1") + .width(100.dp) + .height(60.dp) + .background(Color.Red) + ) + Box( + modifier = Modifier + .layoutId("h2") + .width(100.dp) + .height(60.dp) + .background(Color.Green) + ) + Box( + modifier = Modifier + .layoutId("h3") + .width(100.dp) + .height(60.dp) + .background(Color.Blue) + ) + Box( + modifier = Modifier + .layoutId("h4") + .width(100.dp) + .height(60.dp) + .background(Color.Gray) + ) + Box( + modifier = Modifier + .layoutId("h5") + .width(100.dp) + .height(60.dp) + .background(Color.Yellow) + ) + Box( + modifier = Modifier + .layoutId("h6") + .width(100.dp) + .height(60.dp) + .background(Color.Cyan) + ) + Box( + modifier = Modifier + .layoutId("h7") + .width(100.dp) + .height(60.dp) + .background(Color.Magenta) + ) + Box( + modifier = Modifier + .layoutId("h8") + .width(100.dp) + .height(60.dp) + .background(Color.Red) + ) + Box( + modifier = Modifier + .layoutId("h9") + .width(100.dp) + .height(60.dp) + .background(Color.DarkGray) + ) } } @@ -961,7 +1007,8 @@ public fun ScreenExample11() { Text(text = "Run") } MotionLayout( - ConstraintSet(""" + ConstraintSet( + """ { h1: { circular: ['parent', 0, 100] }, h2: { circular: ['parent', 40, 100], rotationZ: 40 }, @@ -973,8 +1020,10 @@ public fun ScreenExample11() { h8: { circular: ['parent', 280, 100], rotationZ: 280 }, h9: { circular: ['parent', 320, 100], rotationZ: 320 } } - """), - ConstraintSet(""" + """ + ), + ConstraintSet( + """ { h1: { circular: ['parent', 0, 100], rotationZ: 360 }, h2: { circular: ['parent', 40, 100], rotationZ: 400 }, @@ -986,57 +1035,76 @@ public fun ScreenExample11() { h8: { circular: ['parent', 280, 100], rotationZ: 640 }, h9: { circular: ['parent', 320, 100], rotationZ: 680 } } - """), + """ + ), progress = progress, modifier = Modifier .fillMaxSize() .background(Color.White) ) { - Box(modifier = Modifier - .layoutId("h1") - .width(100.dp) - .height(60.dp) - .background(Color.Red)) - Box(modifier = Modifier - .layoutId("h2") - .width(100.dp) - .height(60.dp) - .background(Color.Green)) - Box(modifier = Modifier - .layoutId("h3") - .width(100.dp) - .height(60.dp) - .background(Color.Blue)) - Box(modifier = Modifier - .layoutId("h4") - .width(100.dp) - .height(60.dp) - .background(Color.Gray)) - Box(modifier = Modifier - .layoutId("h5") - .width(100.dp) - .height(60.dp) - .background(Color.Yellow)) - Box(modifier = Modifier - .layoutId("h6") - .width(100.dp) - .height(60.dp) - .background(Color.Cyan)) - Box(modifier = Modifier - .layoutId("h7") - .width(100.dp) - .height(60.dp) - .background(Color.Magenta)) - Box(modifier = Modifier - .layoutId("h8") - .width(100.dp) - .height(60.dp) - .background(Color.Red)) - Box(modifier = Modifier - .layoutId("h9") - .width(100.dp) - .height(60.dp) - .background(Color.DarkGray)) + Box( + modifier = Modifier + .layoutId("h1") + .width(100.dp) + .height(60.dp) + .background(Color.Red) + ) + Box( + modifier = Modifier + .layoutId("h2") + .width(100.dp) + .height(60.dp) + .background(Color.Green) + ) + Box( + modifier = Modifier + .layoutId("h3") + .width(100.dp) + .height(60.dp) + .background(Color.Blue) + ) + Box( + modifier = Modifier + .layoutId("h4") + .width(100.dp) + .height(60.dp) + .background(Color.Gray) + ) + Box( + modifier = Modifier + .layoutId("h5") + .width(100.dp) + .height(60.dp) + .background(Color.Yellow) + ) + Box( + modifier = Modifier + .layoutId("h6") + .width(100.dp) + .height(60.dp) + .background(Color.Cyan) + ) + Box( + modifier = Modifier + .layoutId("h7") + .width(100.dp) + .height(60.dp) + .background(Color.Magenta) + ) + Box( + modifier = Modifier + .layoutId("h8") + .width(100.dp) + .height(60.dp) + .background(Color.Red) + ) + Box( + modifier = Modifier + .layoutId("h9") + .width(100.dp) + .height(60.dp) + .background(Color.DarkGray) + ) } } } @@ -1078,18 +1146,22 @@ public fun ScreenExample12() { Button(onClick = { animateToEnd = !animateToEnd }) { Text(text = "Run ScreenExample12") } - MotionLayout(cs1, cs2, + MotionLayout( + cs1, cs2, progress = progress, modifier = Modifier .fillMaxSize() .background(Color.White) ) { - var colors = arrayListOf(Color.Red, Color.Green, Color.Blue, Color.Cyan, Color.Yellow) + var colors = + arrayListOf(Color.Red, Color.Green, Color.Blue, Color.Cyan, Color.Yellow) for (i in 1..36) { - Box(modifier = Modifier - .layoutId("h$i", "box") - .background(colors[i % colors.size])) + Box( + modifier = Modifier + .layoutId("h$i", "box") + .background(colors[i % colors.size]) + ) } } } @@ -1109,8 +1181,9 @@ public fun ScreenExample13() { modifier = Modifier .fillMaxWidth() .height(400.dp) - .background(Color.White) , - start = ConstraintSet(""" + .background(Color.White), + start = ConstraintSet( + """ { a: { start: ['parent', 'start', 16], @@ -1141,12 +1214,15 @@ public fun ScreenExample13() { """ ), debug = EnumSet.of(MotionLayoutDebugFlags.SHOW_ALL), - progress = cprogress) { + progress = cprogress + ) { var properties = motionProperties("a") - Text(text = "Hello", modifier = Modifier - .layoutId(properties.value.id()) - .background(properties.value.color("background")) - ,color = properties.value.color("textColor") + Text( + text = "Hello", + modifier = Modifier + .layoutId(properties.value.id()) + .background(properties.value.color("background")), + color = properties.value.color("textColor") //,fontSize = properties.value.fontSize("textSize") ) } @@ -1171,8 +1247,9 @@ public fun ScreenExample14() { modifier = Modifier .fillMaxWidth() .height(400.dp) - .background(Color.White) , - start = ConstraintSet(""" + .background(Color.White), + start = ConstraintSet( + """ { a: { width: 40, @@ -1195,7 +1272,8 @@ public fun ScreenExample14() { } """ ), - transition = Transition(""" + transition = Transition( + """ { KeyFrames: { KeyPositions: [ @@ -1208,12 +1286,16 @@ public fun ScreenExample14() { ] } } - """), + """ + ), debug = EnumSet.of(MotionLayoutDebugFlags.SHOW_ALL), - progress = progress) { - Box(modifier = Modifier - .layoutId("a") - .background(Color.Red)) + progress = progress + ) { + Box( + modifier = Modifier + .layoutId("a") + .background(Color.Red) + ) } Button(onClick = { animateToEnd = !animateToEnd }) { @@ -1237,8 +1319,9 @@ public fun ScreenExample15() { modifier = Modifier .fillMaxWidth() .height(400.dp) - .background(Color.White) , - start = ConstraintSet(""" + .background(Color.White), + start = ConstraintSet( + """ { a: { width: 40, @@ -1262,7 +1345,8 @@ public fun ScreenExample15() { } """ ), - transition = Transition(""" + transition = Transition( + """ { KeyFrames: { KeyPositions: [ @@ -1275,9 +1359,11 @@ public fun ScreenExample15() { ] } } - """), + """ + ), debug = EnumSet.of(MotionLayoutDebugFlags.SHOW_ALL), - progress = progress) { + progress = progress + ) { Box(modifier = Modifier .onPlaced { vpSend("width", it.size.width.toFloat()) @@ -1286,7 +1372,7 @@ public fun ScreenExample15() { val matrix = Matrix() it.transformFrom(it.parentCoordinates!!, matrix); - val rot = 180.0f*calculateZRotation(matrix.values)/ PI.toFloat() + val rot = 180.0f * calculateZRotation(matrix.values) / PI.toFloat() println(rot) vpSend("rot", rot) } @@ -1297,20 +1383,22 @@ public fun ScreenExample15() { Button(onClick = { animateToEnd = !animateToEnd }) { Text(text = "Run ScreenExample15") } - vpSend("progress",progress*1000) - VpGraph(modifier = Modifier.fillMaxSize(),"rot","progress","x","width") + vpSend("progress", progress * 1000) + VpGraph(modifier = Modifier.fillMaxSize(), "rot", "progress", "x", "width") } } -fun calculateZRotation(matrix: FloatArray): Float { + +fun calculateZRotation(matrix: FloatArray): Float { // Extract the values from the matrix val m00 = matrix[0] val m01 = matrix[1] - val m10 = matrix[1*3+0] - val m11 = matrix[1*3+1] + val m10 = matrix[1 * 3 + 0] + val m11 = matrix[1 * 3 + 1] // Calculate the z-rotation angle return -atan2(m01, m00) } + @Preview(group = "motion8") @Composable public fun ScreenExample16() { @@ -1326,7 +1414,8 @@ public fun ScreenExample16() { .fillMaxWidth() .height(400.dp) .background(Color.White), - motionScene = MotionScene("""{ + motionScene = MotionScene( + """{ Header: { exportAs: 'motion8' }, @@ -1365,12 +1454,16 @@ public fun ScreenExample16() { } } } - }"""), + }""" + ), debug = EnumSet.of(MotionLayoutDebugFlags.SHOW_ALL), - progress = progress) { - Box(modifier = Modifier - .layoutId("a") - .background(Color.Red)) + progress = progress + ) { + Box( + modifier = Modifier + .layoutId("a") + .background(Color.Red) + ) } Button(onClick = { animateToEnd = !animateToEnd }) { @@ -1393,8 +1486,9 @@ public fun ScreenExample17() { modifier = Modifier .fillMaxWidth() .height(400.dp) - .background(Color.White) , - start = ConstraintSet(""" + .background(Color.White), + start = ConstraintSet( + """ { a: { width: 40, @@ -1431,13 +1525,18 @@ public fun ScreenExample17() { """ ), debug = EnumSet.of(MotionLayoutDebugFlags.SHOW_ALL), - progress = progress) { - Box(modifier = Modifier - .layoutId("a") - .background(Color.Red)) - Box(modifier = Modifier - .layoutId("b") - .background(Color.Blue)) + progress = progress + ) { + Box( + modifier = Modifier + .layoutId("a") + .background(Color.Red) + ) + Box( + modifier = Modifier + .layoutId("b") + .background(Color.Blue) + ) } Button(onClick = { animateToEnd = !animateToEnd }) { @@ -1461,7 +1560,8 @@ public fun ScreenExample18() { .fillMaxWidth() .height(400.dp) .background(Color.White), - motionScene = MotionScene("""{ + motionScene = MotionScene( + """{ Header: { exportAs: 'example 18' }, @@ -1504,15 +1604,21 @@ public fun ScreenExample18() { } } } -"""), +""" + ), debug = EnumSet.of(MotionLayoutDebugFlags.SHOW_ALL), - progress = progress) { - Box(modifier = Modifier - .layoutId("a") - .background(Color.Red)) - Box(modifier = Modifier - .layoutId("b") - .background(Color.Green)) + progress = progress + ) { + Box( + modifier = Modifier + .layoutId("a") + .background(Color.Red) + ) + Box( + modifier = Modifier + .layoutId("b") + .background(Color.Green) + ) } Button(onClick = { animateToEnd = !animateToEnd }) { @@ -1534,7 +1640,9 @@ public fun ScreenExample19() { Button(onClick = { animateToEnd = !animateToEnd }) { Text(text = "Run exemple19") } - MotionLayout(motionScene = MotionScene("""{ + MotionLayout( + motionScene = MotionScene( + """{ Header: { exportAs: 'exemple19' }, @@ -1567,18 +1675,21 @@ public fun ScreenExample19() { } } } - }"""), + }""" + ), progress = progress, modifier = Modifier .fillMaxSize() .background(Color.White) ) { - var colors = arrayListOf(Color.Red, Color.Green, Color.Blue, Color.Cyan, Color.Yellow) + var colors = + arrayListOf(Color.Red, Color.Green, Color.Blue, Color.Cyan, Color.Yellow) for (i in 1..36) { - Box(modifier = Modifier - .layoutId("h$i", "box") - .background(colors[i % colors.size]) + Box( + modifier = Modifier + .layoutId("h$i", "box") + .background(colors[i % colors.size]) ) } } @@ -1632,7 +1743,8 @@ public fun ScreenExample21() { @Composable public fun ScreenExample20() { ConstraintLayout( - ConstraintSet(""" + ConstraintSet( + """ { Header: { exportAs: 'guidelines' @@ -1671,7 +1783,8 @@ public fun ScreenExample20() { end: ['g5', 'end', 'margin'] } } - """), + """ + ), modifier = Modifier .fillMaxSize() .background(Color.White) @@ -1689,13 +1802,15 @@ public fun ScreenExample20() { @Composable fun Playground() { ConstraintLayout( - ConstraintSet(""" + ConstraintSet( + """ { Header: { exportAs: 'Playground' } } - """), + """ + ), modifier = Modifier .fillMaxSize() .background(Color.White) @@ -1707,7 +1822,8 @@ fun Playground() { @Composable public fun ChainNew1() { ConstraintLayout( - ConstraintSet(""" + ConstraintSet( + """ { Variables: { bottom: 20 @@ -1749,7 +1865,8 @@ public fun ChainNew1() { top: ['b', 'bottom'] } } - """), + """ + ), modifier = Modifier .fillMaxSize() ) { @@ -1797,7 +1914,8 @@ public fun ChainNew1() { @Composable public fun ChainNew2() { ConstraintLayout( - ConstraintSet(""" + ConstraintSet( + """ { Variables: { bottom: 20 @@ -1841,7 +1959,8 @@ public fun ChainNew2() { bottom: ['b', 'bottom' ] } } - """), + """ + ), modifier = Modifier .fillMaxSize() ) { @@ -1890,7 +2009,8 @@ public fun ChainNew2() { @Composable public fun ChainNew3() { ConstraintLayout( - ConstraintSet(""" + ConstraintSet( + """ { Variables: { bottom: 20 @@ -1964,7 +2084,7 @@ public fun ChainNew3() { } """ - ), + ), modifier = Modifier .fillMaxSize() .background(Color.LightGray) @@ -1989,14 +2109,12 @@ public fun ChainNew3() { } - - - @Preview(group = "new4") @Composable public fun ChainNew4() { ConstraintLayout( - ConstraintSet(""" + ConstraintSet( + """ { Variables: { bottom: 20