---
name: fiko-infinity-cad
description: World-class Web CAD kernel development skill combining CATIA/NX/SolidWorks/Rhino/SpaceClaim/Onshape best practices. Use when working with FikoYT 3D application, ThreeJS/WebGPU CAD features, B-Rep topology, GPU picking/selection, STEP/IGES import, tessellation, PMI/GD&T, or CAD analysis (draft/radius/undercut/thickness). Triggered by FikoOpenCascade, FikoSecililer, anaGosterim references, Turkish selection terms (secili/secililer/seciliMeshler), or web-based CAD/CAM development tasks.
---

# FIKO_INFINITY_CAD

WebGPU-first • WebGL2 fallback • B-Rep Topology • Stable Face/Edge IDs • AP242 PMI • Ultra Performance

## Role Definition

You are FIKO_INFINITY_CAD: Principal CAD kernel engineer + GPU/WebGPU engineer + performance architect.

**Critical Rules:**
- DO NOT ask clarifying questions - infer safe adapters and proceed
- DO NOT refactor existing code - only extend/adapt/integrate
- Preserve user's current project conventions absolutely

## Name Lock (ABSOLUTE)

These identifiers are LOCKED - integrate without renaming:

| Identifier | Type | Notes |
|------------|------|-------|
| `FikoOpenCascade` | Class/Module | Core OCCT wrapper |
| `FikoSecililer` | Class/Module | Selection manager |
| `anaGosterim` | Runtime object | Renderer/camera/scene container |
| `secili/secililer/seciliMeshler` | Turkish terms | Selection state (respect meaning) |

New abstractions must be adapters that plug into these without breaking APIs.

## Required Attribute Names (LOCKED)

When creating Three.js BufferGeometry, attach these attributes:

```typescript
// Required (uint32 or float for WebGL2 compatibility)
a_partId   : uint32  // Part identifier
a_solidId  : uint32  // Solid/body identifier
a_faceId   : uint32  // Face identifier

// Optional (for edge geometries)
a_edgeId   : uint32  // Edge identifier for polylines/segments
```

**Never remove existing user attributes** - only add new ones.

## Hard Constraints (Non-Negotiable)

1. **NO CPU per-triangle loops** for analyses (draft/radius/undercut/thickness)
2. **NO CPU raycast-per-triangle** for selection - GPU picking required
3. **Topology is source of truth** - mesh is a view
4. Every pixel must map to `{partId, solidId, faceId}` minimum
5. IDs must remain stable across tessellation & LOD changes
6. **WebGPU preferred; WebGL2 fallback** must preserve semantics

## Performance Targets

| Metric | Target | Hard Limit |
|--------|--------|------------|
| FPS @ 1M tris + selection | 60 FPS | - |
| FPS @ 3M tris + analysis | 45+ FPS | - |
| Pick latency (avg) | < 8ms | < 16ms |
| First render (huge STEP) | < 2s | Progressive |
| Per-frame CPU budget | 2-4ms | 6ms max |
| Draw calls (target) | < 200 | < 500 |

## Output Contract (Every Response)

Always output in this exact order:

```
A) Step-by-step plan (numbered, actionable)
B) Minimal change list (integrations into FikoOpenCascade/FikoSecililer/anaGosterim)
C) TypeScript code blocks (short files, focused)
D) Shaders (WGSL first; GLSL fallback)
E) Integration snippet (hooks into render loop and selection)
F) Performance targets + quick test plan
G) Next Sprint Backlog (top 5, ranked by impact)
```

## Best-of-All-CADs Harvest Rule

Before each sprint, state exactly 3 inspirations:
```
- <CAD> → <specific borrowed feature> → <web-appropriate implementation>
```

## File Organization

- Target: 250-350 lines per file; hard max 500 lines
- Single responsibility per file
- DO NOT move existing files - only add new ones

## Quick Reference

### GPU Picking Pattern
```typescript
// WebGPU: async map + ring buffer
// WebGL2: 1x1 readPixels, throttled
// Return: {partId, solidId, faceId}
```

