Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 16 additions & 27 deletions src/pages/edit/Editor/components/NodePinPropertyEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { CCConnectionStore } from "../../../../store/connection";
import { IntrinsicComponentDefinition } from "../../../../store/intrinsics/base";
import { CCNodePinStore } from "../../../../store/nodePin";
import { useStore } from "../../../../store/react";
import getCCComponentEditorRendererNodeGeometry from "../renderer/Node/geometry";
import { getCCComponentEditorRendererNodeGeometry } from "../renderer/Node/geometry";
import { useComponentEditorStore } from "../store";

export function CCComponentEditorNodePinPropertyEditor() {
Expand Down Expand Up @@ -123,33 +123,22 @@ export function CCComponentEditorNodePinPropertyEditor() {
nodePin.id,
);
for (const connection of connections) {
const anotherNodePinId =
connection.from === nodePin.id
? connection.to
: connection.from;
const fromNodePinId =
connection.from === nodePin.id
? nodePin.id
: anotherNodePinId;
const toNodePinId =
connection.from === nodePin.id
? anotherNodePinId
: nodePin.id;
const from = connection.from;
const to = connection.to;
const parentComponentId = connection.parentComponentId;
store.connections.unregister([connection.id]);
if (
store.nodePins.isConnectable(nodePin.id, anotherNodePinId)
) {
// reconnect if still connectable after bit width change
store.connections.register(
CCConnectionStore.create({
parentComponentId,
from: fromNodePinId,
to: toNodePinId,
bentPortion: 0.5,
}),
);
}
store.connections.unregister([connection.id]).then(() => {
if (store.nodePins.isConnectable(from, to)) {
// reconnect if still connectable after bit width change
store.connections.register(
CCConnectionStore.create({
parentComponentId,
from,
to,
bentPortion: 0.5,
}),
);
}
});
}
}
continue;
Expand Down
3 changes: 3 additions & 0 deletions src/pages/edit/Editor/components/ViewModeSwitcher.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { Edit, PlayArrow } from "@mui/icons-material";
import { Fab } from "@mui/material";
import { useCanSimulate } from "../../../../store/react/selectors";
import { useComponentEditorStore } from "../store";

export default function CCComponentEditorViewModeSwitcher() {
const componentEditorState = useComponentEditorStore()();
const canSimulate = useCanSimulate(componentEditorState.componentId);

return (
<Fab
Expand All @@ -15,6 +17,7 @@ export default function CCComponentEditorViewModeSwitcher() {
);
componentEditorState.setTimeStep(0);
}}
disabled={!canSimulate}
>
{componentEditorState.editorMode === "edit" ? <PlayArrow /> : <Edit />}
</Fab>
Expand Down
2 changes: 1 addition & 1 deletion src/pages/edit/Editor/renderer/ComponentPin/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useStore } from "../../../../../store/react";
import { wrappingIncrementSimulationValue } from "../../../../../store/simulation";
import { useComponentEditorStore } from "../../store";
import { stringifySimulationValue } from "../../store/slices/core";
import getCCComponentEditorRendererNodeGeometry from "./../Node/geometry";
import { getCCComponentEditorRendererNodeGeometry } from "./../Node/geometry";
export type CCComponentEditorRendererComponentPinProps = {
nodePinId: CCNodePinId;
};
Expand Down
2 changes: 1 addition & 1 deletion src/pages/edit/Editor/renderer/Connection/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import ensureStoreItem from "../../../../../store/react/error";
import { useNode } from "../../../../../store/react/selectors";
import { useComponentEditorStore } from "../../store";
import { stringifySimulationValue } from "../../store/slices/core/index";
import getCCComponentEditorRendererNodeGeometry from "../Node/geometry";
import { getCCComponentEditorRendererNodeGeometry } from "../Node/geometry";

