Skip to content

Commit 697fbbc

Browse files
committed
Finish saving and loading of Splans
1 parent 196159c commit 697fbbc

1 file changed

Lines changed: 48 additions & 62 deletions

File tree

src/pages/index.tsx

Lines changed: 48 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -59,17 +59,21 @@ class Point {
5959
this.x = x;
6060
this.y = y;
6161
}
62+
63+
toJSON() { // Save point as [x, y] instead of {x: x, y: y}
64+
return [this.x, this.y];
65+
}
6266
}
6367

6468
class Run {
6569
name: string;
6670
points: Point[];
6771
actions: Action[];
6872

69-
constructor(name: string, points: Point[], actions: Action[] = []) {
73+
constructor(name: string, points: [number, number][], actions: { point: [number, number], function: string, args: any[] }[] = []) {
7074
this.name = name;
71-
this.points = points;
72-
this.actions = actions;
75+
this.points = points.map((point) => new Point(point[0], point[1]));
76+
this.actions = actions.map((action) => new Action(new Point(action.point[0], action.point[1]), action.function, action.args));
7377
}
7478
}
7579

@@ -83,28 +87,10 @@ class SplanContent {
8387
this.drive_base = new DriveBase(data.drive_base.left_motor, data.drive_base.right_motor, data.drive_base.wheel_diameter, data.drive_base.axle_track);
8488
this.runs = data.runs.map((run: any) => new Run(run.name, run.points, run.actions));
8589
}
86-
87-
save_file() {
88-
const data = JSON.stringify(this);
89-
const blob = new Blob([data], { type: "application/json" });
90-
const url = URL.createObjectURL(blob);
91-
const link = document.createElement("a");
92-
link.href = url;
93-
link.download = "py_splan.json";
94-
link.click();
95-
URL.revokeObjectURL(url);
96-
}
97-
98-
async generate_code() {
99-
const github_url = "https://raw.githubusercontent.com/PySplanner/PySplanner/refs/heads/main/pysplanner.py"
100-
const response = await fetch(github_url);
101-
const code = await response.text();
102-
// TODO: Add the stuff to the code
103-
}
10490
}
10591