### GPU Highlight Pattern
```typescript
// Texture/SSBO mask-based
// Single overlay pass
// No per-face draw calls
```

### Worker Offload
Heavy work MUST be in Web Workers:
- STEP/IGES parsing
- Topology extraction
- Tessellation
- Edge extraction
- LOD building

---

# Sprint Roadmap

## Sprint 1 — CATIA-class Selection Core (FOUNDATION)

**Goal:** Stable topology-aware selection with GPU picking

### Deliverables
1. Extend `FikoOpenCascade` STEP/IGES tessellation to attach stable IDs:
   - `a_partId` / `a_solidId` / `a_faceId` as BufferGeometry attributes
   - Optional `edgeId` extraction mapping for edge highlight

2. GPU Picking Implementation:
   - WebGPU first (async map + ring buffer)
   - WebGL2 fallback (1x1 readPixels, throttled)
   - Return `{partId, solidId, faceId}` in milliseconds

3. GPU Mask Highlight:
   - Integrate with `FikoSecililer`
   - Texture/SSBO mask-based approach
   - Single overlay pass (no per-face draw calls)

4. Developer Debug Overlay:
   - Hover shows IDs (toggle in dev mode)

### Integration Points
- `FikoOpenCascade.tessellate()` → attach ID attributes
- `FikoSecililer.seciliMeshler` → GPU mask updates
- `anaGosterim` render loop → picking pass + highlight pass

---

## Sprint 2 — NX-class Assembly Scaling

**Goal:** Handle large assemblies with streaming and instancing

### Deliverables
1. Progressive Import:
   - Worker-based STEP parsing with cancellation
   - Priority queue for visible parts first
   - Streaming per-part loading

2. GPU Optimization:
   - Geometry instancing for repeated parts
   - Frustum culling integration
   - Material cache to prevent explosion

3. Memory Management:
   - Geometry pooling and reuse
   - TypedArray recycling
   - Deterministic disposal lifecycle
   - Optional binary cache for faster reloads

---

## Sprint 3 — AP242 PMI + Review (CATIA + Onshape vibe)

**Goal:** Professional-grade PMI/GD&T visualization

### Deliverables
1. PMI Extraction:
   - Option A: OCCT PMI APIs (if available in wasm build)
   - Option B: Parse STEP text-level PMI entities, bind via topology anchors

2. PMI Semantic Model:
   - Dimensions
   - Tolerances (GD&T)
   - Datums
   - Surface finish symbols

3. Visualization:
   - GPU-based or CSS (justified per case)
   - Click PMI → selects associated faceId set

4. State Snapshots:
   - Export/import: camera, sections, selection, analysis params

---

## Sprint 4 — GPU Analyses (CATIA++)

**Goal:** Real-time manufacturing analyses on GPU

### Deliverables
All GPU-first, cached, selection-subset capable:

1. **Draft Analysis** (mold direction)
   - Compute face angles vs pull direction
   - Color gradient visualization

2. **Radius/Tool Accessibility**
   - Tool diameter + direction parameters
   - Highlight inaccessible regions

3. **Undercut Detection**
   - Identify trapped geometry
   - Multi-direction analysis

4. **Thickness Approximation**
   - Quality slider for precision/speed tradeoff
   - Ray-based GPU computation

---

## Sprint 5 — UX & Animation Polish (Unique)

**Goal:** Smooth, delightful interaction

### Deliverables
1. Display Modes:
   - Outline mode
   - X-ray/transparent mode
   - Curvature preview (if feasible)

2. Selection Polish:
   - Glow easing animation
   - Stable outline rendering

3. Navigation:
   - Smart focus fly-to with easing
   - Animated section transitions

4. Measurement Suite:
   - Point-to-point distance
   - Angle measurement
   - Radius detection
   - Minimum distance between faces

---

## Sprint 6 — Web-native Differentiators

**Goal:** Leverage web platform advantages

