Skip to content

Commit 3ce42df

Browse files
committed
Major additions
1 parent 3ec3f2e commit 3ce42df

76 files changed

Lines changed: 6703 additions & 1272 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.husky/pre-commit

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
#!/bin/sh
1+
# For now, no pre-commit hooks are set up.
2+
# #!/bin/sh
23

3-
# Re-invoke this script inside WSL if not already running in WSL
4-
if [ -z "$WSL_DISTRO_NAME" ]; then
5-
exec wsl bash "$0" "$@"
6-
fi
4+
# # Re-invoke this script inside WSL if not already running in WSL
5+
# if [ -z "$WSL_DISTRO_NAME" ]; then
6+
# exec wsl bash "$0" "$@"
7+
# fi
78

8-
# Your actual pre-commit commands below
9-
pnpm run build:changelog
9+
# # Your actual pre-commit commands below
10+
# pnpm run build:changelog

apps/website/app/ClientLayoutShell.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { TopNavBar } from 'src/ui/topNavBar'
66

77
export function ClientLayoutShell({ children }: { children: React.ReactNode }) {
88
const pathname = usePathname()
9-
const showTopNavBar = !pathname.startsWith('/meepstudio')
9+
const showTopNavBar = false
1010

1111
return (
1212
<ClientThemeProvider attribute="class" defaultTheme="system" enableSystem>

apps/website/app/page.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
// Import logger
22
import { logger } from "../packages/utils/logger";
3+
import StudioLayout from "packages/ui/StudioLayout";
34

45
export default function HomePage() {
56
// Logging
67
logger.trace('Calling HomePage');
78

89
// Return the JSX
910
return (
10-
<div className="w-auto h-auto">
11-
11+
<div className="w-full h-full flex">
12+
<div className="flex-1 flex flex-col">
13+
<StudioLayout ghPages={true} />
14+
</div>
1215
</div>
1316
);
1417
}

apps/website/packages/canvas/core/CanvasElement.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -147,16 +147,17 @@ export const CanvasElementComponent: React.FC<CanvasElementProps> = ({
147147
x={element.pos.x * GRID_PX}
148148
y={element.pos.y * GRID_PX}
149149
rotation={(element.orientation || 0) * 180 / Math.PI}
150-
draggable
151-
onDragStart={handleDragStart}
152-
onDragMove={handleDragMove}
153-
onDragEnd={handleDragEnd}
154-
onClick={(e) => {
150+
draggable={!element.locked} // Locked objects cannot be dragged
151+
onDragStart={element.locked ? undefined : handleDragStart}
152+
onDragMove={element.locked ? undefined : handleDragMove}
153+
onDragEnd={element.locked ? undefined : handleDragEnd}
154+
onClick={element.locked ? undefined : (e) => {
155155
e.cancelBubble = true;
156156
onSelect(element.id, {
157157
shift: e.evt?.shiftKey || e.evt?.ctrlKey || e.evt?.metaKey || false,
158158
});
159159
}}
160+
listening={!element.locked} // Locked objects don't listen to events
160161
>
161162
{/* shape + resize handles together – single transform applied */}
162163
{children}

apps/website/packages/canvas/core/SelectionController.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ export class SelectionController {
4848
return this.pointInBox(elem.pos, box);
4949
case 'lattice':
5050
return this.pointInBox(elem.pos, box);
51+
case 'fluxRegion':
52+
return this.regionIntersectsBox(elem, box);
5153
case 'pmlBoundary':
5254
// Boundaries are handled differently - they don't have a position
5355
return false;
@@ -161,4 +163,38 @@ export class SelectionController {
161163
// Simplified for brevity - would check if triangle edges cross box edges
162164
return false;
163165
}
166+
167+
private static regionIntersectsBox(
168+
region: any,
169+
box: { x: number; y: number; width: number; height: number }
170+
): boolean {
171+
const sizeX = region.size?.x || 0;
172+
const sizeY = region.size?.y || 0;
173+
174+
// Determine region type
175+
if (sizeX === 0 && sizeY === 0) {
176+
// Point region
177+
return this.pointInBox(region.pos, box);
178+
} else if (sizeX === 0 || sizeY === 0) {
179+
// Line region - treat as a rectangle with very small width/height
180+
const rx = region.pos.x - sizeX / 2;
181+
const ry = region.pos.y - sizeY / 2;
182+
return (
183+
rx < box.x + box.width &&
184+
rx + sizeX > box.x &&
185+
ry < box.y + box.height &&
186+
ry + sizeY > box.y
187+
);
188+
} else {
189+
// Area region - treat as rectangle
190+
const rx = region.pos.x - sizeX / 2;
191+
const ry = region.pos.y - sizeY / 2;
192+
return (
193+
rx < box.x + box.width &&
194+
rx + sizeX > box.x &&
195+
ry < box.y + box.height &&
196+
ry + sizeY > box.y
197+
);
198+
}
199+
}
164200
}

apps/website/packages/canvas/layers/ElementLayer.tsx

Lines changed: 85 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import { GeometryRenderer } from '../renderers/GeometryRenderer';
66
import { SourceRenderer } from '../renderers/SourceRenderer';
77
import { BoundaryRenderer } from '../renderers/BoundaryRenderer';
88
import { LatticeRenderer } from '../renderers/LatticeRenderer';
9+
import { RegionRenderer } from '../renderers/RegionRenderer';
10+
import { RegionBoxRenderer } from '../renderers/RegionBoxRenderer';
911
import { ResizeHandles } from '../utils/resizeHandles';
1012
import { useCanvasStore } from '../../stores/canvas';
1113

@@ -47,7 +49,7 @@ export const ElementLayer: React.FC<ElementLayerProps> = ({
4749

4850
// Sort elements by type for proper layering
4951
const sortedElements = React.useMemo(() => {
50-
const order = ['lattice', 'cylinder', 'rectangle', 'triangle', 'source', 'boundary'];
52+
const order = ['lattice', 'cylinder', 'rectangle', 'triangle', 'source', 'region', 'boundary'];
5153
return [...elements].sort((a, b) => {
5254
const aIndex = order.indexOf(getElementCategory(a));
5355
const bIndex = order.indexOf(getElementCategory(b));
@@ -57,7 +59,9 @@ export const ElementLayer: React.FC<ElementLayerProps> = ({
5759

5860
return (
5961
<Layer>
60-
{sortedElements.map((element) => {
62+
{sortedElements
63+
.filter(element => !element.invisible) // Don't render invisible elements
64+
.map((element) => {
6165
const isSelected = selectedIds.includes(element.id);
6266

6367
// Boundaries don't need CanvasElementComponent wrapper since they don't have position/drag behavior
@@ -135,6 +139,8 @@ export const ElementLayer: React.FC<ElementLayerProps> = ({
135139
function getElementCategory(element: CanvasElement): string {
136140
if (element.type === 'lattice') return 'lattice';
137141
if (element.type === 'pmlBoundary') return 'boundary';
142+
if (element.type === 'fluxRegion') return 'region';
143+
if (element.type === 'regionBox') return 'region';
138144
if ('component' in element) return 'source';
139145
return element.type;
140146
}
@@ -246,6 +252,83 @@ function renderElement(
246252
);
247253
}
248254

255+
if (element.type === 'fluxRegion') {
256+
return (
257+
<RegionRenderer
258+
region={element as any}
259+
isSelected={isSelected}
260+
scale={scale}
261+
GRID_PX={GRID_PX}
262+
/>
263+
);
264+
}
265+
266+
if (element.type === 'regionBox') {
267+
return (
268+
<RegionBoxRenderer
269+
regionBox={element as any}
270+
isSelected={isSelected}
271+
scale={scale}
272+
GRID_PX={GRID_PX}
273+
/>
274+
);
275+
}
276+
277+
if (element.type === 'regionBox') {
278+
// For region boxes with resize handles, we return a special structure when selected
279+
if (isSelected && !element.locked && selectedIds?.length === 1) {
280+
return (
281+
<>
282+
<RegionBoxRenderer
283+
regionBox={element as any}
284+
isSelected={isSelected}
285+
scale={scale}
286+
GRID_PX={GRID_PX}
287+
/>
288+
<ResizeHandles
289+
rectangle={element as any}
290+
GRID_PX={GRID_PX}
291+
scale={scale}
292+
onResize={(updates: Partial<CanvasElement>) => onUpdate(element.id, updates)}
293+
onResizeEnd={(updates: Partial<CanvasElement>) => onCommit(element.id, updates)}
294+
snapToGrid={(value: number, forceGrid?: boolean, forceResolution?: boolean) => {
295+
const res = resolution;
296+
if (forceResolution && res && res > 1) {
297+
const cellSize = 1 / res;
298+
return Math.round(value / cellSize) * cellSize;
299+
} else if (forceGrid) {
300+
return Math.round(value);
301+
} else if (resolutionSnapping && res && res > 1) {
302+
const cellSize = 1 / res;
303+
return Math.round(value / cellSize) * cellSize;
304+
} else if (gridSnapping) {
305+
return Math.round(value);
306+
}
307+
return value;
308+
}}
309+
gridSnapping={gridSnapping || false}
310+
resolutionSnapping={resolutionSnapping || false}
311+
showGrid={showGrid || false}
312+
showResolutionOverlay={showResolutionOverlay || false}
313+
toggleShowGrid={toggleShowGrid || (() => {})}
314+
toggleShowResolutionOverlay={toggleShowResolutionOverlay || (() => {})}
315+
handleUpdateGeometry={(id: string, updates: Partial<CanvasElement>) => onCommit(id, updates)}
316+
setActiveInstructionSet={(key: string) => onSetActiveInstructionSet?.(key)}
317+
/>
318+
</>
319+
);
320+
}
321+
322+
return (
323+
<RegionBoxRenderer
324+
regionBox={element as any}
325+
isSelected={isSelected}
326+
scale={scale}
327+
GRID_PX={GRID_PX}
328+
/>
329+
);
330+
}
331+
249332
if (element.type === 'lattice') {
250333
return (
251334
<LatticeRenderer

apps/website/packages/canvas/renderers/BoundaryRenderer.tsx

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ import { CanvasPMLBoundary } from '../../types/meepBoundaryTypes';
55
import { useCanvasStore } from '../../stores/canvas';
66

77
const PARAM_SET_COLORS: Record<number, string> = {
8-
0: '#1e2939',
9-
1: '#392e1e',
10-
2: '#211e39',
11-
3: '#36391e'
8+
0: '#2d4b6b', // lighter dark blue for better contrast
9+
1: '#8B4513', // saddle brown - vintage leather
10+
2: '#DC143C', // crimson red - vintage ruby
11+
3: '#DAA520' // goldenrod - vintage brass
1212
};
1313

1414
interface BoundaryRendererProps {
@@ -47,9 +47,7 @@ export const BoundaryRenderer: React.FC<BoundaryRendererProps> = ({
4747
// Helper to get effective assignment
4848
const getEffectiveAssignment = (edge: 'top' | 'bottom' | 'left' | 'right') => {
4949
const explicit = fullBoundary.edgeAssignments?.[edge];
50-
if (explicit !== undefined) return explicit;
51-
if (fullBoundary.parameterSets?.[0]?.active) return 0;
52-
return undefined;
50+
return explicit; // Only return explicit assignments, no fallback
5351
};
5452

5553
// Helper to get edge color
@@ -85,10 +83,7 @@ export const BoundaryRenderer: React.FC<BoundaryRendererProps> = ({
8583
if (assignment !== undefined && fullBoundary.parameterSets?.[assignment]?.active) {
8684
return true;
8785
}
88-
if (Object.keys(fullBoundary.edgeAssignments).length === 0 && fullBoundary.parameterSets?.[0]?.active) {
89-
return true;
90-
}
91-
return false;
86+
return false; // No fallback - only render explicit assignments
9287
}
9388

9489
// Legacy direction system
@@ -110,11 +105,7 @@ export const BoundaryRenderer: React.FC<BoundaryRendererProps> = ({
110105
if (assignment !== undefined && fullBoundary.parameterSets?.[assignment]) {
111106
return (fullBoundary.parameterSets[assignment].thickness || 1) * GRID_PX;
112107
}
113-
if (!fullBoundary.edgeAssignments || Object.keys(fullBoundary.edgeAssignments).length === 0) {
114-
if (fullBoundary.parameterSets?.[0]?.active) {
115-
return (fullBoundary.parameterSets[0].thickness || 1) * GRID_PX;
116-
}
117-
}
108+
// Fallback to boundary thickness only if no edge assignments system is used
118109
return (fullBoundary.thickness || 1) * GRID_PX;
119110
};
120111

apps/website/packages/canvas/renderers/GeometryRenderer.tsx

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -80,14 +80,49 @@ export const GeometryRenderer: React.FC<GeometryRendererProps> = ({
8080
);
8181

8282
case 'triangle':
83-
return (
84-
<Line
85-
points={geometry.vertices.flatMap((v: any) => [v.x * GRID_PX, v.y * GRID_PX])}
86-
closed
87-
fill={fillColor}
88-
stroke={strokeColor}
89-
strokeWidth={strokeWidth}
90-
/>
91-
);
83+
if (isSelected) {
84+
// Render triangle with colored edges when selected
85+
const vertices = geometry.vertices;
86+
const vertexColors = ['#a855f7', '#ec4899', '#f59e0b']; // purple, pink, amber
87+
88+
return (
89+
<Group>
90+
{/* Fill triangle */}
91+
<Line
92+
points={vertices.flatMap((v: any) => [v.x * GRID_PX, v.y * GRID_PX])}
93+
closed
94+
fill={fillColor}
95+
stroke="transparent"
96+
strokeWidth={0}
97+
/>
98+
{/* Colored edges */}
99+
{vertices.map((vertex: any, i: number) => {
100+
const nextVertex = vertices[(i + 1) % vertices.length];
101+
return (
102+
<Line
103+
key={i}
104+
points={[
105+
vertex.x * GRID_PX, vertex.y * GRID_PX,
106+
nextVertex.x * GRID_PX, nextVertex.y * GRID_PX
107+
]}
108+
stroke={vertexColors[i]}
109+
strokeWidth={Math.max(2, strokeWidth)}
110+
/>
111+
);
112+
})}
113+
</Group>
114+
);
115+
} else {
116+
// Normal triangle rendering when not selected
117+
return (
118+
<Line
119+
points={geometry.vertices.flatMap((v: any) => [v.x * GRID_PX, v.y * GRID_PX])}
120+
closed
121+
fill={fillColor}
122+
stroke={strokeColor}
123+
strokeWidth={strokeWidth}
124+
/>
125+
);
126+
}
92127
}
93128
};

0 commit comments

Comments
 (0)