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 () => {
|
||||
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';
|
||||
await exportToWokwiZip(files, components, wires, boardType, projectName);
|
||||
await exportToWokwiZip(files, components, wires, boardType, projectName, boardPosition);
|
||||
} catch (err) {
|
||||
setMessage({ type: 'error', text: 'Export failed.' });
|
||||
}
|
||||
|
|
@ -113,9 +113,10 @@ export const EditorToolbar = ({ consoleOpen, setConsoleOpen, compileLogs: _compi
|
|||
try {
|
||||
const result = await importFromWokwiZip(file);
|
||||
const { loadFiles } = useEditorStore.getState();
|
||||
const { setComponents, setWires, setBoardType, stopSimulation } = useSimulatorStore.getState();
|
||||
const { setComponents, setWires, setBoardType, setBoardPosition, stopSimulation } = useSimulatorStore.getState();
|
||||
stopSimulation();
|
||||
if (result.boardType) setBoardType(result.boardType);
|
||||
setBoardPosition(result.boardPosition);
|
||||
setComponents(result.components);
|
||||
setWires(result.wires);
|
||||
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 React, { useEffect, useState, useRef, useCallback } from 'react';
|
||||
import { ArduinoUno } from '../components-wokwi/ArduinoUno';
|
||||
|
|
@ -19,6 +19,8 @@ export const SimulatorCanvas = () => {
|
|||
const {
|
||||
boardType,
|
||||
setBoardType,
|
||||
boardPosition,
|
||||
setBoardPosition,
|
||||
components,
|
||||
running,
|
||||
pinManager,
|
||||
|
|
@ -289,10 +291,17 @@ export const SimulatorCanvas = () => {
|
|||
// Handle component dragging
|
||||
if (draggedComponentId) {
|
||||
const world = toWorld(e.clientX, e.clientY);
|
||||
updateComponent(draggedComponentId, {
|
||||
x: Math.max(0, world.x - dragOffset.x),
|
||||
y: Math.max(0, world.y - dragOffset.y),
|
||||
} as any);
|
||||
if (draggedComponentId === '__board__') {
|
||||
setBoardPosition({
|
||||
x: Math.max(0, world.x - dragOffset.x),
|
||||
y: Math.max(0, world.y - dragOffset.y),
|
||||
});
|
||||
} else {
|
||||
updateComponent(draggedComponentId, {
|
||||
x: Math.max(0, world.x - dragOffset.x),
|
||||
y: Math.max(0, world.y - dragOffset.y),
|
||||
} as any);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle wire creation preview
|
||||
|
|
@ -317,7 +326,7 @@ export const SimulatorCanvas = () => {
|
|||
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);
|
||||
if (component) {
|
||||
setPropertyDialogComponentId(draggedComponentId);
|
||||
|
|
@ -574,23 +583,47 @@ export const SimulatorCanvas = () => {
|
|||
{/* Board visual — switches based on selected board type */}
|
||||
{boardType === 'arduino-uno' ? (
|
||||
<ArduinoUno
|
||||
x={ARDUINO_POSITION.x}
|
||||
y={ARDUINO_POSITION.y}
|
||||
x={boardPosition.x}
|
||||
y={boardPosition.y}
|
||||
led13={Boolean(components.find((c) => c.id === 'led-builtin')?.properties.state)}
|
||||
/>
|
||||
) : (
|
||||
<NanoRP2040
|
||||
x={ARDUINO_POSITION.x}
|
||||
y={ARDUINO_POSITION.y}
|
||||
x={boardPosition.x}
|
||||
y={boardPosition.y}
|
||||
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 */}
|
||||
<PinOverlay
|
||||
componentId={boardType === 'arduino-uno' ? 'arduino-uno' : 'nano-rp2040'}
|
||||
componentX={ARDUINO_POSITION.x}
|
||||
componentY={ARDUINO_POSITION.y}
|
||||
componentX={boardPosition.x}
|
||||
componentY={boardPosition.y}
|
||||
onPinClick={handlePinClick}
|
||||
showPins={true}
|
||||
wrapperOffsetX={0}
|
||||
|
|
|
|||
|
|
@ -19,8 +19,10 @@ export const BOARD_LABELS: Record<BoardType, string> = {
|
|||
'raspberry-pi-pico': 'Raspberry Pi Pico',
|
||||
};
|
||||
|
||||
// Fixed position for the Arduino board (not in components array)
|
||||
export const ARDUINO_POSITION = { x: 50, y: 50 };
|
||||
// Default position for the Arduino board
|
||||
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 {
|
||||
id: string;
|
||||
|
|
@ -35,6 +37,10 @@ interface SimulatorState {
|
|||
boardType: BoardType;
|
||||
setBoardType: (type: BoardType) => void;
|
||||
|
||||
// Board position (mutable — allows dragging)
|
||||
boardPosition: { x: number; y: number };
|
||||
setBoardPosition: (pos: { x: number; y: number }) => void;
|
||||
|
||||
// Simulation state
|
||||
simulator: AVRSimulator | RP2040Simulator | null;
|
||||
pinManager: PinManager;
|
||||
|
|
@ -102,6 +108,7 @@ export const useSimulatorStore = create<SimulatorState>((set, get) => {
|
|||
|
||||
return {
|
||||
boardType: 'arduino-uno' as BoardType,
|
||||
boardPosition: { ...DEFAULT_BOARD_POSITION },
|
||||
simulator: null,
|
||||
pinManager,
|
||||
running: false,
|
||||
|
|
@ -168,6 +175,10 @@ export const useSimulatorStore = create<SimulatorState>((set, get) => {
|
|||
serialBaudRate: 0,
|
||||
serialMonitorOpen: false,
|
||||
|
||||
setBoardPosition: (pos) => {
|
||||
set({ boardPosition: pos });
|
||||
},
|
||||
|
||||
setBoardType: (type: BoardType) => {
|
||||
const { running } = get();
|
||||
if (running) {
|
||||
|
|
@ -426,9 +437,10 @@ export const useSimulatorStore = create<SimulatorState>((set, get) => {
|
|||
updateWirePositions: (componentId) => {
|
||||
set((state) => {
|
||||
const component = state.components.find((c) => c.id === componentId);
|
||||
// For fixed components like Arduino, use ARDUINO_POSITION
|
||||
const compX = component ? component.x : ARDUINO_POSITION.x;
|
||||
const compY = component ? component.y : ARDUINO_POSITION.y;
|
||||
// For the board, use boardPosition from state
|
||||
const bp = state.boardPosition;
|
||||
const compX = component ? component.x : bp.x;
|
||||
const compY = component ? component.y : bp.y;
|
||||
|
||||
const updatedWires = state.wires.map((wire) => {
|
||||
const updated = { ...wire };
|
||||
|
|
@ -471,8 +483,9 @@ export const useSimulatorStore = create<SimulatorState>((set, get) => {
|
|||
const updatedWires = state.wires.map((wire) => {
|
||||
const updated = { ...wire };
|
||||
const startComp = state.components.find((c) => c.id === wire.start.componentId);
|
||||
const startX = startComp ? startComp.x : ARDUINO_POSITION.x;
|
||||
const startY = startComp ? startComp.y : ARDUINO_POSITION.y;
|
||||
const bp = state.boardPosition;
|
||||
const startX = startComp ? startComp.x : bp.x;
|
||||
const startY = startComp ? startComp.y : bp.y;
|
||||
|
||||
const startPos = calculatePinPosition(
|
||||
wire.start.componentId,
|
||||
|
|
@ -486,8 +499,8 @@ export const useSimulatorStore = create<SimulatorState>((set, get) => {
|
|||
|
||||
// Resolve end component position
|
||||
const endComp = state.components.find((c) => c.id === wire.end.componentId);
|
||||
const endX = endComp ? endComp.x : ARDUINO_POSITION.x;
|
||||
const endY = endComp ? endComp.y : ARDUINO_POSITION.y;
|
||||
const endX = endComp ? endComp.x : bp.x;
|
||||
const endY = endComp ? endComp.y : bp.y;
|
||||
|
||||
const endPos = calculatePinPosition(
|
||||
wire.end.componentId,
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@
|
|||
|
||||
import JSZip from 'jszip';
|
||||
import type { Wire } from '../types/wire';
|
||||
import { ARDUINO_POSITION } from '../store/useSimulatorStore';
|
||||
|
||||
// ── Type definitions ──────────────────────────────────────────────────────────
|
||||
|
||||
|
|
@ -45,6 +44,7 @@ export interface VelxioComponent {
|
|||
|
||||
export interface ImportResult {
|
||||
boardType: 'arduino-uno' | 'raspberry-pi-pico';
|
||||
boardPosition: { x: number; y: number };
|
||||
components: VelxioComponent[];
|
||||
wires: Wire[];
|
||||
files: Array<{ name: string; content: string }>;
|
||||
|
|
@ -118,6 +118,7 @@ export async function exportToWokwiZip(
|
|||
wires: Wire[],
|
||||
boardType: string,
|
||||
projectName: string,
|
||||
boardPosition: { x: number; y: number } = { x: 50, y: 50 },
|
||||
): Promise<void> {
|
||||
const zip = new JSZip();
|
||||
|
||||
|
|
@ -125,14 +126,14 @@ export async function exportToWokwiZip(
|
|||
const boardId = BOARD_TO_WOKWI_ID[boardType] ?? 'uno';
|
||||
|
||||
// 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[] = [
|
||||
{ type: boardWokwiType, id: boardId, top: 0, left: 0, attrs: {} },
|
||||
...components.map((c) => ({
|
||||
type: metadataIdToWokwiType(c.metadataId),
|
||||
id: c.id,
|
||||
top: Math.round(c.y - ARDUINO_POSITION.y),
|
||||
left: Math.round(c.x - ARDUINO_POSITION.x),
|
||||
top: Math.round(c.y - boardPosition.y),
|
||||
left: Math.round(c.x - boardPosition.x),
|
||||
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 boardId = boardPart?.id ?? 'uno';
|
||||
|
||||
// Calculate offset: Wokwi board position → Velxio ARDUINO_POSITION
|
||||
const wokwiBoardX = boardPart?.left ?? 0;
|
||||
const wokwiBoardY = boardPart?.top ?? 0;
|
||||
const offsetX = ARDUINO_POSITION.x - wokwiBoardX;
|
||||
const offsetY = ARDUINO_POSITION.y - wokwiBoardY;
|
||||
// Board position from diagram (use directly as Velxio board position)
|
||||
const boardPosition = {
|
||||
x: boardPart?.left ?? 50,
|
||||
y: boardPart?.top ?? 50,
|
||||
};
|
||||
|
||||
// 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
|
||||
.filter((p) => !WOKWI_TYPE_TO_BOARD[p.type])
|
||||
.map((p) => ({
|
||||
id: p.id,
|
||||
metadataId: wokwiTypeToMetadataId(p.type),
|
||||
x: p.left + offsetX,
|
||||
y: p.top + offsetY,
|
||||
x: p.left,
|
||||
y: p.top,
|
||||
properties: { ...p.attrs },
|
||||
}));
|
||||
|
||||
|
|
@ -257,5 +258,5 @@ export async function importFromWokwiZip(file: File): Promise<ImportResult> {
|
|||
return a.name.localeCompare(b.name);
|
||||
});
|
||||
|
||||
return { boardType, components, wires, files };
|
||||
return { boardType, boardPosition, components, wires, files };
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue