2 Commits

Author SHA1 Message Date
PotatoMaxwell fa0ebe899c Merge pull request 'rotation bug fixed' (#9) from jingwen_main into qinyue_main
Reviewed-on: #9
2026-06-09 12:17:23 +00:00
xsxx03-art 2846899097 rotation bug fixed 2026-06-08 18:54:42 +08:00
2 changed files with 47 additions and 11 deletions
+17
View File
@@ -1282,6 +1282,22 @@ bundles:${groupsYaml ? `\n${groupsYaml}` : ' {}'}`;
return null; return null;
}; };
const getRotatableNodeHandleDirection = (node, handleId) => {
if (!node || !handleId) return null;
if (node.type !== 'rotatableNode' && !(!node.data?.elementType && node.data?.componentName)) return null;
const ports = node.data && node.data.ports;
if (!ports || !ports[handleId]) return null;
const boxSize = normalizeBoxSize({ box_size: node.data && node.data.boxSize }, DEFAULT_COMPONENT_BOX_SIZE);
const handles = buildPortHandles(ports, {
rotation: Number((node.data && node.data.rotation) || 0),
flip: Boolean(node.data && node.data.flip),
flop: Boolean(node.data && node.data.flop),
boxSize
});
const found = handles.find(handle => handle.name === handleId);
return found ? found.position : null;
};
// Backward-compatible alias for same-type route crossing validation. // Backward-compatible alias for same-type route crossing validation.
const findSameFamilyRouteCrossing = findSameTypeRouteCrossing; const findSameFamilyRouteCrossing = findSameTypeRouteCrossing;
@@ -1324,6 +1340,7 @@ bundles:${groupsYaml ? `\n${groupsYaml}` : ' {}'}`;
createComponentSymbolMetrics, createComponentSymbolMetrics,
transformPortInfo, transformPortInfo,
getNodePortCanvasPoint, getNodePortCanvasPoint,
getRotatableNodeHandleDirection,
buildPortHandles, buildPortHandles,
buildElementPorts, buildElementPorts,
buildElementPinEntries, buildElementPinEntries,
+30 -11
View File
@@ -1566,6 +1566,7 @@ Organization : OptiHK Limited
calculateLayoutBounds, calculateLayoutBounds,
calculateCompositeBoxSize, calculateCompositeBoxSize,
buildPortHandles, buildPortHandles,
getRotatableNodeHandleDirection,
buildElementPorts, buildElementPorts,
getElementPinName, getElementPinName,
buildElementBoxSize, buildElementBoxSize,
@@ -1710,8 +1711,10 @@ Organization : OptiHK Limited
useEffect(() => { useEffect(() => {
const transformKey = `${data.rotation || 0}:${data.flip ? 1 : 0}:${data.flop ? 1 : 0}`; const transformKey = `${data.rotation || 0}:${data.flip ? 1 : 0}:${data.flop ? 1 : 0}`;
if (prevTransformRef.current !== transformKey) { if (prevTransformRef.current !== transformKey) {
updateNodeInternalsRef.current(id);
prevTransformRef.current = transformKey; prevTransformRef.current = transformKey;
requestAnimationFrame(() => {
updateNodeInternalsRef.current(id);
});
} }
}, [data.rotation, data.flip, data.flop, id]); }, [data.rotation, data.flip, data.flop, id]);
@@ -1731,6 +1734,16 @@ Organization : OptiHK Limited
top: Position.Top, top: Position.Top,
bottom: Position.Bottom bottom: Position.Bottom
}; };
const rotateHandleDirection = (dir, rot) => {
const norm = ((rot % 360) + 360) % 360;
const map = {
0: { right: 'right', left: 'left', top: 'top', bottom: 'bottom' },
90: { right: 'bottom', left: 'top', top: 'left', bottom: 'right' },
180: { right: 'left', left: 'right', top: 'bottom', bottom: 'top' },
270: { right: 'top', left: 'bottom', top: 'right', bottom: 'left' }
};
return (map[norm] || map[0])[dir] || dir;
};
const componentSize = normalizeBoxSize({ box_size: data.boxSize }, DEFAULT_COMPONENT_BOX_SIZE); const componentSize = normalizeBoxSize({ box_size: data.boxSize }, DEFAULT_COMPONENT_BOX_SIZE);
const flippedPorts = useMemo( const flippedPorts = useMemo(
() => { () => {
@@ -1856,24 +1869,28 @@ Organization : OptiHK Limited
transformOrigin: 'center center', transformOrigin: 'center center',
pointerEvents: 'none' pointerEvents: 'none'
}}> }}>
{portHandles.map((portHandle) => ( {portHandles.map((portHandle) => {
const originalDir = portDirectionMap.get(portHandle.name) || portHandle.position;
const effectiveDir = rotateHandleDirection(originalDir, data.rotation || 0);
return (
<React.Fragment key={portHandle.name}> <React.Fragment key={portHandle.name}>
<Handle <Handle
type="source" type="source"
position={handlePositionMap[portDirectionMap.get(portHandle.name) || portHandle.position]} position={handlePositionMap[effectiveDir]}
id={portHandle.name} id={portHandle.name}
title={portHandle.name} title={portHandle.name}
style={{ ...baseHandleStyle, ...portHandle.style, zIndex: 10, pointerEvents: 'all' }} style={{ ...baseHandleStyle, ...portHandle.style, zIndex: 10, pointerEvents: 'all' }}
/> />
<Handle <Handle
type="target" type="target"
position={handlePositionMap[portDirectionMap.get(portHandle.name) || portHandle.position]} position={handlePositionMap[effectiveDir]}
id={portHandle.name} id={portHandle.name}
title={portHandle.name} title={portHandle.name}
style={{ ...baseHandleStyle, ...portHandle.style, zIndex: 5, pointerEvents: 'all' }} style={{ ...baseHandleStyle, ...portHandle.style, zIndex: 5, pointerEvents: 'all' }}
/> />
</React.Fragment> </React.Fragment>
))} );
})}
{portHandles.map((portHandle) => ( {portHandles.map((portHandle) => (
<span key={`label-${portHandle.name}`} className="port-name-label" style={portLabelStyle(portHandle)} title={portHandle.name}> <span key={`label-${portHandle.name}`} className="port-name-label" style={portLabelStyle(portHandle)} title={portHandle.name}>
{portHandle.name} {portHandle.name}
@@ -4127,8 +4144,10 @@ Organization : OptiHK Limited
const targetEndpoint = `${edge.target}:${edge.targetHandle || ''}`; const targetEndpoint = `${edge.target}:${edge.targetHandle || ''}`;
const key = [sourceEndpoint, targetEndpoint].sort().join('<>'); const key = [sourceEndpoint, targetEndpoint].sort().join('<>');
const group = groups.get(key) || []; const group = groups.get(key) || [];
const sourceDirection = getAnchorHandleRouteDirection(nodeMap[edge.source], edge.sourceHandle); const sourceDirection = getAnchorHandleRouteDirection(nodeMap[edge.source], edge.sourceHandle)
const targetDirection = getAnchorHandleRouteDirection(nodeMap[edge.target], edge.targetHandle); || getRotatableNodeHandleDirection(nodeMap[edge.source], edge.sourceHandle);
const targetDirection = getAnchorHandleRouteDirection(nodeMap[edge.target], edge.targetHandle)
|| getRotatableNodeHandleDirection(nodeMap[edge.target], edge.targetHandle);
const usesAnchorDirection = Boolean(sourceDirection || targetDirection); const usesAnchorDirection = Boolean(sourceDirection || targetDirection);
const hasRoutePoints = edge.data && Array.isArray(edge.data.points) && edge.data.points.length >= 2; const hasRoutePoints = edge.data && Array.isArray(edge.data.points) && edge.data.points.length >= 2;
const directionalEdge = usesAnchorDirection const directionalEdge = usesAnchorDirection
@@ -4151,7 +4170,7 @@ Organization : OptiHK Limited
}; };
}); });
return [...separatedEdges, ...rulerEdges]; return [...separatedEdges, ...rulerEdges];
}, [currentEdges, currentNodes, getAnchorHandleRouteDirection, rulerEdges]); }, [currentEdges, currentNodes, getAnchorHandleRouteDirection, getRotatableNodeHandleDirection, rulerEdges]);
const [projectCompositeMap, setProjectCompositeMap] = useState({}); const [projectCompositeMap, setProjectCompositeMap] = useState({});
const [standaloneComposites, setStandaloneComposites] = useState([]); const [standaloneComposites, setStandaloneComposites] = useState([]);
@@ -6130,6 +6149,7 @@ Organization : OptiHK Limited
const route = currentLinkRoute; const route = currentLinkRoute;
const view = routeStyleForSettings(route, false); const view = routeStyleForSettings(route, false);
const edgeId = `edge-${connection.source}-${connection.sourceHandle}-${connection.target}-${connection.targetHandle}-${Date.now()}`; const edgeId = `edge-${connection.source}-${connection.sourceHandle}-${connection.target}-${connection.targetHandle}-${Date.now()}`;
const nodeMap = Object.fromEntries(activePage.nodes.map(node => [node.id, node]));
const candidate = { const candidate = {
id: edgeId, id: edgeId,
source: connection.source, source: connection.source,
@@ -6139,9 +6159,8 @@ Organization : OptiHK Limited
type: view.type, type: view.type,
selectable: true, selectable: true,
style: view.style, style: view.style,
data: { route } data: { route },
}; };
const nodeMap = Object.fromEntries(activePage.nodes.map(node => [node.id, node]));
const conflict = findSameTypeRouteCrossing(candidate, activePage.edges, nodeMap, technologyManifest); const conflict = findSameTypeRouteCrossing(candidate, activePage.edges, nodeMap, technologyManifest);
if (conflict) { if (conflict) {
const source = nodeMap[conflict.conflictEdge.source]?.data?.componentDisplayName || conflict.conflictEdge.source; const source = nodeMap[conflict.conflictEdge.source]?.data?.componentDisplayName || conflict.conflictEdge.source;
@@ -6155,7 +6174,7 @@ Organization : OptiHK Limited
: p : p
))); )));
addLog(`Connected ${connection.sourceHandle} to ${connection.targetHandle}.`); addLog(`Connected ${connection.sourceHandle} to ${connection.targetHandle}.`);
}, [activePageId, activePage, rulerMode, currentLinkRoute, technologyManifest, addLog]); }, [activePageId, activePage, rulerMode, currentLinkRoute, technologyManifest, addLog, getAnchorHandleRouteDirection]);
// Select custom route edges from their SVG hit target. // Select custom route edges from their SVG hit target.
const handleRouteEdgeMouseDown = useCallback((event) => { const handleRouteEdgeMouseDown = useCallback((event) => {