Skip to content
Open
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
22 changes: 13 additions & 9 deletions src/odb/src/3dblox/checker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -300,15 +300,19 @@ void Checker::checkBumpPhysicalAlignment(dbMarkerCategory* top_cat,
if (!cat) {
cat = dbMarkerCategory::createOrReplace(top_cat, "Bump Alignment");
}
auto* marker = dbMarker::create(cat);
marker->addSource(bump.bump_inst);
marker->addShape(Rect(p.x() - kBumpMarkerHalfSize,
p.y() - kBumpMarkerHalfSize,
p.x() + kBumpMarkerHalfSize,
p.y() + kBumpMarkerHalfSize));
marker->setComment(
fmt::format("Bump is outside its parent region {}",
region.region_inst->getChipRegion()->getName()));
// dbMarker::create returns nullptr once the category hits its
// max_markers_ limit; skip the addSource/addShape/setComment
// chain to avoid a null-deref crash in that case.
if (auto* marker = dbMarker::create(cat)) {
marker->addSource(bump.bump_inst);
marker->addShape(Rect(p.x() - kBumpMarkerHalfSize,
p.y() - kBumpMarkerHalfSize,
p.x() + kBumpMarkerHalfSize,
p.y() + kBumpMarkerHalfSize));
marker->setComment(
fmt::format("Bump is outside its parent region {}",
region.region_inst->getChipRegion()->getName()));
}
}
}
}
Expand Down
107 changes: 94 additions & 13 deletions src/web/src/3d-viewer-widget.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import * as THREE from 'https://esm.sh/three@0.160.0';

import {getThemeColors} from './theme.js';
import {getThemeColors, setCookie} from './theme.js';

// Camera navigation tuning constants
const kRotationSensitivity = 2.0;
Expand All @@ -19,6 +19,7 @@ const kMinZNear = 10.0;
const kZFarSafetyMargin = 10000;
const kStackGapFactor = 0.15;
const kDrcBlinkIntervalMs = 300;
const kDrcBlinkToggles = 6;
const DEG2RAD = Math.PI / 180;