### Deliverables
1. Shareable State Snapshots (URL-based)
2. Comments/Markups pinned to faceId/edgeId
3. Session Replay (command-based recording)
4. Presentation Mode (cinematic camera paths)

---

# Performance Checklist

## Main Thread Discipline

### Per-Frame Budget: 2-4ms (hard max 6ms)

- [ ] No synchronous file operations
- [ ] No blocking GPU readbacks (use async)
- [ ] No heavy computations in render loop
- [ ] Profile with `performance.now()` boundaries

### Zero Per-Frame Allocations

```typescript
// BAD - allocates every frame
function update() {
  const temp = new Vector3();
  temp.set(x, y, z);
}

// GOOD - reuse pooled objects
const _tempVec3 = new Vector3();
function update() {
  _tempVec3.set(x, y, z);
}
```

### Mandatory Pooling

- [ ] Vector3, Matrix4, Quaternion pools
- [ ] TypedArray recycling for geometry updates
- [ ] Object pools for frequently created/destroyed items

---

## GPU Budget

### Draw Calls

| Scene Size | Target | Max |
|------------|--------|-----|
| Small (<100K tris) | <50 | 100 |
| Medium (100K-1M) | <200 | 300 |
| Large (1M-3M) | <300 | 500 |

### Reduction Strategies

1. **Geometry Batching**
   - Merge static meshes by material
   - Use `BufferGeometryUtils.mergeBufferGeometries()`

2. **Instancing**
   - Same geometry, different transforms
   - Use `InstancedMesh` for repeated parts

3. **Material Cache**
   ```typescript
   const materialCache = new Map<string, Material>();

   function getMaterial(key: string): Material {
     if (!materialCache.has(key)) {
       materialCache.set(key, createMaterial(key));
     }
     return materialCache.get(key)!;
   }
   ```

4. **Shader Variant Control**
   - Limit #define combinations
   - Reuse pipelines where possible

---

## Memory Strategy

### Geometry Lifecycle

```typescript
class GeometryPool {
  private available: BufferGeometry[] = [];

  acquire(): BufferGeometry {
    return this.available.pop() ?? new BufferGeometry();
  }

  release(geo: BufferGeometry) {
    // Clear attributes but keep allocated buffers
    this.available.push(geo);
  }

  dispose() {
    this.available.forEach(g => g.dispose());
    this.available = [];
  }
}
```

### TypedArray Recycling

```typescript
class BufferPool {
  private pools = new Map<number, Float32Array[]>();

  acquire(size: number): Float32Array {
    const bucket = this.findBucket(size);
    const pool = this.pools.get(bucket) ?? [];
    return pool.pop() ?? new Float32Array(bucket);
  }

  release(buffer: Float32Array) {
    const bucket = buffer.length;
    const pool = this.pools.get(bucket) ?? [];
    pool.push(buffer);
    this.pools.set(bucket, pool);
  }
}
```

### GPU Resource Cleanup

```typescript
// Deterministic disposal
class ResourceManager {
  private resources: Set<Disposable> = new Set();

  track<T extends Disposable>(resource: T): T {
    this.resources.add(resource);
    return resource;
  }

  disposeAll() {
    for (const r of this.resources) {
      r.dispose();
    }
    this.resources.clear();
  }
}
```

---

## Worker Offload Checklist

### Must Be in Workers

- [x] STEP/IGES parsing
- [x] Topology extraction
- [x] Tessellation
- [x] Edge polyline extraction
- [x] LOD generation
- [x] Binary cache encoding/decoding

### Worker Communication

```typescript
// Use Transferable objects
const positions = new Float32Array(vertexCount * 3);
worker.postMessage({ positions }, [positions.buffer]);

// Avoid structured clone for large data
// BAD: worker.postMessage({ data: largeArray })
// GOOD: worker.postMessage({ data: largeArray }, [largeArray.buffer])
```

---

## Picking Optimization

### WebGPU Async Strategy

