Logic jump PR cleanup

This commit is contained in:
Anuken
2025-02-10 15:51:02 -05:00
4 changed files with 205 additions and 85 deletions

View File

@@ -20,18 +20,22 @@ import mindustry.logic.LStatements.*;
import mindustry.ui.*; import mindustry.ui.*;
public class LCanvas extends Table{ public class LCanvas extends Table{
public static final int maxJumpsDrawn = 100; private static final Seq<JumpCurve> tmpOccupiers1 = new Seq<>();
private static final Seq<JumpCurve> tmpOccupiers2 = new Seq<>();
private static final Bits tmpBits1 = new Bits();
private static final Bits tmpBits2 = new Bits();
private static final int invalidJump = Integer.MAX_VALUE; // terrible hack
//ew static variables //ew static variables
static LCanvas canvas; static LCanvas canvas;
private static final boolean dynamicJumpHeights = true;
public DragLayout statements; public DragLayout statements;
public ScrollPane pane; public ScrollPane pane;
public Group jumps; public Group jumps; // compatibility
StatementElem dragging; StatementElem dragging;
StatementElem hovered; StatementElem hovered;
float targetWidth; float targetWidth;
int jumpCount = 0;
boolean privileged; boolean privileged;
Seq<Tooltip> tooltips = new Seq<>(); Seq<Tooltip> tooltips = new Seq<>();
@@ -106,14 +110,16 @@ public class LCanvas extends Table{
clear(); clear();
statements = new DragLayout(); statements = new DragLayout();
jumps = new WidgetGroup(); jumps = statements.jumps;
pane = pane(t -> { pane = pane(t -> {
t.center(); t.center();
t.add(statements).pad(2f).center().width(targetWidth); t.add(statements).pad(2f).center().width(targetWidth);
t.addChild(jumps); t.addChild(statements.jumps);
jumps.cullable = false; jumps.touchable = Touchable.disabled;
jumps.update(()->jumps.setCullingArea(t.getCullingArea()));
statements.jumps.cullable = false;
}).grow().get(); }).grow().get();
pane.setFlickScroll(false); pane.setFlickScroll(false);
pane.setScrollYForce(s); pane.setScrollYForce(s);
@@ -123,12 +129,6 @@ public class LCanvas extends Table{
} }
} }
@Override
public void draw(){
jumpCount = 0;
super.draw();
}
public void add(LStatement statement){ public void add(LStatement statement){
statements.addChild(new StatementElem(statement)); statements.addChild(new StatementElem(statement));
} }
@@ -141,7 +141,7 @@ public class LCanvas extends Table{
} }
public void load(String asm){ public void load(String asm){
jumps.clear(); statements.jumps.clear();
Seq<LStatement> statements = LAssembler.read(asm, privileged); Seq<LStatement> statements = LAssembler.read(asm, privileged);
statements.truncate(LExecutor.maxInstructions); statements.truncate(LExecutor.maxInstructions);
@@ -154,13 +154,12 @@ public class LCanvas extends Table{
st.setupUI(); st.setupUI();
} }
this.statements.layout(); this.statements.updateJumpHeights = true;
} }
public void clearStatements(){ public void clearStatements(){
jumps.clear(); statements.jumps.clear();
statements.clearChildren(); statements.clearChildren();
statements.layout();
} }
StatementElem checkHovered(){ StatementElem checkHovered(){
@@ -195,6 +194,11 @@ public class LCanvas extends Table{
Seq<Element> seq = new Seq<>(); Seq<Element> seq = new Seq<>();
int insertPosition = 0; int insertPosition = 0;
boolean invalidated; boolean invalidated;
public Group jumps = new WidgetGroup();
private Seq<JumpCurve> processedJumps = new Seq<>();
private IntMap<JumpCurve> reprBefore = new IntMap<>();
private IntMap<JumpCurve> reprAfter = new IntMap<>();
public boolean updateJumpHeights = true;
{ {
setTransform(true); setTransform(true);
@@ -206,10 +210,12 @@ public class LCanvas extends Table{
float cy = 0; float cy = 0;
seq.clear(); seq.clear();
float totalHeight = getChildren().sumf(e -> e.getHeight() + space); float totalHeight = getChildren().sumf(e -> e.getPrefHeight() + space);
if(height != totalHeight || width != Scl.scl(targetWidth)){
height = prefHeight = totalHeight; height = prefHeight = totalHeight;
width = prefWidth = Scl.scl(targetWidth); width = prefWidth = Scl.scl(targetWidth);
invalidateHierarchy();
}
//layout everything normally //layout everything normally
for(int i = 0; i < getChildren().size; i++){ for(int i = 0; i < getChildren().size; i++){
@@ -219,7 +225,7 @@ public class LCanvas extends Table{
if(dragging == e) continue; if(dragging == e) continue;
e.setSize(width, e.getPrefHeight()); e.setSize(width, e.getPrefHeight());
e.setPosition(0, height - cy, Align.topLeft); e.setPosition(0, totalHeight - cy, Align.topLeft);
((StatementElem)e).updateAddress(i); ((StatementElem)e).updateAddress(i);
cy += e.getPrefHeight() + space; cy += e.getPrefHeight() + space;
@@ -250,13 +256,97 @@ public class LCanvas extends Table{
} }
} }
if(parent != null) parent.invalidateHierarchy(); if(dynamicJumpHeights){
if(updateJumpHeights) setJumpHeights();
updateJumpHeights = false;
}
if(parent != null && parent instanceof Table){ if(parent != null && parent instanceof Table){
setCullingArea(parent.getCullingArea()); setCullingArea(parent.getCullingArea());
} }
} }
private void setJumpHeights(){
SnapshotSeq<Element> jumpsChildren = jumps.getChildren();
processedJumps.clear();
reprBefore.clear();
reprAfter.clear();
jumpsChildren.each(e -> {
if(!(e instanceof JumpCurve e2)) return;
e2.prepareHeight();
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());
processedJumps.sort((a, b) -> a.jumpUIBegin - b.jumpUIBegin);
Seq<JumpCurve> occupiers = tmpOccupiers1;
Bits occupied = tmpBits1;
occupiers.clear();
occupied.clear();
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);
}
occupiers.clear();
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<JumpCurve> occupiers, Bits occupied){
JumpCurve jmp = processedJumps.get(index);
if(jmp.markedDone) return jmp.predHeight;
Seq<JumpCurve> tmpOccupiers = tmpOccupiers2;
Bits tmpOccupied = tmpBits2;
tmpOccupiers.set(occupiers);
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;
tmpOccupiers.clear();
return jmp.predHeight;
}
@Override @Override
public float getPrefWidth(){ public float getPrefWidth(){
return prefWidth; return prefWidth;
@@ -312,9 +402,10 @@ public class LCanvas extends Table{
} }
dragging = null; dragging = null;
invalidateHierarchy();
} }
layout(); updateJumpHeights = true;
} }
} }
@@ -350,7 +441,7 @@ public class LCanvas extends Table{
t.button(Icon.cancel, Styles.logici, () -> { t.button(Icon.cancel, Styles.logici, () -> {
remove(); remove();
dragging = null; dragging = null;
statements.layout(); statements.updateJumpHeights = true;
}).size(24f); }).size(24f);
t.addListener(new InputListener(){ t.addListener(new InputListener(){
@@ -369,7 +460,8 @@ public class LCanvas extends Table{
lasty = v.y; lasty = v.y;
dragging = StatementElem.this; dragging = StatementElem.this;
toFront(); toFront();
statements.layout(); statements.updateJumpHeights = true;
statements.invalidate();
return true; return true;
} }
@@ -381,7 +473,7 @@ public class LCanvas extends Table{
lastx = v.x; lastx = v.x;
lasty = v.y; lasty = v.y;
statements.layout(); statements.invalidate();
} }
@Override @Override
@@ -423,9 +515,9 @@ public class LCanvas extends Table{
StatementElem s = new StatementElem(copy); StatementElem s = new StatementElem(copy);
statements.addChildAfter(StatementElem.this, s); statements.addChildAfter(StatementElem.this, s);
statements.layout();
copy.elem = s; copy.elem = s;
copy.setupUI(); copy.setupUI();
statements.updateJumpHeights = true;
} }
} }
@@ -444,19 +536,20 @@ public class LCanvas extends Table{
public static class JumpButton extends ImageButton{ public static class JumpButton extends ImageButton{
Color hoverColor = Pal.place; Color hoverColor = Pal.place;
Color defaultColor = Color.white;
Prov<StatementElem> to; Prov<StatementElem> to;
boolean selecting; boolean selecting;
float mx, my; float mx, my;
ClickListener listener; ClickListener listener;
public JumpCurve curve; public JumpCurve curve;
public StatementElem elem;
public JumpButton(Prov<StatementElem> getter, Cons<StatementElem> setter){ public JumpButton(Prov<StatementElem> getter, Cons<StatementElem> setter, StatementElem elem){
super(Tex.logicNode, new ImageButtonStyle(){{ super(Tex.logicNode, new ImageButtonStyle(){{
imageUpColor = Color.white; imageUpColor = Color.white;
}}); }});
this.elem = elem;
to = getter; to = getter;
addListener(listener = new ClickListener()); addListener(listener = new ClickListener());
@@ -467,6 +560,7 @@ public class LCanvas extends Table{
setter.get(null); setter.get(null);
mx = x; mx = x;
my = y; my = y;
canvas.statements.updateJumpHeights = true;
return true; return true;
} }
@@ -487,6 +581,7 @@ public class LCanvas extends Table{
setter.get(null); setter.get(null);
} }
selecting = false; selecting = false;
canvas.statements.updateJumpHeights = true;
} }
}); });
@@ -495,7 +590,7 @@ public class LCanvas extends Table{
setter.get(null); setter.get(null);
} }
setColor(listener.isOver() ? hoverColor : defaultColor); setColor(listener.isOver() ? hoverColor : Color.white);
getStyle().imageUpColor = this.color; getStyle().imageUpColor = this.color;
}); });
@@ -516,15 +611,51 @@ public class LCanvas extends Table{
public static class JumpCurve extends Element{ public static class JumpCurve extends Element{
public JumpButton button; public JumpButton button;
private boolean invertedHeight;
// for jump prediction; see DragLayout
public int predHeight = 0;
public boolean markedDone = false;
public int jumpUIBegin = 0, jumpUIEnd = 0;
public boolean flipped = false;
private float uiHeight = 60f;
public JumpCurve(JumpButton button){ public JumpCurve(JumpButton button){
this.button = button; 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 @Override
public void act(float delta){ public void act(float delta){
super.act(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()){ if(button.listener.isOver()){
toFront(); toFront();
} }
@@ -532,71 +663,58 @@ public class LCanvas extends Table{
@Override @Override
public void draw(){ public void draw(){
canvas.jumpCount ++; if(height == 0) return;
Vec2 t = Tmp.v1.set(width, !invertedHeight ? height : 0), r = Tmp.v2.set(0, !invertedHeight ? 0 : height);
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;
Group desc = canvas.pane; 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){ float s = button.getWidth();
hover.localToAscendantCoordinates(desc, t.set(hover.getWidth(), hover.getHeight()/2f)); Draw.color(button.color);
Tex.logicNode.draw(t.x + s * 0.75f, t.y - s / 2f, -s, s);
draw = true; Draw.reset();
}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();
}
} }
public void drawCurve(float x, float y, float x2, float y2){ public void drawCurve(float x, float y, float x2, float y2){
Lines.stroke(4f, button.color); Lines.stroke(Scl.scl(4f), button.color);
Draw.alpha(parentAlpha); Draw.alpha(parentAlpha);
Draw.color(button.color);
float dist = 100f; // exponential smoothing
uiHeight = Mathf.lerp(
60f + 20f * (float) predHeight,
uiHeight,
dynamicJumpHeights ? Mathf.pow(0.9f, Time.delta) : 0
);
//square jumps //trapezoidal jumps
if(false){ float dy = (y2 == y ? 0f : y2 > y ? 1f : -1f) * uiHeight * 0.5f;
float len = Scl.scl(Mathf.randomSeed(hashCode(), 10, 50)); Lines.beginLine();
Lines.linePoint(x, y);
Lines.linePoint(x + uiHeight, y + dy);
Lines.linePoint(x + uiHeight, y2 - dy);
Lines.linePoint(x2, y2);
Lines.endLine();
}
float maxX = Math.max(x, x2) + len; public void prepareHeight(){
if(this.button.to.get() == null){
Lines.beginLine(); this.markedDone = true;
Lines.linePoint(x, y); this.predHeight = 0;
Lines.linePoint(maxX, y); this.flipped = false;
Lines.linePoint(maxX, y2); this.jumpUIBegin = this.jumpUIEnd = invalidJump;
Lines.linePoint(x2, y2); } else {
Lines.endLine(); this.markedDone = false;
return; 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
} }
Lines.curve(
x, y,
x + dist, y,
x2 + dist, y2,
x2, y2,
Math.max(18, (int)(Mathf.dst(x, y, x2, y2) / 6)));
} }
} }
} }

View File

@@ -934,7 +934,7 @@ public class LStatements{
table.table(this::rebuild); table.table(this::rebuild);
table.add().growX(); 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(); String name = name();

View File

@@ -225,6 +225,8 @@ public class LogicDialog extends BaseDialog{
buttons.button("@add", Icon.add, () -> { buttons.button("@add", Icon.add, () -> {
showAddDialog(); showAddDialog();
}).disabled(t -> canvas.statements.getChildren().size >= LExecutor.maxInstructions); }).disabled(t -> canvas.statements.getChildren().size >= LExecutor.maxInstructions);
Core.app.post(canvas::rebuild);
} }
public void showAddDialog(){ public void showAddDialog(){

View File

@@ -26,4 +26,4 @@ org.gradle.caching=true
org.gradle.internal.http.socketTimeout=100000 org.gradle.internal.http.socketTimeout=100000
org.gradle.internal.http.connectionTimeout=100000 org.gradle.internal.http.connectionTimeout=100000
android.enableR8.fullMode=false android.enableR8.fullMode=false
archash=c8004178c4 archash=9bb61f8dee