From b8c9a05963ca0817451850853ae8c47395d2f11a Mon Sep 17 00:00:00 2001 From: code-explorer786 <68312688+code-explorer786@users.noreply.github.com> Date: Sun, 16 Jun 2024 14:13:10 +0700 Subject: [PATCH 1/9] LCanvas: jumps -> statements.jumps Original canvas.jumps is now a reference to canvas.statements.jumps. Please remove canvas.jumps in favour of canvas.statements.jumps in a future release. Thanks! --- core/src/mindustry/logic/LCanvas.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/core/src/mindustry/logic/LCanvas.java b/core/src/mindustry/logic/LCanvas.java index b78908ef6f..8924f68d4b 100644 --- a/core/src/mindustry/logic/LCanvas.java +++ b/core/src/mindustry/logic/LCanvas.java @@ -26,7 +26,7 @@ public class LCanvas extends Table{ public DragLayout statements; public ScrollPane pane; - public Group jumps; + public Group jumps; // compatibility StatementElem dragging; StatementElem hovered; @@ -106,14 +106,14 @@ public class LCanvas extends Table{ clear(); statements = new DragLayout(); - jumps = new WidgetGroup(); + jumps = statements.jumps; pane = pane(t -> { t.center(); t.add(statements).pad(2f).center().width(targetWidth); - t.addChild(jumps); + t.addChild(statements.jumps); - jumps.cullable = false; + statements.jumps.cullable = false; }).grow().get(); pane.setFlickScroll(false); pane.setScrollYForce(s); @@ -141,7 +141,7 @@ public class LCanvas extends Table{ } public void load(String asm){ - jumps.clear(); + statements.jumps.clear(); Seq statements = LAssembler.read(asm, privileged); statements.truncate(LExecutor.maxInstructions); @@ -158,7 +158,7 @@ public class LCanvas extends Table{ } public void clearStatements(){ - jumps.clear(); + statements.jumps.clear(); statements.clearChildren(); statements.layout(); } @@ -195,6 +195,7 @@ public class LCanvas extends Table{ Seq seq = new Seq<>(); int insertPosition = 0; boolean invalidated; + public Group jumps = new WidgetGroup(); { setTransform(true); From b58a6bb8d6de7dc85c9933e11b46eaeb639007a9 Mon Sep 17 00:00:00 2001 From: code-explorer786 <68312688+code-explorer786@users.noreply.github.com> Date: Wed, 26 Jun 2024 10:37:20 +0700 Subject: [PATCH 2/9] wip: different heights for jump arrows Note that this is very incomplete, for two different reasons: - it's O(n^2) or potentially worse - it makes the GC work hard (it invokes `new` in `LCanvas.DragLayout.layout()`) For the second reason this does not clearly fit the criteria of CONTRIBUTING.md, but I have yet to figure out ways to work around this (or make the algorithm more efficient). --- core/assets/bundles/bundle.properties | 1 + core/src/mindustry/logic/LCanvas.java | 93 ++++++++++++++++++- core/src/mindustry/logic/LStatements.java | 2 +- .../ui/dialogs/SettingsMenuDialog.java | 2 + 4 files changed, 94 insertions(+), 4 deletions(-) diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index 08f271d82d..22a9289eed 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -1225,6 +1225,7 @@ setting.bridgeopacity.name = Bridge Opacity setting.playerchat.name = Display Player Bubble Chat setting.showweather.name = Show Weather Graphics setting.hidedisplays.name = Hide Logic Displays +setting.dynamicjumpheights.name = Dynamic Jump Heights setting.macnotch.name = Adapt interface to display notch setting.macnotch.description = Restart required to apply changes steam.friendsonly = Friends Only diff --git a/core/src/mindustry/logic/LCanvas.java b/core/src/mindustry/logic/LCanvas.java index 8924f68d4b..417931e792 100644 --- a/core/src/mindustry/logic/LCanvas.java +++ b/core/src/mindustry/logic/LCanvas.java @@ -21,8 +21,10 @@ import mindustry.ui.*; public class LCanvas extends Table{ public static final int maxJumpsDrawn = 100; + private static final int invalidJump = Integer.MAX_VALUE; // terrible hack //ew static variables static LCanvas canvas; + private static boolean dynamicJumpHeights = false; public DragLayout statements; public ScrollPane pane; @@ -108,6 +110,8 @@ public class LCanvas extends Table{ statements = new DragLayout(); jumps = statements.jumps; + dynamicJumpHeights = Core.settings.getBool("dynamicjumpheights", false); + pane = pane(t -> { t.center(); t.add(statements).pad(2f).center().width(targetWidth); @@ -196,6 +200,7 @@ public class LCanvas extends Table{ int insertPosition = 0; boolean invalidated; public Group jumps = new WidgetGroup(); + private Seq processedJumps = new Seq(); { setTransform(true); @@ -251,13 +256,73 @@ public class LCanvas extends Table{ } } - if(parent != null) parent.invalidateHierarchy(); + if(dynamicJumpHeights) setJumpHeights(); + + if(parent != null) parent.invalidateHierarchy();//don't invalid self if(parent != null && parent instanceof Table){ setCullingArea(parent.getCullingArea()); } } + private void setJumpHeights(){ + SnapshotSeq jumpsChildren = jumps.getChildren(); + processedJumps.clear(); + jumpsChildren.each(e -> { + if(e instanceof JumpCurve e2){ + e2.prepareHeight(); + if(e2.jumpUIBegin != invalidJump){ + processedJumps.add(e2); + } + } + }); + processedJumps.sort((a,b) -> a.jumpUIBegin - b.jumpUIBegin); + + Seq occupiers = new Seq(); + Bits occupied = new Bits(); + for(int i = 0; i < processedJumps.size; i++){ + JumpCurve cur = processedJumps.get(i); + occupiers.retainAll(e -> { + if(e.jumpUIEnd > cur.jumpUIBegin) return true; + occupied.clear(e.predHeight); + return false; + }); + int h = getJumpHeight(i, occupiers, occupied); + occupiers.add(cur); + occupied.set(h); + } + } + + private int getJumpHeight(int index, Seq occupiers, Bits occupied){ + JumpCurve jmp = processedJumps.get(index); + if(jmp.markedDone) return jmp.predHeight; + + // TODO: optimize for LESS cloning + Seq tmpOccupiers = new Seq(occupiers); + Bits tmpOccupied = new Bits(); + tmpOccupied.set(occupied); + + int max = -1; + for(int i = index + 1; i < processedJumps.size; i++){ + JumpCurve cur = processedJumps.get(i); + if(cur.jumpUIEnd > jmp.jumpUIEnd) continue; + tmpOccupiers.retainAll(e -> { + if(e.jumpUIEnd > cur.jumpUIBegin) return true; + tmpOccupied.clear(e.predHeight); + return false; + }); + int h = getJumpHeight(i, tmpOccupiers, tmpOccupied); + tmpOccupiers.add(cur); + tmpOccupied.set(h); + max = Math.max(max, h); + } + + jmp.predHeight = occupied.nextClearBit(max + 1); + jmp.markedDone = true; + + return jmp.predHeight; + } + @Override public float getPrefWidth(){ return prefWidth; @@ -452,12 +517,14 @@ public class LCanvas extends Table{ ClickListener listener; public JumpCurve curve; + public StatementElem elem; - public JumpButton(Prov getter, Cons setter){ + public JumpButton(Prov getter, Cons setter, StatementElem elem){ super(Tex.logicNode, new ImageButtonStyle(){{ imageUpColor = Color.white; }}); + this.elem = elem; to = getter; addListener(listener = new ClickListener()); @@ -518,6 +585,11 @@ public class LCanvas extends Table{ public static class JumpCurve extends Element{ public JumpButton button; + // for jump prediction; see DragLayout + public int predHeight = 0; + public boolean markedDone = false; + public int jumpUIBegin = 0, jumpUIEnd = 0; + public JumpCurve(JumpButton button){ this.button = button; } @@ -575,7 +647,7 @@ public class LCanvas extends Table{ Lines.stroke(4f, button.color); Draw.alpha(parentAlpha); - float dist = 100f; + float dist = 100f + 100f * (float) predHeight; //square jumps if(false){ @@ -599,5 +671,20 @@ public class LCanvas extends Table{ x2, y2, Math.max(18, (int)(Mathf.dst(x, y, x2, y2) / 6))); } + + public void prepareHeight(){ + if(this.button.to.get() == null){ + this.markedDone = true; + this.predHeight = 0; + this.jumpUIBegin = this.jumpUIEnd = invalidJump; + } else { + this.markedDone = false; + int i = this.button.elem.index; + int j = this.button.to.get().index; + this.jumpUIBegin = Math.min(i,j); + this.jumpUIEnd = Math.max(i,j); + // height will be recalculated later + } + } } } diff --git a/core/src/mindustry/logic/LStatements.java b/core/src/mindustry/logic/LStatements.java index 9d8faa880a..1c03625234 100644 --- a/core/src/mindustry/logic/LStatements.java +++ b/core/src/mindustry/logic/LStatements.java @@ -870,7 +870,7 @@ public class LStatements{ table.table(this::rebuild); table.add().growX(); - table.add(new JumpButton(() -> dest, s -> dest = s)).size(30).right().padLeft(-8); + table.add(new JumpButton(() -> dest, s -> dest = s, this.elem)).size(30).right().padLeft(-8); String name = name(); diff --git a/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java b/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java index 01f7c05a53..04d2d93e61 100644 --- a/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java +++ b/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java @@ -502,6 +502,8 @@ public class SettingsMenuDialog extends BaseDialog{ if(!mobile){ Core.settings.put("swapdiagonal", false); } + + graphics.checkPref("dynamicjumpheights", true); } public void exportData(Fi file) throws IOException{ From 26105a64f144dbad8ae01a025aa004f1e80a6022 Mon Sep 17 00:00:00 2001 From: code-explorer786 <68312688+code-explorer786@users.noreply.github.com> Date: Thu, 27 Jun 2024 10:25:37 +0700 Subject: [PATCH 3/9] Use pools It feels hacky, but it works regardless. The GC should be working as hard as it was beforehand. --- core/src/mindustry/logic/LCanvas.java | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/core/src/mindustry/logic/LCanvas.java b/core/src/mindustry/logic/LCanvas.java index 417931e792..10e2e8279b 100644 --- a/core/src/mindustry/logic/LCanvas.java +++ b/core/src/mindustry/logic/LCanvas.java @@ -13,6 +13,7 @@ import arc.scene.ui.*; import arc.scene.ui.layout.*; import arc.struct.*; import arc.util.*; +import arc.util.pooling.*; import mindustry.*; import mindustry.gen.*; import mindustry.graphics.*; @@ -278,8 +279,10 @@ public class LCanvas extends Table{ }); processedJumps.sort((a,b) -> a.jumpUIBegin - b.jumpUIBegin); - Seq occupiers = new Seq(); - Bits occupied = new Bits(); + Seq occupiers = Pools.obtain(Seq.class, Seq::new); + Bits occupied = Pools.obtain(Bits.class, Bits::new); + occupiers.clear(); + occupied.clear(); for(int i = 0; i < processedJumps.size; i++){ JumpCurve cur = processedJumps.get(i); occupiers.retainAll(e -> { @@ -291,15 +294,21 @@ public class LCanvas extends Table{ occupiers.add(cur); occupied.set(h); } + + occupied.clear(); + occupiers.clear(); + Pools.free(occupied); + Pools.free(occupiers); } private int getJumpHeight(int index, Seq occupiers, Bits occupied){ JumpCurve jmp = processedJumps.get(index); if(jmp.markedDone) return jmp.predHeight; - // TODO: optimize for LESS cloning - Seq tmpOccupiers = new Seq(occupiers); - Bits tmpOccupied = new Bits(); + Seq tmpOccupiers = Pools.obtain(Seq.class, Seq::new).clear(); + Bits tmpOccupied = Pools.obtain(Bits.class, Bits::new); + tmpOccupiers.set(occupiers); + tmpOccupied.clear(); tmpOccupied.set(occupied); int max = -1; @@ -320,6 +329,11 @@ public class LCanvas extends Table{ jmp.predHeight = occupied.nextClearBit(max + 1); jmp.markedDone = true; + tmpOccupied.clear(); + tmpOccupiers.clear(); + Pools.free(tmpOccupied); + Pools.free(tmpOccupiers); + return jmp.predHeight; } From 282d21db05b97b4e27448c765f5b3a542990f944 Mon Sep 17 00:00:00 2001 From: code-explorer786 <68312688+code-explorer786@users.noreply.github.com> Date: Thu, 27 Jun 2024 15:47:18 +0700 Subject: [PATCH 4/9] Update jump heights only when necessary. This method uses signaling (through a boolean) and I feel like it's extremely hacky, but I tested this and it (kind of?) works. Unfortunately I found this to potentially lag when resizing. I am not sure if this is guaranteed to work (as in, produce correct behaviour) or not, but to my understanding only LogicDialog uses LCanvas (grep out "LCanvas\|canvas" and see) so it will most likely work? I tested this and accidentally found an unrelated issue related to LCanvas.maxJumpsDrawn, but I hope this will be fixed in the near future. --- core/src/mindustry/logic/LCanvas.java | 13 ++++++++++++- core/src/mindustry/logic/LogicDialog.java | 2 ++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/core/src/mindustry/logic/LCanvas.java b/core/src/mindustry/logic/LCanvas.java index 10e2e8279b..0fe774b228 100644 --- a/core/src/mindustry/logic/LCanvas.java +++ b/core/src/mindustry/logic/LCanvas.java @@ -159,6 +159,7 @@ public class LCanvas extends Table{ st.setupUI(); } + this.statements.updateJumpHeights = true; this.statements.layout(); } @@ -202,6 +203,7 @@ public class LCanvas extends Table{ boolean invalidated; public Group jumps = new WidgetGroup(); private Seq processedJumps = new Seq(); + public boolean updateJumpHeights = true; { setTransform(true); @@ -257,7 +259,10 @@ public class LCanvas extends Table{ } } - if(dynamicJumpHeights) setJumpHeights(); + if(dynamicJumpHeights){ + if(updateJumpHeights) setJumpHeights(); + updateJumpHeights = false; + } if(parent != null) parent.invalidateHierarchy();//don't invalid self @@ -394,6 +399,7 @@ public class LCanvas extends Table{ dragging = null; } + updateJumpHeights = true; layout(); } } @@ -430,6 +436,7 @@ public class LCanvas extends Table{ t.button(Icon.cancel, Styles.logici, () -> { remove(); dragging = null; + statements.updateJumpHeights = true; statements.layout(); }).size(24f); @@ -449,6 +456,7 @@ public class LCanvas extends Table{ lasty = v.y; dragging = StatementElem.this; toFront(); + statements.updateJumpHeights = true; statements.layout(); return true; } @@ -506,6 +514,7 @@ public class LCanvas extends Table{ statements.layout(); copy.elem = s; copy.setupUI(); + statements.updateJumpHeights = true; } } @@ -549,6 +558,7 @@ public class LCanvas extends Table{ setter.get(null); mx = x; my = y; + canvas.statements.updateJumpHeights = true; return true; } @@ -569,6 +579,7 @@ public class LCanvas extends Table{ setter.get(null); } selecting = false; + canvas.statements.updateJumpHeights = true; } }); diff --git a/core/src/mindustry/logic/LogicDialog.java b/core/src/mindustry/logic/LogicDialog.java index eaac0dd322..65ce456c0f 100644 --- a/core/src/mindustry/logic/LogicDialog.java +++ b/core/src/mindustry/logic/LogicDialog.java @@ -256,6 +256,8 @@ public class LogicDialog extends BaseDialog{ dialog.addCloseButton(); dialog.show(); }).disabled(t -> canvas.statements.getChildren().size >= LExecutor.maxInstructions); + + Core.app.post(canvas::rebuild); } public void show(String code, LExecutor executor, boolean privileged, Cons modified){ From 89b67fc4b46ab86bbc5b8e93de3c2be75b2c889b Mon Sep 17 00:00:00 2001 From: code-explorer786 <68312688+code-explorer786@users.noreply.github.com> Date: Fri, 28 Jun 2024 10:34:27 +0700 Subject: [PATCH 5/9] Transition jump heights It looks good. --- core/src/mindustry/logic/LCanvas.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/core/src/mindustry/logic/LCanvas.java b/core/src/mindustry/logic/LCanvas.java index 0fe774b228..baba9bc617 100644 --- a/core/src/mindustry/logic/LCanvas.java +++ b/core/src/mindustry/logic/LCanvas.java @@ -615,6 +615,8 @@ public class LCanvas extends Table{ public boolean markedDone = false; public int jumpUIBegin = 0, jumpUIEnd = 0; + private float uiHeight = 100f; + public JumpCurve(JumpButton button){ this.button = button; } @@ -672,7 +674,8 @@ public class LCanvas extends Table{ Lines.stroke(4f, button.color); Draw.alpha(parentAlpha); - float dist = 100f + 100f * (float) predHeight; + // exponential smoothing + uiHeight = Mathf.lerp(100f + 100f * (float) predHeight, uiHeight, dynamicJumpHeights ? Mathf.pow(0.9f, Time.delta) : 0); //square jumps if(false){ @@ -691,8 +694,8 @@ public class LCanvas extends Table{ Lines.curve( x, y, - x + dist, y, - x2 + dist, y2, + x + uiHeight, y, + x2 + uiHeight, y2, x2, y2, Math.max(18, (int)(Mathf.dst(x, y, x2, y2) / 6))); } From 939f6939d0481ff3e2fdb1172b833dd10c8058d9 Mon Sep 17 00:00:00 2001 From: code-explorer786 <68312688+code-explorer786@users.noreply.github.com> Date: Fri, 28 Jun 2024 20:42:46 +0700 Subject: [PATCH 6/9] Jump curve coloring See #general-programming in the Mindustry Discord server: https://discord.com/channels/391020510269669376/663854113418641429/1256146387834372106 Co-authored-by: Ilya246 --- core/assets/bundles/bundle.properties | 1 + core/src/mindustry/logic/LCanvas.java | 17 ++++++++++++++++- .../ui/dialogs/SettingsMenuDialog.java | 1 + 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index 22a9289eed..ec0655afd5 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -1226,6 +1226,7 @@ setting.playerchat.name = Display Player Bubble Chat setting.showweather.name = Show Weather Graphics setting.hidedisplays.name = Hide Logic Displays setting.dynamicjumpheights.name = Dynamic Jump Heights +setting.coloredjumps.name = Colored Jumps setting.macnotch.name = Adapt interface to display notch setting.macnotch.description = Restart required to apply changes steam.friendsonly = Friends Only diff --git a/core/src/mindustry/logic/LCanvas.java b/core/src/mindustry/logic/LCanvas.java index baba9bc617..e065a343bc 100644 --- a/core/src/mindustry/logic/LCanvas.java +++ b/core/src/mindustry/logic/LCanvas.java @@ -26,6 +26,7 @@ public class LCanvas extends Table{ //ew static variables static LCanvas canvas; private static boolean dynamicJumpHeights = false; + private static boolean coloredJumps = false; public DragLayout statements; public ScrollPane pane; @@ -112,6 +113,7 @@ public class LCanvas extends Table{ jumps = statements.jumps; dynamicJumpHeights = Core.settings.getBool("dynamicjumpheights", false); + coloredJumps = Core.settings.getBool("coloredjumps", false); pane = pane(t -> { t.center(); @@ -204,6 +206,7 @@ public class LCanvas extends Table{ public Group jumps = new WidgetGroup(); private Seq processedJumps = new Seq(); public boolean updateJumpHeights = true; + private Rand rand = new Rand(); { setTransform(true); @@ -264,6 +267,16 @@ public class LCanvas extends Table{ updateJumpHeights = false; } + if(coloredJumps){ + jumps.getChildren().each(e -> { + if(!(e instanceof JumpCurve e2) || e2.button.to.get() == null) return; + int idx = e2.button.to.get().index; + if(dragging != null && idx >= dragging.index) idx += 1; + rand.setSeed((long) idx); + Color.HSVtoRGB(rand.random(0.0f, 360.0f), rand.random(80.0f, 90.0f), 100.0f, e2.button.defaultColor); + }); + } + if(parent != null) parent.invalidateHierarchy();//don't invalid self if(parent != null && parent instanceof Table){ @@ -533,7 +546,7 @@ public class LCanvas extends Table{ public static class JumpButton extends ImageButton{ Color hoverColor = Pal.place; - Color defaultColor = Color.white; + Color defaultColor = new Color(Color.white); Prov to; boolean selecting; float mx, my; @@ -673,6 +686,7 @@ public class LCanvas extends Table{ public void drawCurve(float x, float y, float x2, float y2){ Lines.stroke(4f, button.color); Draw.alpha(parentAlpha); + Draw.color(button.color); // exponential smoothing uiHeight = Mathf.lerp(100f + 100f * (float) predHeight, uiHeight, dynamicJumpHeights ? Mathf.pow(0.9f, Time.delta) : 0); @@ -698,6 +712,7 @@ public class LCanvas extends Table{ x2 + uiHeight, y2, x2, y2, Math.max(18, (int)(Mathf.dst(x, y, x2, y2) / 6))); + Draw.reset(); } public void prepareHeight(){ diff --git a/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java b/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java index 04d2d93e61..78d1a4c241 100644 --- a/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java +++ b/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java @@ -504,6 +504,7 @@ public class SettingsMenuDialog extends BaseDialog{ } graphics.checkPref("dynamicjumpheights", true); + graphics.checkPref("coloredjumps", true); } public void exportData(Fi file) throws IOException{ From c5b8133ff91161706a067105fbf93ffe82d69d3d Mon Sep 17 00:00:00 2001 From: Ilya246 Date: Fri, 28 Jun 2024 19:03:04 +0400 Subject: [PATCH 7/9] optimise jump colors and attempt to space them apart --- core/src/mindustry/logic/LCanvas.java | 40 +++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/core/src/mindustry/logic/LCanvas.java b/core/src/mindustry/logic/LCanvas.java index e065a343bc..c9ce0c558f 100644 --- a/core/src/mindustry/logic/LCanvas.java +++ b/core/src/mindustry/logic/LCanvas.java @@ -206,10 +206,45 @@ public class LCanvas extends Table{ public Group jumps = new WidgetGroup(); private Seq processedJumps = new Seq(); public boolean updateJumpHeights = true; - private Rand rand = new Rand(); + private Color[] jumpColors = new Color[LExecutor.maxInstructions]; { setTransform(true); + + Time.mark(); + + //set seed so that colors are consistent between game launches + Rand rand = new Rand(0L); + + int checkAmount = 5; //to how many previous hues to compare + float minDistance = 360f / checkAmount / 2f; //by at least how much should each hue be different from the checkAmount previous and next ones + + Seq previousHues = new Seq<>(); + + for(int i = 0; i < LExecutor.maxInstructions; i++){ + float hue = 0f; + + boolean goodEnough = false; + while(!goodEnough){ + goodEnough = true; + hue = Mathf.random(360f); + for(float previousHue : previousHues){ + if(Math.abs(previousHue - hue) < minDistance) { + goodEnough = false; + break; + } + } + } + + jumpColors[i] = Color.HSVtoRGB(hue, rand.random(80f, 100f), 100f); + + previousHues.add(hue); + if(previousHues.size == checkAmount){ + previousHues.remove(0); + } + } + + Log.debug("Processed logic jump colors in @ms", Time.elapsed()); //2.2ms on my (Ilya246) machine } @Override @@ -272,8 +307,7 @@ public class LCanvas extends Table{ if(!(e instanceof JumpCurve e2) || e2.button.to.get() == null) return; int idx = e2.button.to.get().index; if(dragging != null && idx >= dragging.index) idx += 1; - rand.setSeed((long) idx); - Color.HSVtoRGB(rand.random(0.0f, 360.0f), rand.random(80.0f, 90.0f), 100.0f, e2.button.defaultColor); + e2.button.defaultColor.set(jumpColors[idx]); }); } From a5de1317117e6e9822ebc1e097faf21794532758 Mon Sep 17 00:00:00 2001 From: code-explorer786 <68312688+code-explorer786@users.noreply.github.com> Date: Sun, 30 Jun 2024 12:00:34 +0700 Subject: [PATCH 8/9] Trapezoidal jumps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I haven't checked this yet, but it seems like it also reduces the complexity (runtime) from ≈O(n^2) to ≈O(k^2 + n) (n = number of jump curves) (k = number of unique destinations) The worst case, that is k = n, is still O(n^2) but generally it's much more (scalably) performant. Thanks Arkanic for suggesting this! https://discord.com/channels/391020510269669376/663854113418641429/1256161815503704104 Co-authored-by: Arkanic <50847107+Arkanic@users.noreply.github.com> --- core/assets/bundles/bundle.properties | 1 + core/src/mindustry/logic/LCanvas.java | 76 +++++++++++++------ .../ui/dialogs/SettingsMenuDialog.java | 1 + 3 files changed, 53 insertions(+), 25 deletions(-) diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index ec0655afd5..30c21c154c 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -1227,6 +1227,7 @@ setting.showweather.name = Show Weather Graphics setting.hidedisplays.name = Hide Logic Displays setting.dynamicjumpheights.name = Dynamic Jump Heights setting.coloredjumps.name = Colored Jumps +setting.trapezoidaljumps.name = Trapezoidal Jumps setting.macnotch.name = Adapt interface to display notch setting.macnotch.description = Restart required to apply changes steam.friendsonly = Friends Only diff --git a/core/src/mindustry/logic/LCanvas.java b/core/src/mindustry/logic/LCanvas.java index c9ce0c558f..dcfbff1b28 100644 --- a/core/src/mindustry/logic/LCanvas.java +++ b/core/src/mindustry/logic/LCanvas.java @@ -21,12 +21,12 @@ import mindustry.logic.LStatements.*; import mindustry.ui.*; public class LCanvas extends Table{ - public static final int maxJumpsDrawn = 100; private static final int invalidJump = Integer.MAX_VALUE; // terrible hack //ew static variables static LCanvas canvas; private static boolean dynamicJumpHeights = false; private static boolean coloredJumps = false; + private static boolean trapezoidalJumps = false; public DragLayout statements; public ScrollPane pane; @@ -35,7 +35,6 @@ public class LCanvas extends Table{ StatementElem dragging; StatementElem hovered; float targetWidth; - int jumpCount = 0; boolean privileged; Seq tooltips = new Seq<>(); @@ -114,6 +113,7 @@ public class LCanvas extends Table{ dynamicJumpHeights = Core.settings.getBool("dynamicjumpheights", false); coloredJumps = Core.settings.getBool("coloredjumps", false); + trapezoidalJumps = Core.settings.getBool("trapezoidaljumps", false); pane = pane(t -> { t.center(); @@ -132,7 +132,6 @@ public class LCanvas extends Table{ @Override public void draw(){ - jumpCount = 0; super.draw(); } @@ -205,6 +204,8 @@ public class LCanvas extends Table{ boolean invalidated; public Group jumps = new WidgetGroup(); private Seq processedJumps = new Seq(); + private IntMap reprBefore = new IntMap(); + private IntMap reprAfter = new IntMap(); public boolean updateJumpHeights = true; private Color[] jumpColors = new Color[LExecutor.maxInstructions]; @@ -321,14 +322,32 @@ public class LCanvas extends Table{ private void setJumpHeights(){ SnapshotSeq jumpsChildren = jumps.getChildren(); processedJumps.clear(); - jumpsChildren.each(e -> { - if(e instanceof JumpCurve e2){ + if(trapezoidalJumps){ + reprBefore.clear(); + reprAfter.clear(); + jumpsChildren.each(e -> { + if(!(e instanceof JumpCurve e2)) return; e2.prepareHeight(); - if(e2.jumpUIBegin != invalidJump){ - processedJumps.add(e2); + if(e2.jumpUIBegin == invalidJump) return; + if(e2.flipped){ + JumpCurve prev = reprAfter.get(e2.jumpUIBegin); + if(prev != null && prev.jumpUIEnd >= e2.jumpUIEnd) return; + reprAfter.put(e2.jumpUIBegin, e2); + }else{ + JumpCurve prev = reprBefore.get(e2.jumpUIEnd); + if(prev != null && prev.jumpUIBegin <= e2.jumpUIBegin) return; + reprBefore.put(e2.jumpUIEnd, e2); } - } - }); + }); + processedJumps.add(reprBefore.values().toArray()); + processedJumps.add(reprAfter.values().toArray()); + } else { + jumpsChildren.each(e -> { + if(!(e instanceof JumpCurve e2)) return; + e2.prepareHeight(); + processedJumps.add(e2); + }); + } processedJumps.sort((a,b) -> a.jumpUIBegin - b.jumpUIBegin); Seq occupiers = Pools.obtain(Seq.class, Seq::new); @@ -351,6 +370,15 @@ public class LCanvas extends Table{ occupiers.clear(); Pools.free(occupied); Pools.free(occupiers); + + if(trapezoidalJumps){ + jumpsChildren.each(e -> { + if(!(e instanceof JumpCurve e2)) return; + if(e2.jumpUIBegin == invalidJump) return; + e2.predHeight = e2.flipped ? reprAfter.get(e2.jumpUIBegin).predHeight : reprBefore.get(e2.jumpUIEnd).predHeight; + e2.markedDone = true; + }); + } } private int getJumpHeight(int index, Seq occupiers, Bits occupied){ @@ -661,8 +689,9 @@ public class LCanvas extends Table{ public int predHeight = 0; public boolean markedDone = false; public int jumpUIBegin = 0, jumpUIEnd = 0; + public boolean flipped = false; - private float uiHeight = 100f; + private float uiHeight = 60f; public JumpCurve(JumpButton button){ this.button = button; @@ -679,12 +708,6 @@ public class LCanvas extends Table{ @Override public void draw(){ - canvas.jumpCount ++; - - if(canvas.jumpCount > maxJumpsDrawn && !button.selecting && !button.listener.isOver()){ - return; - } - Element hover = button.to.get() == null && button.selecting ? canvas.hovered : button.to.get(); boolean draw = false; Vec2 t = Tmp.v1, r = Tmp.v2; @@ -723,18 +746,19 @@ public class LCanvas extends Table{ Draw.color(button.color); // exponential smoothing - uiHeight = Mathf.lerp(100f + 100f * (float) predHeight, uiHeight, dynamicJumpHeights ? Mathf.pow(0.9f, Time.delta) : 0); - - //square jumps - if(false){ - float len = Scl.scl(Mathf.randomSeed(hashCode(), 10, 50)); - - float maxX = Math.max(x, x2) + len; + uiHeight = Mathf.lerp( + trapezoidalJumps ? 60f + 20f * (float) predHeight : 100f + 100f * (float) predHeight, + uiHeight, + dynamicJumpHeights ? Mathf.pow(0.9f, Time.delta) : 0 + ); + //trapezoidal jumps + if(trapezoidalJumps){ + float dy = (y2 == y ? 0f : y2 > y ? 1f : -1f) * uiHeight * 0.5f; Lines.beginLine(); Lines.linePoint(x, y); - Lines.linePoint(maxX, y); - Lines.linePoint(maxX, y2); + Lines.linePoint(x + uiHeight, y + dy); + Lines.linePoint(x + uiHeight, y2 - dy); Lines.linePoint(x2, y2); Lines.endLine(); return; @@ -753,11 +777,13 @@ public class LCanvas extends Table{ if(this.button.to.get() == null){ this.markedDone = true; this.predHeight = 0; + this.flipped = false; this.jumpUIBegin = this.jumpUIEnd = invalidJump; } else { this.markedDone = false; int i = this.button.elem.index; int j = this.button.to.get().index; + this.flipped = i >= j; this.jumpUIBegin = Math.min(i,j); this.jumpUIEnd = Math.max(i,j); // height will be recalculated later diff --git a/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java b/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java index 78d1a4c241..4390316bc6 100644 --- a/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java +++ b/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java @@ -505,6 +505,7 @@ public class SettingsMenuDialog extends BaseDialog{ graphics.checkPref("dynamicjumpheights", true); graphics.checkPref("coloredjumps", true); + graphics.checkPref("trapezoidaljumps", true); } public void exportData(Fi file) throws IOException{ From 116798e3284b18f88c1adeb5ebf9d9fec000a4e8 Mon Sep 17 00:00:00 2001 From: way-zer Date: Tue, 6 Aug 2024 01:30:38 +0800 Subject: [PATCH 9/9] OC: fix slow of LCanvas --- core/src/mindustry/logic/LCanvas.java | 91 +++++++++++++++------------ 1 file changed, 50 insertions(+), 41 deletions(-) diff --git a/core/src/mindustry/logic/LCanvas.java b/core/src/mindustry/logic/LCanvas.java index dcfbff1b28..db9cf7c231 100644 --- a/core/src/mindustry/logic/LCanvas.java +++ b/core/src/mindustry/logic/LCanvas.java @@ -120,10 +120,12 @@ public class LCanvas extends Table{ t.add(statements).pad(2f).center().width(targetWidth); t.addChild(statements.jumps); + jumps.touchable = Touchable.disabled; + jumps.update(()->jumps.setCullingArea(t.getCullingArea())); statements.jumps.cullable = false; }).grow().get(); pane.setFlickScroll(false); - pane.setScrollYForce(s); + pane.setScrollY(s); if(toLoad != null){ load(toLoad); @@ -161,13 +163,11 @@ public class LCanvas extends Table{ } this.statements.updateJumpHeights = true; - this.statements.layout(); } public void clearStatements(){ statements.jumps.clear(); statements.clearChildren(); - statements.layout(); } StatementElem checkHovered(){ @@ -254,10 +254,12 @@ public class LCanvas extends Table{ float cy = 0; seq.clear(); - float totalHeight = getChildren().sumf(e -> e.getHeight() + space); - - height = prefHeight = totalHeight; - width = prefWidth = Scl.scl(targetWidth); + float totalHeight = getChildren().sumf(e -> e.getPrefHeight() + space); + if(height != totalHeight || width != Scl.scl(targetWidth)){ + height = prefHeight = totalHeight; + width = prefWidth = Scl.scl(targetWidth); + invalidateHierarchy(); + } //layout everything normally for(int i = 0; i < getChildren().size; i++){ @@ -267,7 +269,7 @@ public class LCanvas extends Table{ if(dragging == e) continue; e.setSize(width, e.getPrefHeight()); - e.setPosition(0, height - cy, Align.topLeft); + e.setPosition(0, totalHeight - cy, Align.topLeft); ((StatementElem)e).updateAddress(i); cy += e.getPrefHeight() + space; @@ -312,8 +314,6 @@ public class LCanvas extends Table{ }); } - if(parent != null) parent.invalidateHierarchy();//don't invalid self - if(parent != null && parent instanceof Table){ setCullingArea(parent.getCullingArea()); } @@ -472,10 +472,10 @@ public class LCanvas extends Table{ } dragging = null; + invalidateHierarchy(); } updateJumpHeights = true; - layout(); } } @@ -512,7 +512,6 @@ public class LCanvas extends Table{ remove(); dragging = null; statements.updateJumpHeights = true; - statements.layout(); }).size(24f); t.addListener(new InputListener(){ @@ -532,7 +531,7 @@ public class LCanvas extends Table{ dragging = StatementElem.this; toFront(); statements.updateJumpHeights = true; - statements.layout(); + statements.invalidate(); return true; } @@ -544,7 +543,7 @@ public class LCanvas extends Table{ lastx = v.x; lasty = v.y; - statements.layout(); + statements.invalidate(); } @Override @@ -586,7 +585,6 @@ public class LCanvas extends Table{ StatementElem s = new StatementElem(copy); statements.addChildAfter(StatementElem.this, s); - statements.layout(); copy.elem = s; copy.setupUI(); statements.updateJumpHeights = true; @@ -684,6 +682,7 @@ public class LCanvas extends Table{ public static class JumpCurve extends Element{ public JumpButton button; + private boolean invertedHeight; // for jump prediction; see DragLayout public int predHeight = 0; @@ -697,10 +696,37 @@ public class LCanvas extends Table{ this.button = button; } + @Override + public void setSize(float width, float height){ + if(height < 0){ + y += height; + height = -height; + invertedHeight = true; + } + super.setSize(width, height); + } + @Override public void act(float delta){ super.act(delta); + //MDTX(WayZer, 2024/8/6) Support Cull + invertedHeight = false; + Group desc = canvas.jumps.parent; + Vec2 t = Tmp.v1.set(button.getWidth() / 2f, button.getHeight() / 2f); + button.localToAscendantCoordinates(desc, t); + setPosition(t.x, t.y); + Element hover = button.to.get() == null && button.selecting ? canvas.hovered : button.to.get(); + if(hover != null){ + t.set(hover.getWidth(), hover.getHeight() / 2f); + hover.localToAscendantCoordinates(desc, t); + setSize(t.x - x, t.y - y); + }else if(button.selecting){ + setSize(button.mx, button.my); + }else{ + setSize(0, 0); + } + if(button.listener.isOver()){ toFront(); } @@ -708,36 +734,19 @@ public class LCanvas extends Table{ @Override public void draw(){ - Element hover = button.to.get() == null && button.selecting ? canvas.hovered : button.to.get(); - boolean draw = false; - Vec2 t = Tmp.v1, r = Tmp.v2; + if(height == 0) return; + Vec2 t = Tmp.v1.set(width, !invertedHeight ? height : 0), r = Tmp.v2.set(0, !invertedHeight ? 0 : height); Group desc = canvas.pane; + localToAscendantCoordinates(desc, r); + localToAscendantCoordinates(desc, t); - button.localToAscendantCoordinates(desc, r.set(0, 0)); + drawCurve(r.x, r.y, t.x, t.y); - if(hover != null){ - hover.localToAscendantCoordinates(desc, t.set(hover.getWidth(), hover.getHeight()/2f)); - - draw = true; - }else if(button.selecting){ - t.set(r).add(button.mx, button.my); - draw = true; - } - - float offset = canvas.pane.getVisualScrollY() - canvas.pane.getMaxY(); - - t.y += offset; - r.y += offset; - - if(draw){ - drawCurve(r.x + button.getWidth()/2f, r.y + button.getHeight()/2f, t.x, t.y); - - float s = button.getWidth(); - Draw.color(button.color); - Tex.logicNode.draw(t.x + s*0.75f, t.y - s/2f, -s, s); - Draw.reset(); - } + float s = button.getWidth(); + Draw.color(button.color); + Tex.logicNode.draw(t.x + s * 0.75f, t.y - s / 2f, -s, s); + Draw.reset(); } public void drawCurve(float x, float y, float x2, float y2){