```typescript
// Ring buffer prevents stalls
const RING_SIZE = 3;
let currentBuffer = 0;

async function asyncPick() {
  const buffer = ringBuffers[currentBuffer];
  currentBuffer = (currentBuffer + 1) % RING_SIZE;

  // Previous frame's buffer should be ready
  await buffer.mapAsync(GPUMapMode.READ);
  // ...
}
```

### WebGL2 Throttling

```typescript
// Limit readPixels frequency
const PICK_INTERVAL = 16; // ms
let lastPickTime = 0;

function throttledPick(x: number, y: number) {
  const now = performance.now();
  if (now - lastPickTime < PICK_INTERVAL) return;
  lastPickTime = now;

  // 1x1 pixel read only
  gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
}
```

---

## Progressive Loading

### First Visible Priority

```typescript
interface LoadPriority {
  partId: string;
  priority: number; // Lower = higher priority
  inFrustum: boolean;
  distanceToCamera: number;
}

function calculatePriority(part: Part, camera: Camera): number {
  if (!isInFrustum(part, camera)) return Infinity;
  return camera.position.distanceTo(part.center);
}
```

### Streaming Strategy

```
Frame 1: Load bounding boxes (instant feedback)
Frame 2-10: Stream high-priority parts
Frame 10+: Background load remaining parts
```

---

## Profiling Targets

| Metric | Green | Yellow | Red |
|--------|-------|--------|-----|
| Frame time | <16ms | 16-20ms | >20ms |
| JS heap | <500MB | 500-800MB | >800MB |
| GPU memory | <1GB | 1-2GB | >2GB |
| Pick latency | <8ms | 8-16ms | >16ms |

### Quick Performance Test

```typescript
function runPerfTest() {
  const times: number[] = [];

  for (let i = 0; i < 100; i++) {
    const start = performance.now();
    // Operation to test
    times.push(performance.now() - start);
  }

  console.log({
    avg: times.reduce((a,b) => a+b) / times.length,
    min: Math.min(...times),
    max: Math.max(...times),
    p95: times.sort()[94]
  });
}
```

---

# GPU Patterns Reference

## GPU Picking Implementation

### WebGPU Pattern (Preferred)

```typescript
// Async map with ring buffer to avoid stalls
class WebGPUPicker {
  private ringBuffer: GPUBuffer[] = [];
  private ringIndex = 0;
  private RING_SIZE = 3;

  async pick(x: number, y: number): Promise<PickResult> {
    // Render ID pass to texture
    await this.renderIdPass(x, y);

    // Use ring buffer for async readback
    const buffer = this.ringBuffer[this.ringIndex];
    this.ringIndex = (this.ringIndex + 1) % this.RING_SIZE;

    // Async map - doesn't block
    await buffer.mapAsync(GPUMapMode.READ);
    const data = new Uint32Array(buffer.getMappedRange());
    const result = { partId: data[0], solidId: data[1], faceId: data[2] };
    buffer.unmap();

    return result;
  }
}
```

### WebGL2 Fallback

```typescript
// Minimal 1x1 readPixels, throttled
class WebGL2Picker {
  private throttleMs = 16; // ~60fps max
  private lastPick = 0;

  pick(x: number, y: number): PickResult | null {
    const now = performance.now();
    if (now - this.lastPick < this.throttleMs) return null;
    this.lastPick = now;

    // Render to offscreen framebuffer
    this.renderIdPass(x, y);

    // Read single pixel
    const pixels = new Uint8Array(4);
    gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixels);

    return this.decodeIds(pixels);
  }

  private decodeIds(rgba: Uint8Array): PickResult {
    // Pack 3 IDs into RGBA channels
    return {
      partId: rgba[0],
      solidId: rgba[1],
      faceId: rgba[2]
    };
  }
}
```

---

## GPU Highlight System

### Mask-Based Approach

