feat: Enhance board positioning by integrating boardPosition state in export/import functions and updating component dragging logic
parent
5f2c176648
commit
f2b275c03d
|
|
@ -97,9 +97,9 @@ export const EditorToolbar = ({ consoleOpen, setConsoleOpen, compileLogs: _compi
|
||||||
|
|
||||||
const handleExport = async () => {
|
const handleExport = async () => {
|
||||||
try {
|
try {
|
||||||
const { components, wires } = useSimulatorStore.getState();
|
const { components, wires, boardPosition } = useSimulatorStore.getState();
|
||||||
const projectName = files.find((f) => f.name.endsWith('.ino'))?.name.replace('.ino', '') || 'velxio-project';
|
const projectName = files.find((f) => f.name.endsWith('.ino'))?.name.replace('.ino', '') || 'velxio-project';
|
||||||
await exportToWokwiZip(files, components, wires, boardType, projectName);
|
await exportToWokwiZip(files, components, wires, boardType, projectName, boardPosition);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setMessage({ type: 'error', text: 'Export failed.' });
|
setMessage({ type: 'error', text: 'Export failed.' });
|
||||||
}
|
}
|
||||||
|
|
@ -113,9 +113,10 @@ export const EditorToolbar = ({ consoleOpen, setConsoleOpen, compileLogs: _compi
|
||||||
try {
|
try {
|
||||||
const result = await importFromWokwiZip(file);
|
const result = await importFromWokwiZip(file);
|
||||||
const { loadFiles } = useEditorStore.getState();
|
const { loadFiles } = useEditorStore.getState();
|
||||||
const { setComponents, setWires, setBoardType, stopSimulation } = useSimulatorStore.getState();
|
const { setComponents, setWires, setBoardType, setBoardPosition, stopSimulation } = useSimulatorStore.getState();
|
||||||
stopSimulation();
|
stopSimulation();
|
||||||
if (result.boardType) setBoardType(result.boardType);
|
if (result.boardType) setBoardType(result.boardType);
|
||||||
|
setBoardPosition(result.boardPosition);
|
||||||
setComponents(result.components);
|
setComponents(result.components);
|
||||||
setWires(result.wires);
|
setWires(result.wires);
|
||||||
if (result.files.length > 0) loadFiles(result.files);
|
if (result.files.length > 0) loadFiles(result.files);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { useSimulatorStore, ARDUINO_POSITION, BOARD_LABELS } from '../../store/useSimulatorStore';
|
import { useSimulatorStore, BOARD_LABELS } from '../../store/useSimulatorStore';
|
||||||
import type { BoardType } from '../../store/useSimulatorStore';
|
import type { BoardType } from '../../store/useSimulatorStore';
|
||||||
import React, { useEffect, useState, useRef, useCallback } from 'react';
|
import React, { useEffect, useState, useRef, useCallback } from 'react';
|
||||||
import { ArduinoUno } from '../components-wokwi/ArduinoUno';
|
import { ArduinoUno } from '../components-wokwi/ArduinoUno';
|
||||||
|
|
@ -19,6 +19,8 @@ export const SimulatorCanvas = () => {
|
||||||
const {
|
const {
|
||||||
boardType,
|
boardType,
|
||||||
setBoardType,
|
setBoardType,
|
||||||
|
boardPosition,
|
||||||
|
setBoardPosition,
|
||||||
components,
|
components,
|
||||||
running,
|
running,
|
||||||
pinManager,
|
pinManager,
|
||||||
|
|
@ -289,11 +291,18 @@ export const SimulatorCanvas = () => {
|
||||||
// Handle component dragging
|
// Handle component dragging
|
||||||
if (draggedComponentId) {
|
if (draggedComponentId) {
|
||||||
const world = toWorld(e.clientX, e.clientY);
|
const world = toWorld(e.clientX, e.clientY);
|
||||||
|
if (draggedComponentId === '__board__') {
|
||||||
|
setBoardPosition({
|
||||||
|
x: Math.max(0, world.x - dragOffset.x),
|
||||||
|
y: Math.max(0, world.y - dragOffset.y),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
updateComponent(draggedComponentId, {
|
updateComponent(draggedComponentId, {
|
||||||
x: Math.max(0, world.x - dragOffset.x),
|
x: Math.max(0, world.x - dragOffset.x),
|
||||||
y: Math.max(0, world.y - dragOffset.y),
|
y: Math.max(0, world.y - dragOffset.y),
|
||||||
} as any);
|
} as any);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Handle wire creation preview
|
// Handle wire creation preview
|
||||||
if (wireInProgress) {
|
if (wireInProgress) {
|
||||||
|
|
@ -317,7 +326,7 @@ export const SimulatorCanvas = () => {
|
||||||
Math.pow(e.clientY - clickStartPos.y, 2)
|
Math.pow(e.clientY - clickStartPos.y, 2)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (posDiff < 5 && timeDiff < 300) {
|
if (posDiff < 5 && timeDiff < 300 && draggedComponentId !== '__board__') {
|
||||||
const component = components.find((c) => c.id === draggedComponentId);
|
const component = components.find((c) => c.id === draggedComponentId);
|
||||||
if (component) {
|
if (component) {
|
||||||
setPropertyDialogComponentId(draggedComponentId);
|
setPropertyDialogComponentId(draggedComponentId);
|
||||||
|
|
@ -574,23 +583,47 @@ export const SimulatorCanvas = () => {
|
||||||
{/* Board visual — switches based on selected board type */}
|
{/* Board visual — switches based on selected board type */}
|
||||||
{boardType === 'arduino-uno' ? (
|
{boardType === 'arduino-uno' ? (
|
||||||
<ArduinoUno
|
<ArduinoUno
|
||||||
x={ARDUINO_POSITION.x}
|
x={boardPosition.x}
|
||||||
y={ARDUINO_POSITION.y}
|
y={boardPosition.y}
|
||||||
led13={Boolean(components.find((c) => c.id === 'led-builtin')?.properties.state)}
|
led13={Boolean(components.find((c) => c.id === 'led-builtin')?.properties.state)}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<NanoRP2040
|
<NanoRP2040
|
||||||
x={ARDUINO_POSITION.x}
|
x={boardPosition.x}
|
||||||
y={ARDUINO_POSITION.y}
|
y={boardPosition.y}
|
||||||
ledBuiltIn={Boolean(components.find((c) => c.id === 'led-builtin')?.properties.state)}
|
ledBuiltIn={Boolean(components.find((c) => c.id === 'led-builtin')?.properties.state)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Board interaction overlay for dragging */}
|
||||||
|
{!running && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
left: boardPosition.x,
|
||||||
|
top: boardPosition.y,
|
||||||
|
width: boardType === 'arduino-uno' ? 360 : 280,
|
||||||
|
height: boardType === 'arduino-uno' ? 250 : 180,
|
||||||
|
cursor: 'move',
|
||||||
|
zIndex: 1,
|
||||||
|
}}
|
||||||
|
onMouseDown={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
const world = toWorld(e.clientX, e.clientY);
|
||||||
|
setDraggedComponentId('__board__');
|
||||||
|
setDragOffset({
|
||||||
|
x: world.x - boardPosition.x,
|
||||||
|
y: world.y - boardPosition.y,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Board pin overlay */}
|
{/* Board pin overlay */}
|
||||||
<PinOverlay
|
<PinOverlay
|
||||||
componentId={boardType === 'arduino-uno' ? 'arduino-uno' : 'nano-rp2040'}
|
componentId={boardType === 'arduino-uno' ? 'arduino-uno' : 'nano-rp2040'}
|
||||||
componentX={ARDUINO_POSITION.x}
|
componentX={boardPosition.x}
|
||||||
componentY={ARDUINO_POSITION.y}
|
componentY={boardPosition.y}
|
||||||
onPinClick={handlePinClick}
|
onPinClick={handlePinClick}
|
||||||
showPins={true}
|
showPins={true}
|
||||||
wrapperOffsetX={0}
|
wrapperOffsetX={0}
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,10 @@ export const BOARD_LABELS: Record<BoardType, string> = {
|
||||||
'raspberry-pi-pico': 'Raspberry Pi Pico',
|
'raspberry-pi-pico': 'Raspberry Pi Pico',
|
||||||
};
|
};
|
||||||
|
|
||||||
// Fixed position for the Arduino board (not in components array)
|
// Default position for the Arduino board
|
||||||
export const ARDUINO_POSITION = { x: 50, y: 50 };
|
export const DEFAULT_BOARD_POSITION = { x: 50, y: 50 };
|
||||||
|
// Keep legacy export alias for any remaining references
|
||||||
|
export const ARDUINO_POSITION = DEFAULT_BOARD_POSITION;
|
||||||
|
|
||||||
interface Component {
|
interface Component {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
@ -35,6 +37,10 @@ interface SimulatorState {
|
||||||
boardType: BoardType;
|
boardType: BoardType;
|
||||||
setBoardType: (type: BoardType) => void;
|
setBoardType: (type: BoardType) => void;
|
||||||
|
|
||||||
|
// Board position (mutable — allows dragging)
|
||||||
|
boardPosition: { x: number; y: number };
|
||||||
|
setBoardPosition: (pos: { x: number; y: number }) => void;
|
||||||
|
|
||||||
// Simulation state
|
// Simulation state
|
||||||
simulator: AVRSimulator | RP2040Simulator | null;
|
simulator: AVRSimulator | RP2040Simulator | null;
|
||||||
pinManager: PinManager;
|
pinManager: PinManager;
|
||||||
|
|
@ -102,6 +108,7 @@ export const useSimulatorStore = create<SimulatorState>((set, get) => {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
boardType: 'arduino-uno' as BoardType,
|
boardType: 'arduino-uno' as BoardType,
|
||||||
|
boardPosition: { ...DEFAULT_BOARD_POSITION },
|
||||||
simulator: null,
|
simulator: null,
|
||||||
pinManager,
|
pinManager,
|
||||||
running: false,
|
running: false,
|
||||||
|
|
@ -168,6 +175,10 @@ export const useSimulatorStore = create<SimulatorState>((set, get) => {
|
||||||
serialBaudRate: 0,
|
serialBaudRate: 0,
|
||||||
serialMonitorOpen: false,
|
serialMonitorOpen: false,
|
||||||
|
|
||||||
|
setBoardPosition: (pos) => {
|
||||||
|
set({ boardPosition: pos });
|
||||||
|
},
|
||||||
|
|
||||||
setBoardType: (type: BoardType) => {
|
setBoardType: (type: BoardType) => {
|
||||||
const { running } = get();
|
const { running } = get();
|
||||||
if (running) {
|
if (running) {
|
||||||
|
|
@ -426,9 +437,10 @@ export const useSimulatorStore = create<SimulatorState>((set, get) => {
|
||||||
updateWirePositions: (componentId) => {
|
updateWirePositions: (componentId) => {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
const component = state.components.find((c) => c.id === componentId);
|
const component = state.components.find((c) => c.id === componentId);
|
||||||
// For fixed components like Arduino, use ARDUINO_POSITION
|
// For the board, use boardPosition from state
|
||||||
const compX = component ? component.x : ARDUINO_POSITION.x;
|
const bp = state.boardPosition;
|
||||||
const compY = component ? component.y : ARDUINO_POSITION.y;
|
const compX = component ? component.x : bp.x;
|
||||||
|
const compY = component ? component.y : bp.y;
|
||||||
|
|
||||||
const updatedWires = state.wires.map((wire) => {
|
const updatedWires = state.wires.map((wire) => {
|
||||||
const updated = { ...wire };
|
const updated = { ...wire };
|
||||||
|
|
@ -471,8 +483,9 @@ export const useSimulatorStore = create<SimulatorState>((set, get) => {
|
||||||
const updatedWires = state.wires.map((wire) => {
|
const updatedWires = state.wires.map((wire) => {
|
||||||
const updated = { ...wire };
|
const updated = { ...wire };
|
||||||
const startComp = state.components.find((c) => c.id === wire.start.componentId);
|
const startComp = state.components.find((c) => c.id === wire.start.componentId);
|
||||||
const startX = startComp ? startComp.x : ARDUINO_POSITION.x;
|
const bp = state.boardPosition;
|
||||||
const startY = startComp ? startComp.y : ARDUINO_POSITION.y;
|
const startX = startComp ? startComp.x : bp.x;
|
||||||
|
const startY = startComp ? startComp.y : bp.y;
|
||||||
|
|
||||||
const startPos = calculatePinPosition(
|
const startPos = calculatePinPosition(
|
||||||
wire.start.componentId,
|
wire.start.componentId,
|
||||||
|
|
@ -486,8 +499,8 @@ export const useSimulatorStore = create<SimulatorState>((set, get) => {
|
||||||
|
|
||||||
// Resolve end component position
|
// Resolve end component position
|
||||||
const endComp = state.components.find((c) => c.id === wire.end.componentId);
|
const endComp = state.components.find((c) => c.id === wire.end.componentId);
|
||||||
const endX = endComp ? endComp.x : ARDUINO_POSITION.x;
|
const endX = endComp ? endComp.x : bp.x;
|
||||||
const endY = endComp ? endComp.y : ARDUINO_POSITION.y;
|
const endY = endComp ? endComp.y : bp.y;
|
||||||
|
|
||||||
const endPos = calculatePinPosition(
|
const endPos = calculatePinPosition(
|
||||||
wire.end.componentId,
|
wire.end.componentId,
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@
|
||||||
|
|
||||||
import JSZip from 'jszip';
|
import JSZip from 'jszip';
|
||||||
import type { Wire } from '../types/wire';
|
import type { Wire } from '../types/wire';
|
||||||
import { ARDUINO_POSITION } from '../store/useSimulatorStore';
|
|
||||||
|
|
||||||
// ── Type definitions ──────────────────────────────────────────────────────────
|
// ── Type definitions ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
@ -45,6 +44,7 @@ export interface VelxioComponent {
|
||||||
|
|
||||||
export interface ImportResult {
|
export interface ImportResult {
|
||||||
boardType: 'arduino-uno' | 'raspberry-pi-pico';
|
boardType: 'arduino-uno' | 'raspberry-pi-pico';
|
||||||
|
boardPosition: { x: number; y: number };
|
||||||
components: VelxioComponent[];
|
components: VelxioComponent[];
|
||||||
wires: Wire[];
|
wires: Wire[];
|
||||||
files: Array<{ name: string; content: string }>;
|
files: Array<{ name: string; content: string }>;
|
||||||
|
|
@ -118,6 +118,7 @@ export async function exportToWokwiZip(
|
||||||
wires: Wire[],
|
wires: Wire[],
|
||||||
boardType: string,
|
boardType: string,
|
||||||
projectName: string,
|
projectName: string,
|
||||||
|
boardPosition: { x: number; y: number } = { x: 50, y: 50 },
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const zip = new JSZip();
|
const zip = new JSZip();
|
||||||
|
|
||||||
|
|
@ -125,14 +126,14 @@ export async function exportToWokwiZip(
|
||||||
const boardId = BOARD_TO_WOKWI_ID[boardType] ?? 'uno';
|
const boardId = BOARD_TO_WOKWI_ID[boardType] ?? 'uno';
|
||||||
|
|
||||||
// Build parts — board first, then user components
|
// Build parts — board first, then user components
|
||||||
// Subtract ARDUINO_POSITION to convert from Velxio coords to Wokwi-relative coords
|
// Subtract boardPosition so coords are relative to the board
|
||||||
const parts: WokwiPart[] = [
|
const parts: WokwiPart[] = [
|
||||||
{ type: boardWokwiType, id: boardId, top: 0, left: 0, attrs: {} },
|
{ type: boardWokwiType, id: boardId, top: 0, left: 0, attrs: {} },
|
||||||
...components.map((c) => ({
|
...components.map((c) => ({
|
||||||
type: metadataIdToWokwiType(c.metadataId),
|
type: metadataIdToWokwiType(c.metadataId),
|
||||||
id: c.id,
|
id: c.id,
|
||||||
top: Math.round(c.y - ARDUINO_POSITION.y),
|
top: Math.round(c.y - boardPosition.y),
|
||||||
left: Math.round(c.x - ARDUINO_POSITION.x),
|
left: Math.round(c.x - boardPosition.x),
|
||||||
attrs: c.properties as Record<string, unknown>,
|
attrs: c.properties as Record<string, unknown>,
|
||||||
})),
|
})),
|
||||||
];
|
];
|
||||||
|
|
@ -192,20 +193,20 @@ export async function importFromWokwiZip(file: File): Promise<ImportResult> {
|
||||||
const boardType = boardPart ? WOKWI_TYPE_TO_BOARD[boardPart.type] : 'arduino-uno';
|
const boardType = boardPart ? WOKWI_TYPE_TO_BOARD[boardPart.type] : 'arduino-uno';
|
||||||
const boardId = boardPart?.id ?? 'uno';
|
const boardId = boardPart?.id ?? 'uno';
|
||||||
|
|
||||||
// Calculate offset: Wokwi board position → Velxio ARDUINO_POSITION
|
// Board position from diagram (use directly as Velxio board position)
|
||||||
const wokwiBoardX = boardPart?.left ?? 0;
|
const boardPosition = {
|
||||||
const wokwiBoardY = boardPart?.top ?? 0;
|
x: boardPart?.left ?? 50,
|
||||||
const offsetX = ARDUINO_POSITION.x - wokwiBoardX;
|
y: boardPart?.top ?? 50,
|
||||||
const offsetY = ARDUINO_POSITION.y - wokwiBoardY;
|
};
|
||||||
|
|
||||||
// Convert non-board parts to Velxio components (apply offset)
|
// Convert non-board parts to Velxio components (use Wokwi coords directly)
|
||||||
const components: VelxioComponent[] = diagram.parts
|
const components: VelxioComponent[] = diagram.parts
|
||||||
.filter((p) => !WOKWI_TYPE_TO_BOARD[p.type])
|
.filter((p) => !WOKWI_TYPE_TO_BOARD[p.type])
|
||||||
.map((p) => ({
|
.map((p) => ({
|
||||||
id: p.id,
|
id: p.id,
|
||||||
metadataId: wokwiTypeToMetadataId(p.type),
|
metadataId: wokwiTypeToMetadataId(p.type),
|
||||||
x: p.left + offsetX,
|
x: p.left,
|
||||||
y: p.top + offsetY,
|
y: p.top,
|
||||||
properties: { ...p.attrs },
|
properties: { ...p.attrs },
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
@ -257,5 +258,5 @@ export async function importFromWokwiZip(file: File): Promise<ImportResult> {
|
||||||
return a.name.localeCompare(b.name);
|
return a.name.localeCompare(b.name);
|
||||||
});
|
});
|
||||||
|
|
||||||
return { boardType, components, wires, files };
|
return { boardType, boardPosition, components, wires, files };
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue