Implement Live State Editing

This commit is contained in:
2025-08-31 19:24:28 +02:00
parent 847d61ecca
commit e0275b12af
4 changed files with 155 additions and 2 deletions

View File

@ -18,6 +18,7 @@
<button disabled id="model_name"></button>
<button id="select_state_button">Select State</button>
<button disabled id="state_name"></button>
<button id="edit_state_button">Edit State</button>
<button id="up_button">UP</button>
<button id="down_button">DOWN</button>
<button id="left_button">LEFT</button>

View File

@ -101,6 +101,34 @@ const clear_visualization = () => {
canvas.height = vh - 43;
context.clearRect(0, 0, canvas.width, canvas.height);
draw_grid();
};
const draw_grid = () => {
const canvas = document.getElementById("model_canvas");
const context = canvas.getContext("2d");
const [vw, vh] = get_viewport_dims();
canvas.width = vw / 2 - 9.5;
canvas.height = vh - 43;
const square_width = canvas.width / current_state.width;
const square_height = canvas.height / current_state.height;
// Horizontal lines
for (let y = 1; y < current_state.height; y += 1) {
context.moveTo(0, y * square_height);
context.lineTo(canvas.width, y * square_height);
context.stroke();
}
// Vertical lines
for (let x = 1; x < current_state.width; x += 1) {
context.moveTo(x * square_width, 0);
context.lineTo(x * square_width, canvas.height);
context.stroke();
}
};
//
@ -138,6 +166,76 @@ const node_click_view_state = (node, graph) => {
// Set up model event-handlers
//
let editing = false;
let startpos = null;
document
.getElementById("model_canvas")
.addEventListener("mousedown", (event) => {
if (model.select(current_state, event.offsetX, event.offsetY) === null) {
startpos = [event.offsetX, event.offsetY];
}
});
document.getElementById("model_canvas").addEventListener("mouseup", (event) => {
if (startpos === null) {
return;
}
let endpos = [event.offsetX, event.offsetY];
const canvas = document.getElementById("model_canvas");
const square_width = canvas.width / current_state.width;
const square_height = canvas.height / current_state.height;
// Coordinates
const startx = Math.floor(startpos[0] / square_width);
const starty = Math.floor(startpos[1] / square_height);
const endx = Math.floor(endpos[0] / square_width);
const endy = Math.floor(endpos[1] / square_height);
// Check that the block to be added doesn't collide with anything
for (let ix = startx; ix <= endx; ix++) {
for (let iy = starty; iy <= endy; iy++) {
if (
model.select(current_state, ix * square_width, iy * square_height) !==
null
) {
startpos = null;
return;
}
}
}
// Add block
let new_block = model.add_block(
current_state,
Math.min(startx, endx),
Math.min(starty, endy),
Math.max(startx, endx),
Math.max(starty, endy),
);
if (new_block === null) {
startpos = null;
return;
}
// Clear graph + visualize
selected_element = new_block;
clear_visualization();
model.visualize(current_state);
model.highlight(current_state, selected_element);
// Generate graph for new state
clear_graph();
model.generate(states, initial_state, graph, null);
reset_graph_view(graph);
startpos = null;
});
document.getElementById("model_canvas").addEventListener("click", (event) => {
const element = model.select(current_state, event.offsetX, event.offsetY);
@ -218,6 +316,7 @@ document.getElementById("select_model_button").addEventListener("click", () => {
clear_graph();
clear_visualization();
model.visualize(initial_state);
editing = false;
document.getElementById("model_name").innerHTML = model.name;
document.getElementById("state_name").innerHTML = initial_state.name;
});
@ -232,12 +331,29 @@ document.getElementById("select_state_button").addEventListener("click", () => {
states.add(initial_state);
clear_graph();
clear_visualization();
// graph = null;
// graph = generate_graph(data, node_click_view_state);
model.visualize(initial_state);
editing = false;
document.getElementById("state_name").innerHTML = initial_state.name;
});
document.getElementById("edit_state_button").addEventListener("click", () => {
if (editing === false) {
current_initial_state = 0;
initial_state = model.empty_state;
current_state = initial_state;
selected_element = null;
clear_states();
states.add(initial_state);
clear_graph();
clear_visualization();
model.visualize(initial_state);
editing = true;
document.getElementById("state_name").innerHTML = initial_state.name;
} else {
editing = false;
}
});
document
.getElementById("generate_graph_button")
.addEventListener("click", () => {

View File

@ -183,6 +183,22 @@ const visualize_path = (from_state, to_state) => {
// For each state in path: visualize(state)
};
const add_block = (state, x0, y0, x1, y1) => {
let dir;
if (x1 - x0 > y1 - y0) {
dir = HORIZONTAL;
} else if (x1 - x0 < y1 - y0) {
dir = VERTICAL;
} else {
// No square blocks for this model
return null;
}
let new_block = [state.board.length, dir, x0, y0, x1, y1];
state.board.push(new_block);
return new_block;
};
export const restricted_sliding_blocks_model = {
name: "Restricted Sliding Blocks",
generate,
@ -192,4 +208,11 @@ export const restricted_sliding_blocks_model = {
initial_states,
select,
move,
empty_state: {
name: "Custom",
width: 5,
height: 5,
board: [],
},
add_block,
};

View File

@ -162,6 +162,12 @@ const visualize_path = (from_state, to_state) => {
// For each state in path: visualize(state)
};
const add_block = (state, x0, y0, x1, y1) => {
let new_block = [state.board.length, x0, y0, x1, y1];
state.board.push(new_block);
return new_block;
};
export const sliding_blocks_model = {
name: "Sliding Blocks",
generate,
@ -171,4 +177,11 @@ export const sliding_blocks_model = {
initial_states,
select,
move,
empty_state: {
name: "Custom",
width: 5,
height: 6,
board: [],
},
add_block,
};