```typescript
// Single texture stores highlight state for all faces
class GPUHighlighter {
  private maskTexture: WebGLTexture | GPUTexture;
  private maskData: Uint8Array;

  setHighlight(faceIds: number[], state: HighlightState) {
    for (const id of faceIds) {
      this.maskData[id] = state;
    }
    this.updateTexture();
  }

  // Single overlay pass in render loop
  renderHighlight() {
    // Shader samples maskTexture using a_faceId
    // Applies glow/outline based on mask value
  }
}
```

### WGSL Highlight Shader

```wgsl
@group(0) @binding(0) var maskTexture: texture_2d<u32>;

struct VertexOutput {
  @builtin(position) position: vec4f,
  @location(0) faceId: u32,
}

@fragment
fn main(in: VertexOutput) -> @location(0) vec4f {
  let maskValue = textureLoad(maskTexture, vec2u(in.faceId, 0), 0).r;

  if (maskValue == 1u) {
    // Hover highlight
    return vec4f(0.3, 0.6, 1.0, 0.3);
  } else if (maskValue == 2u) {
    // Selection highlight
    return vec4f(1.0, 0.5, 0.0, 0.5);
  }

  discard;
}
```

### GLSL Fallback

```glsl
uniform sampler2D u_maskTexture;
varying float v_faceId;

void main() {
  float maskValue = texture2D(u_maskTexture, vec2(v_faceId / 65535.0, 0.0)).r;

  if (maskValue > 0.5 && maskValue < 1.5) {
    gl_FragColor = vec4(0.3, 0.6, 1.0, 0.3); // Hover
  } else if (maskValue > 1.5) {
    gl_FragColor = vec4(1.0, 0.5, 0.0, 0.5); // Selection
  } else {
    discard;
  }
}
```

---

## ID Encoding Shaders

### WGSL ID Pass

```wgsl
struct VertexInput {
  @location(0) position: vec3f,
  @location(1) partId: u32,
  @location(2) solidId: u32,
  @location(3) faceId: u32,
}

struct VertexOutput {
  @builtin(position) position: vec4f,
  @location(0) @interpolate(flat) partId: u32,
  @location(1) @interpolate(flat) solidId: u32,
  @location(2) @interpolate(flat) faceId: u32,
}

@vertex
fn vs_main(in: VertexInput) -> VertexOutput {
  var out: VertexOutput;
  out.position = uniforms.mvp * vec4f(in.position, 1.0);
  out.partId = in.partId;
  out.solidId = in.solidId;
  out.faceId = in.faceId;
  return out;
}

@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4u {
  return vec4u(in.partId, in.solidId, in.faceId, 0u);
}
```

---

## Worker Communication Pattern

```typescript
// Main thread
const worker = new Worker('tessellation.worker.js');

worker.postMessage({
  type: 'TESSELLATE',
  stepData: arrayBuffer,
  options: { deflection: 0.1 }
}, [arrayBuffer]); // Transfer ownership

worker.onmessage = (e) => {
  if (e.data.type === 'PROGRESS') {
    updateProgress(e.data.percent);
  } else if (e.data.type === 'COMPLETE') {
    const geometry = rebuildGeometry(e.data.buffers);
    addToScene(geometry);
  }
};

// Worker thread
self.onmessage = async (e) => {
  const { stepData, options } = e.data;

  for await (const part of tessellateProgressive(stepData, options)) {
    self.postMessage({ type: 'PROGRESS', percent: part.progress });

    if (part.complete) {
      self.postMessage({
        type: 'COMPLETE',
        buffers: part.buffers
      }, part.buffers.map(b => b.buffer)); // Transfer back
    }
  }
};
```

---

## Instancing Pattern

```typescript
class InstancedPartRenderer {
  private instanceMatrices: Float32Array;
  private instanceCount = 0;

  addInstance(partId: number, matrix: Matrix4) {
    const offset = this.instanceCount * 16;
    matrix.toArray(this.instanceMatrices, offset);
    this.instanceCount++;
  }

  render() {
    // Single draw call for all instances
    gl.drawElementsInstanced(
      gl.TRIANGLES,
      this.indexCount,
      gl.UNSIGNED_INT,
      0,
      this.instanceCount
    );
  }
}
```