export type CCComponentEditorRendererConnectionEndpoint = {
direction: CCComponentPinType;
Expand Down
71 changes: 31 additions & 40 deletions src/pages/edit/Editor/renderer/Node/components/Default/geometry.ts
Original file line number Diff line number Diff line change
@@ -1,50 +1,41 @@
import { type Vector2, vector2 } from "../../../../../../../common/vector2";
import type { Vector2 } from "../../../../../../../common/vector2";
import type { CCNodePinId } from "../../../../../../../store/nodePin";
import type {
CCComponentEditorRendererNodeGeometryCalculator,
CCComponentEditorRendererNodeGeometrySource,
CCComponentEditorRendererNodeLayout,
CCComponentEditorRendererNodeLayoutSource,
} from "../../types";

const width = 100;
const gapY = 20;
const paddingY = 15;

export const ccComponentRendererNodeDefaultGeometryCalculator: CCComponentEditorRendererNodeGeometryCalculator =
(source: CCComponentEditorRendererNodeGeometrySource) => {
const size: Vector2 = {
x: width,
y:
gapY *
Math.max(
source.inputNodePinIds.length,
source.outputNodePinIds.length,
) +
paddingY * 2,
};
export function ccComponentRendererNodeDefaultLayoutCalculator(
source: CCComponentEditorRendererNodeLayoutSource,
): CCComponentEditorRendererNodeLayout {
const size: Vector2 = {
x: width,
y:
gapY *
Math.max(
source.inputNodePinIds.length,
source.outputNodePinIds.length,
) +
paddingY * 2,
};

const nodePinPositionById = new Map<CCNodePinId, Vector2>();
for (const [index, nodePinId] of source.inputNodePinIds.entries()) {
nodePinPositionById.set(nodePinId, {
x: source.position.x - size.x / 2,
y:
source.position.y +
gapY * (index - source.inputNodePinIds.length / 2 + 0.5),
});
}
for (const [index, nodePinId] of source.outputNodePinIds.entries()) {
nodePinPositionById.set(nodePinId, {
x: source.position.x + size.x / 2,
y:
source.position.y +
gapY * (index - source.outputNodePinIds.length / 2 + 0.5),
});
}
const nodePinOffsetById = new Map<CCNodePinId, Vector2>();

return {
rect: {
position: vector2.sub(source.position, vector2.div(size, 2)),
size,
},
nodePinPositionById,
};
};
const startYIn =
size.y / 2 - (gapY * (source.inputNodePinIds.length - 1)) / 2;
for (const [index, pinId] of source.inputNodePinIds.entries()) {
nodePinOffsetById.set(pinId, { x: 0, y: startYIn + gapY * index });
}

const startYOut =
size.y / 2 - (gapY * (source.outputNodePinIds.length - 1)) / 2;
for (const [index, pinId] of source.outputNodePinIds.entries()) {
nodePinOffsetById.set(pinId, { x: size.x, y: startYOut + gapY * index });
}

return { size, nodePinOffsetById };
}
12 changes: 6 additions & 6 deletions src/pages/edit/Editor/renderer/Node/components/Default/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,18 @@ export function CCComponentEditorRendererNodeDefaultRenderer(
<>
<text
fill={theme.palette.textPrimary}
x={props.geometry.rect.position.x}
y={props.geometry.rect.position.y - 5}
x={0}
y={-5}
textAnchor="start"
fontSize={12}
>
{props.component.name}
</text>
<rect
x={props.geometry.rect.position.x}
y={props.geometry.rect.position.y}
width={props.geometry.rect.size.x}
height={props.geometry.rect.size.y}
x={0}
y={0}
width="100%"
height="100%"
fill={theme.palette.white}
stroke={
props.nodeState.isSelected
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { SettingsOutlined } from "@mui/icons-material";
import {
Button,
FormLabel,
IconButton,
Popover,
Stack,
TextField,
Typography,
} from "@mui/material";
import { useState } from "react";
import type { CCIntrinsicComponentDisplaySpec } from "../../../../../../../store/intrinsics/types";

type Props = {
config: CCIntrinsicComponentDisplaySpec["config"];
onConfigChange: (config: CCIntrinsicComponentDisplaySpec["config"]) => void;
};

export function CCComponentEditorRendererNodeDisplayRendererConfigSettingButton(
props: Props,
) {
const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);
const [newConfig, setNewConfig] = useState(props.config);

return (
<>
<IconButton
sx={{ width: "20px", height: "20px" }}
// Prevent the drag behavior of the parent node when clicking the button
onPointerDown={(e) => e.stopPropagation()}
onClick={(e) => {
e.stopPropagation();
setAnchorEl(e.currentTarget);
setNewConfig(props.config);
}}
>
<SettingsOutlined sx={{ width: "12px", height: "12px" }} />
</IconButton>
<Popover
anchorEl={anchorEl}
open={Boolean(anchorEl)}
onClose={() => setAnchorEl(null)}
slotProps={{
root: { onPointerDown: (e) => e.stopPropagation() },
paper: { sx: { width: "300px", p: 2 } },
}}
>
<form
onSubmit={(e) => {
e.preventDefault();
props.onConfigChange(newConfig);
setAnchorEl(null);
}}
>
<Typography variant="h6" gutterBottom>
Display Settings
</Typography>
<FormLabel component="div" sx={{ mb: 0.5 }}>
Resolution
</FormLabel>
<Stack direction="row" alignItems="center" gap={1}>
<TextField
value={newConfig.resolution.x || ""}
onChange={(e) =>
setNewConfig((prev) => ({
...prev,
resolution: {
...prev.resolution,
x: parseInt(e.target.value, 10) || 0,
},
}))
}
size="small"
sx={{ flex: 1 }}
slotProps={{
htmlInput: { inputMode: "numeric", sx: { textAlign: "end" } },
}}
/>
<Typography component="span" variant="body1">
x
</Typography>
<TextField
value={newConfig.resolution.y || ""}
onChange={(e) =>
setNewConfig((prev) => ({
...prev,
resolution: {
...prev.resolution,
y: parseInt(e.target.value, 10) || 0,
},
}))
}
size="small"
sx={{ flex: 1 }}
slotProps={{
htmlInput: { inputMode: "numeric", sx: { textAlign: "end" } },
}}
/>
</Stack>
<Stack direction="row" justifyContent="flex-end" mt={2} gap={1}>
<Button
type="button"
variant="outlined"
size="small"
onClick={() => setAnchorEl(null)}
>
Cancel
</Button>
<Button type="submit" variant="contained" size="small">
Apply
</Button>
</Stack>
</form>
</Popover>
</>
);
}
51 changes: 26 additions & 25 deletions src/pages/edit/Editor/renderer/Node/components/Display/geometry.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,34 @@
import nullthrows from "nullthrows";
import { type Vector2, vector2 } from "../../../../../../../common/vector2";
import type { CCIntrinsicComponentDisplaySpec } from "../../../../../../../store/intrinsics/types";
import type { CCNodePinId } from "../../../../../../../store/nodePin";
import type {
CCComponentEditorRendererNodeGeometryCalculator,
CCComponentEditorRendererNodeGeometrySource,
CCComponentEditorRendererNodeLayout,
CCComponentEditorRendererNodeLayoutSource,
} from "../../types";

const width = 320;
const height = 200;
export const ccComponentEditorRendererNodeDisplayLayoutConstants = {
padding: 8,
gridSize: 12,
gridSizeDisplayWidth: 60,
};

export const ccComponentRendererNodeDisplayGeometryCalculator: CCComponentEditorRendererNodeGeometryCalculator =
(source: CCComponentEditorRendererNodeGeometrySource) => {
const size: Vector2 = {
x: width,
y: height,
};
export function ccComponentRendererNodeDisplayLayoutCalculator(
source: CCComponentEditorRendererNodeLayoutSource,
): CCComponentEditorRendererNodeLayout {
const { padding, gridSize, gridSizeDisplayWidth } =
ccComponentEditorRendererNodeDisplayLayoutConstants;
const config = source.config as CCIntrinsicComponentDisplaySpec["config"];

return {
rect: {
position: vector2.sub(source.position, vector2.div(size, 2)),
size,
},
nodePinPositionById: new Map<CCNodePinId, Vector2>(
source.inputNodePinIds.map(
(id) =>
[
id,
vector2.create(source.position.x - size.x / 2, source.position.y),
] as const,
),
),
};
const size = {
x: gridSizeDisplayWidth + gridSize * config.resolution.x + padding * 2,
y: gridSize * config.resolution.y + padding * 2,
};

return {
size,
nodePinOffsetById: new Map<CCNodePinId, Vector2>([
[nullthrows(source.inputNodePinIds[0]), vector2.create(0, size.y / 2)],
]),
};
}
Loading
Loading