10692
// Custom PySplanner B-Spline algorithm
107-
const getCurvePoints = (pts: number[], tension = 0.5, isClosed = false, numOfSegments = 16) => {
93+
const GetCurvePoints = (pts: number[], tension = 0.5, isClosed = false, numOfSegments = 16) => {
10894
let _pts = pts.slice(0); // Copy the array of points
10995
let res = [], x, y, t1x, t2x, t1y, t2y, c1, c2, c3, c4, st, t;
11096

@@ -149,10 +135,8 @@ export default function App() {
149135
const mat_img = `./game_board_${theme ? theme : "dark"}.png`
150136
const [settings_active, SetSettingsActive] = useState(false)
151137
const [spike_server, SetSpikeServer] = useState<BluetoothRemoteGATTServer | null>(null)
152-
const [pysplan_handloer, SetPySplanHandler] = useState<SplanContent | null>(null)
153-
const [points, setPoints] = useState<Point[]>([]);
154-
const [history, setHistory] = useState<Point[][]>([[]]);
155-
const [currentIndex, setCurrentIndex] = useState(0);
138+
const [pysplan_handler, SetPySplanHandler] = useState<SplanContent | null>(null)
139+
const [run_index, SetRunIndex] = useState(0)
156140

157141
const HandleLoadSplan = () => {
158142
const input = document.createElement('input');
@@ -164,21 +148,24 @@ export default function App() {
164148
const reader = new FileReader();
165149
reader.onload = async () => {
166150
const data = JSON.parse(reader.result as string);
167-
try { const splan = new SplanContent(data); SetPySplanHandler(splan); } catch (e) { toast.error("Failed to load Splan file, check the console for more info", {duration: 5000}); console.error(e); }
151+
try {
152+
const splan = new SplanContent(data)
153+
SetPySplanHandler(splan);
154+
} catch (e) { toast.error("Failed to load Splan file, check the console for more info", {duration: 5000}); console.error(e); }
168155
}
169156
reader.readAsText(file);
170157
}
171158
input.click();
172159
}
173160

174161
const HandleSaveSplan = () => {
175-
if (!pysplan_handloer) { toast.error("No Splan to save", {duration: 5000}); return; }
176-
const data = JSON.stringify(pysplan_handloer);
162+
if (!pysplan_handler) { toast.error("No Splan to save", {duration: 5000}); return; }
163+
const data = JSON.stringify(pysplan_handler);
177164
const blob = new Blob([data], { type: "application/json" });
178165
const url = URL.createObjectURL(blob);
179166
const link = document.createElement("a");
180167
link.href = url;
181-
link.download = `${pysplan_handloer.name}.pysplan`;
168+
link.download = `${pysplan_handler.name}.pysplan`;
182169
link.click();
183170
URL.revokeObjectURL(url);
184171
}
@@ -190,40 +177,25 @@ export default function App() {
190177
// TODO: Add the stuff to the code
191178
}
192179

193-
const addPoint = (e: React.MouseEvent) => {
180+
const AddPoint = (e: React.MouseEvent) => {
181+
if (!pysplan_handler) { toast.error("No run selected", {duration: 5000}); return; }
194182
const rect = e.currentTarget.getBoundingClientRect();
195-
const newPoint = { x: e.clientX - rect.left, y: e.clientY - rect.top };
196-
const newPoints = [...points, newPoint];
183+
const new_point = new Point(e.clientX - rect.left, e.clientY - rect.top);
184+
const new_points = [...pysplan_handler.runs[run_index].points, new_point];
197185

198-
if (newPoints.length === 25) {
186+
if (new_points.length === 25) {
199187
toast.warning("WARNING: Exceeding 25 points may cause lagging/crashing of the Spike or EV3 robot.", {duration: 10000});
200-
} else if (newPoints.length === 50) {
188+
} else if (new_points.length === 50) {
201189
toast.error("You have reached the maximum number of points, which is 50.", {duration: 10000});
202190
return
203191
}
204192

205-
setPoints(newPoints);
206-
setHistory(history.slice(0, currentIndex + 1).concat([newPoints]));
207-
setCurrentIndex(currentIndex + 1);
193+
pysplan_handler.runs[run_index].points = new_points;
194+
SetPySplanHandler(pysplan_handler);
208195
};
209196

210-
const handleKeyDown = (e: KeyboardEvent) => {
211-
if (e.ctrlKey && e.key === 'z' && currentIndex > 0) {
212-
setCurrentIndex(currentIndex - 1);
213-
setPoints(history[currentIndex - 1]);
214-
} else if (e.ctrlKey && e.key === 'y' && currentIndex < history.length - 1) {
215-
setCurrentIndex(currentIndex + 1);
216-
setPoints(history[currentIndex + 1]);
217-
}
218-
};
219-
220-
useEffect(() => {
221-
window.addEventListener('keydown', handleKeyDown);
222-
return () => window.removeEventListener('keydown', handleKeyDown);
223-
}, [points, history, currentIndex]);
224-
225-
const flatPoints = points.flatMap(p => [p.x, p.y]);
226-
const splinePoints = getCurvePoints(flatPoints);
197+
const flat_points = pysplan_handler?.runs[run_index].points.flatMap(p => [p.x, p.y])
198+
const spline_points = GetCurvePoints(flat_points ?? []);
227199

228200
const GetSpikeServer = async () => {
229201
toast.promise(
@@ -280,11 +252,25 @@ export default function App() {
280252
</Accordion>
281253
<Button variant="secondary" className="w-full" onClick={ () => SetSettingsActive(true) }>Settings</Button>
282254
</div>
283-
<Separator orientation="horizontal" className="bg-zinc-600 w-[calc(100%-30px)] ml-[15px] my-4" />
255+
<Separator orientation="horizontal" className="bg-zinc-600 w-[calc(100%-30px)] ml-[15px] mt-4" />
284256
</div>
285257
<div className="flex flex-col flex-grow overflow-y-auto">
286-
<p className="text-center mt-4 w-[calc(100%-30px)] ml-[15px] font-bold text-lg">Under Construction</p>
287-
<p className="text-center mt-1 w-[calc(100%-30px)] ml-[15px] text-sm text-zinc-500">This path planner is currently being developed</p>
258+
{pysplan_handler ? (
259+
<div className="flex flex-col gap-2 w-[calc(100%-30px)] ml-[15px]">
260+
{pysplan_handler.runs.map((run, idx) => (
261+
<Button key={idx} variant={run_index === idx ? "secondary" : "outline"} className="w-full" onClick={() => SetRunIndex(idx)}>{run.name}</Button>
262+
))}
263+
</div>
264+
) : (
265+
<div>
266+
<p className="text-center mt-4 w-[calc(100%-30px)] ml-[15px] font-bold text-lg">
267+
Under Construction
268+
</p>
269+
<p className="text-center mt-1 w-[calc(100%-30px)] ml-[15px] text-sm text-zinc-500">
270+
This path planner is currently being developed
271+
</p>
272+
</div>
273+
)}
288274
</div>
289275
</Card>
290276
);
@@ -293,16 +279,16 @@ export default function App() {
293279
const Home = () => {
294280
return (
295281
<div className="flex items-center justify-center w-full h-full">
296-
{pysplan_handloer ? (
282+
{pysplan_handler ? (
297283
<div className="relative flex items-center justify-center w-full h-full border rounded-lg ml-4">
298284
<div className="relative">
299285
<img src={mat_img} className="w-auto h-auto max-h-[85vh] max-w-[85vw] object-contain"/>
300286
<div className="absolute inset-0 flex items-center justify-center">
301-
<div className="w-full h-full relative" onClick={addPoint}>
302-
{points.map((p, idx) => (
287+
<div className="w-full h-full relative" onClick={AddPoint}>
288+
{pysplan_handler.runs[run_index].points.map((p, idx) => (
303289
<div key={idx} className="absolute bg-green-500 w-2 h-2 rounded-full" style={{ left: `${p.x}px`, top: `${p.y}px` }}/>
304290
))}
305-
{splinePoints.map((p, idx) => (
291+
{spline_points.map((p, idx) => (
306292
<div key={idx} className="absolute bg-green-400 w-1 h-1 rounded-full" style={{ left: `${p.x}px`, top: `${p.y}px` }}/>
307293
))}
308294
</div>

0 commit comments

Comments
 (0)