// Distinct color palette for chiplets (saturated, good contrast in 3D)
Expand Down Expand Up @@ -94,6 +95,9 @@ export class ThreeDViewerWidget {
this._tooltip.className = 'three-d-tooltip';
this._canvasContainer.appendChild(this._tooltip);

this._optionsOverlay = this._createOptionsOverlay();
this._canvasContainer.appendChild(this._optionsOverlay);

this._raycaster = new THREE.Raycaster();

this._scene = new THREE.Scene();
Expand Down Expand Up @@ -250,6 +254,11 @@ export class ThreeDViewerWidget {

canvas.addEventListener('dblclick', (e) => {
e.preventDefault();
const markerId = this._pickDrcMarkerId(e);
if (markerId != null && this._app.drcWidget) {
this._app.drcWidget.highlightMarkerById(markerId, true);
return;
}
this.resetCamera();
});

Expand All @@ -265,19 +274,45 @@ export class ThreeDViewerWidget {
canvas.addEventListener('mouseleave', () => { this._hideTooltip(); });
}

_updateTooltip(event) {
if (!this._chipletMeshes.length) {
this._hideTooltip();
return;
}
// Options overlay (top-left): toggles like "True Z" that affect how the
// scene is rendered. Mirrors app.useTrueZ so the cookie is the single
// source of truth across page reloads.
_createOptionsOverlay() {
const overlay = document.createElement('div');
overlay.className = 'three-d-options';
const trueZLabel = document.createElement('label');
trueZLabel.title = 'Render chiplets at their true Z '
+ '(disable stacking offset for overlapping chiplets at same Z).';
const trueZCb = document.createElement('input');
trueZCb.type = 'checkbox';
trueZCb.checked = !!this._app.useTrueZ;
trueZCb.addEventListener('change', () => {
this._app.useTrueZ = trueZCb.checked;
setCookie('or_use_true_z', trueZCb.checked ? '1' : '0');
this.refreshSceneForOptions();
});
trueZLabel.appendChild(trueZCb);
trueZLabel.appendChild(document.createTextNode('True Z (no stacking)'));
overlay.appendChild(trueZLabel);
return overlay;
}

_setRaycasterFromEvent(event) {
const canvas = this._renderer.domElement;
const rect = canvas.getBoundingClientRect();
const ndc = new THREE.Vector2(
((event.clientX - rect.left) / rect.width) * 2 - 1,
-(((event.clientY - rect.top) / rect.height) * 2 - 1));

this._raycaster.setFromCamera(ndc, this._camera);
}

_updateTooltip(event) {
if (!this._chipletMeshes.length) {
this._hideTooltip();
return;
}

this._setRaycasterFromEvent(event);
const meshes = this._chipletMeshes.map((m) => m.mesh);
const hits = this._raycaster.intersectObjects(meshes, false);
if (hits.length === 0) {
Expand Down Expand Up @@ -306,6 +341,16 @@ export class ThreeDViewerWidget {
}
}

_pickDrcMarkerId(event) {
if (!this._drcMeshGroup) return null;
const hitboxes = this._drcMeshGroup.children.filter(
c => c.userData && c.userData.isHitbox);
if (hitboxes.length === 0) return null;
this._setRaycasterFromEvent(event);
const hits = this._raycaster.intersectObjects(hitboxes, false);
return hits.length > 0 ? hits[0].object.userData.markerId : null;
}

// Quaternion-based rotation: axis perpendicular to mouse movement in
// screen space, angle proportional to drag distance.
_handleRotate(dx, dy) {
Expand Down Expand Up @@ -580,6 +625,7 @@ export class ThreeDViewerWidget {
buildScene(data) {
if (this._destroyed)
return;
this._lastSceneData = data;
this._clearScene();

if (!data || !data.chiplets || data.chiplets.length === 0)
Expand Down Expand Up @@ -626,8 +672,11 @@ export class ThreeDViewerWidget {
}

// Separate chiplets that share the same Z and overlap in XY by stacking
// them in slots. See computeZOffsetSlots().
const zOffsets = computeZOffsetSlots(chiplets);
// them in slots. See computeZOffsetSlots(). When app.useTrueZ is on, skip
// the offset so chiplets render at their actual Z (may overlap).
const zOffsets = this._app.useTrueZ
? new Array(chiplets.length).fill(0)
: computeZOffsetSlots(chiplets);

chiplets.forEach((chiplet, idx) => {
const width = chiplet.width / dbu;
Expand Down Expand Up @@ -730,6 +779,10 @@ export class ThreeDViewerWidget {
this._drcMaterial.dispose();
this._drcMaterial = null;
}
if (this._drcHitboxMaterial) {
this._drcHitboxMaterial.dispose();
this._drcHitboxMaterial = null;
}
this._drcMeshGroup = null;
this.render();
}
Expand Down Expand Up @@ -764,6 +817,12 @@ export class ThreeDViewerWidget {
depthWrite : false
});

this._drcHitboxMaterial = new THREE.MeshBasicMaterial({
transparent : true,
opacity : 0,
depthWrite : false,
});

for (const violation of violations) {
const rects = (violation.rects && violation.rects.length > 0)
? violation.rects
Expand Down Expand Up @@ -792,11 +851,15 @@ export class ThreeDViewerWidget {

const boxGeometry = new THREE.BoxGeometry(width, height, currentDepth);
const edgesGeometry = new THREE.EdgesGeometry(boxGeometry);
boxGeometry.dispose();
const line = new THREE.LineSegments(edgesGeometry, this._drcMaterial);
line.position.set(cx, cy, cz);
line.renderOrder = 999;
this._drcMeshGroup.add(line);

const hitbox = new THREE.Mesh(boxGeometry, this._drcHitboxMaterial);
hitbox.position.set(cx, cy, cz);
hitbox.userData = { markerId : violation.id, isHitbox : true };
this._drcMeshGroup.add(hitbox);
}
}

Expand All @@ -805,12 +868,30 @@ export class ThreeDViewerWidget {
this.render();

let visible = true;
let toggles = 0;
this._drcBlinkInterval = setInterval(() => {
if (this._drcMeshGroup && this._drcMaterial) {
visible = !visible;
this._drcMaterial.opacity = visible ? 1.0 : 0.2;
if (!this._drcMeshGroup || !this._drcMaterial)
return;
toggles += 1;
if (toggles >= kDrcBlinkToggles) {
this._drcMaterial.opacity = 1.0;
this.render();
clearInterval(this._drcBlinkInterval);
this._drcBlinkInterval = null;
return;
}
visible = !visible;
this._drcMaterial.opacity = visible ? 1.0 : 0.2;
this.render();
}, kDrcBlinkIntervalMs);
}

refreshSceneForOptions() {
if (this._destroyed)
return;
if (this._lastSceneData) {
this.clearHighlightDRC();
this.buildScene(this._lastSceneData);
}
}
}
5 changes: 5 additions & 0 deletions src/web/src/checkbox-tree-model.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ export class CheckboxTreeModel {
return this._nodeMap.get(id);
}

isVisible(id) {
const node = this.get(id);
return node ? node.checked : false;
}

// Handle a user check/uncheck. Propagates down then up.
check(id, checked) {
const node = this._nodeMap.get(id);
Expand Down
Loading
Loading