Skip to content

Commit cf3852b

Browse files
committed
Merge branch 'main' of github.com:dull-bird/cv_debug_mate_cpp
2 parents 52bc889 + c9453f1 commit cf3852b

11 files changed

Lines changed: 710 additions & 359 deletions

File tree

package.json

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@
5858
"command": "cv-debugmate.unpairVariable",
5959
"title": "Unpair",
6060
"icon": "$(link-external)"
61+
},
62+
{
63+
"command": "cv-debugmate.addToGroup",
64+
"title": "Add to Group",
65+
"icon": "$(plus)"
6166
}
6267
],
6368
"views": {
@@ -91,23 +96,28 @@
9196
"view/item/context": [
9297
{
9398
"command": "cv-debugmate.pairVariable",
94-
"when": "view == cv-debugmate-variables && (viewItem == 'cvVariable:mat' || viewItem == 'cvVariable:pointcloud')",
99+
"when": "view == cv-debugmate-variables && (viewItem =~ /^cvVariable:(mat|pointcloud)/)",
95100
"group": "inline@2"
96101
},
97102
{
98103
"command": "cv-debugmate.unpairVariable",
99-
"when": "view == cv-debugmate-variables && (viewItem == 'cvVariablePaired:mat' || viewItem == 'cvVariablePaired:pointcloud')",
104+
"when": "view == cv-debugmate-variables && (viewItem =~ /^cvVariablePaired:(mat|pointcloud)/)",
100105
"group": "inline@2"
101106
},
102107
{
103108
"command": "cv-debugmate.pairVariable",
104-
"when": "view == cv-debugmate-variables && (viewItem == 'cvVariable:mat' || viewItem == 'cvVariable:pointcloud')",
109+
"when": "view == cv-debugmate-variables && (viewItem =~ /^cvVariable:(mat|pointcloud)/)",
105110
"group": "1_pairing"
106111
},
107112
{
108113
"command": "cv-debugmate.unpairVariable",
109-
"when": "view == cv-debugmate-variables && (viewItem == 'cvVariablePaired:mat' || viewItem == 'cvVariablePaired:pointcloud')",
114+
"when": "view == cv-debugmate-variables && (viewItem =~ /^cvVariablePaired:(mat|pointcloud)/)",
110115
"group": "1_pairing"
116+
},
117+
{
118+
"command": "cv-debugmate.addToGroup",
119+
"when": "view == cv-debugmate-variables && viewItem == 'cvGroup:paired'",
120+
"group": "inline@1"
111121
}
112122
]
113123
}

src/cvVariablesProvider.ts

Lines changed: 149 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as vscode from 'vscode';
22
import { isMat, isPoint3Vector, is1DVector, isLikely1DMat, is1DSet, isMatx, is2DStdArray, is1DStdArray, isPoint3StdArray, is2DCStyleArray, is1DCStyleArray, is3DCStyleArray, is3DStdArray, isUninitializedOrInvalid, isUninitializedMat, isUninitializedMatFromChildren, isUninitializedVector, isPointerType, getPointerEvaluateExpression } from './utils/opencv';
33
import { SyncManager } from './utils/syncManager';
4+
import { PanelManager } from './utils/panelManager';
45

56
const COLORS = [
67
'#3794ef', // Blue
@@ -34,6 +35,14 @@ function getColoredIcon(kind: 'mat' | 'pointcloud' | 'plot' | 'group', color: st
3435
return { light: iconUri, dark: iconUri };
3536
}
3637

38+
function getColoredCircleIcon(color: string): { light: vscode.Uri, dark: vscode.Uri } {
39+
// Create a simple filled circle SVG
40+
const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><circle cx="8" cy="8" r="6" fill="${color}"/></svg>`;
41+
const base64 = Buffer.from(svg).toString('base64');
42+
const iconUri = vscode.Uri.parse(`data:image/svg+xml;base64,${base64}`);
43+
return { light: iconUri, dark: iconUri };
44+
}
45+
3746
export class CVVariable extends vscode.TreeItem {
3847
public readonly isEmpty: boolean;
3948

@@ -51,7 +60,8 @@ export class CVVariable extends vscode.TreeItem {
5160
public pairedWith?: string,
5261
public groupIndex?: number,
5362
public readonly isPointer: boolean = false,
54-
public readonly baseType: string = ''
63+
public readonly baseType: string = '',
64+
public readonly sessionId?: string // Add sessionId to check panel status
5565
) {
5666
super(name, collapsibleState);
5767
this.tooltip = `${this.name}: ${this.type}${this.pairedWith ? ` (Paired with ${this.pairedWith})` : ''}${this.isPointer ? ' (pointer)' : ''}`;
@@ -63,17 +73,28 @@ export class CVVariable extends vscode.TreeItem {
6373
this.isEmpty = (sizeInfo === '0' || sizeInfo === '0x0' || sizeInfo === '' || size === 0);
6474
const displaySize = this.isEmpty ? 'empty' : sizeInfo;
6575

66-
// Show pointer indicator in description
76+
// Check if panel is open
77+
const isOpen = sessionId ? this.checkIfPanelOpen(kind, sessionId, name) : false;
78+
79+
// Show pointer indicator and open status in description
6780
const pointerIndicator = this.isPointer ? '→ ' : '';
68-
this.description = `${pointerIndicator}[${displaySize}]`;
81+
const openIndicator = isOpen ? '● ' : '';
82+
this.description = `${openIndicator}${pointerIndicator}[${displaySize}]`;
6983

70-
if (isPaired && groupIndex !== undefined && kind !== 'plot') {
84+
// For mat (image) variables, always use file-media icon regardless of pairing
85+
if (kind === 'mat') {
86+
this.iconPath = new vscode.ThemeIcon('file-media');
87+
this.contextValue = isPaired
88+
? `cvVariablePaired:${kind}${this.isEmpty ? ':empty' : ''}${this.isPointer ? ':pointer' : ''}${isOpen ? ':open' : ''}`
89+
: `cvVariable:${kind}${this.isEmpty ? ':empty' : ''}${this.isPointer ? ':pointer' : ''}${isOpen ? ':open' : ''}`;
90+
} else if (isPaired && groupIndex !== undefined && kind !== 'plot') {
91+
// For pointcloud, use colored icon when paired
7192
const color = COLORS[groupIndex % COLORS.length];
7293
this.iconPath = getColoredIcon(kind, color);
73-
this.contextValue = `cvVariablePaired:${kind}${this.isEmpty ? ':empty' : ''}${this.isPointer ? ':pointer' : ''}`;
94+
this.contextValue = `cvVariablePaired:${kind}${this.isEmpty ? ':empty' : ''}${this.isPointer ? ':pointer' : ''}${isOpen ? ':open' : ''}`;
7495
} else {
7596
this.iconPath = new vscode.ThemeIcon(typeIcon);
76-
this.contextValue = `cvVariable:${kind}${this.isEmpty ? ':empty' : ''}${this.isPointer ? ':pointer' : ''}`;
97+
this.contextValue = `cvVariable:${kind}${this.isEmpty ? ':empty' : ''}${this.isPointer ? ':pointer' : ''}${isOpen ? ':open' : ''}`;
7798
}
7899

79100
if (!this.isEmpty) {
@@ -84,6 +105,38 @@ export class CVVariable extends vscode.TreeItem {
84105
};
85106
}
86107
}
108+
109+
private checkIfPanelOpen(kind: string, sessionId: string, variableName: string): boolean {
110+
const viewTypeMap: Record<string, string> = {
111+
'mat': 'MatImageViewer',
112+
'pointcloud': 'PointCloudViewer',
113+
'plot': 'PlotViewer'
114+
};
115+
const viewType = viewTypeMap[kind];
116+
if (!viewType) return false;
117+
return PanelManager.hasPanel(viewType, sessionId, variableName);
118+
}
119+
}
120+
121+
export class CVCategoryGroup extends vscode.TreeItem {
122+
constructor(
123+
public readonly label: string,
124+
public readonly collapsibleState: vscode.TreeItemCollapsibleState,
125+
public readonly kind: 'mat' | 'plot' | 'pointcloud',
126+
public readonly children: (CVGroup | CVVariable)[]
127+
) {
128+
super(label, collapsibleState);
129+
this.contextValue = `cvCategory:${kind}`;
130+
131+
// Set icon based on category type
132+
if (kind === 'mat') {
133+
this.iconPath = new vscode.ThemeIcon('file-media');
134+
} else if (kind === 'plot') {
135+
this.iconPath = new vscode.ThemeIcon('graph');
136+
} else if (kind === 'pointcloud') {
137+
this.iconPath = new vscode.ThemeIcon('layers');
138+
}
139+
}
87140
}
88141

89142
export class CVGroup extends vscode.TreeItem {
@@ -94,20 +147,21 @@ export class CVGroup extends vscode.TreeItem {
94147
public readonly groupIndex?: number
95148
) {
96149
super(label, collapsibleState);
97-
this.contextValue = 'cvGroup';
150+
// Only paired groups (with groupIndex) can have add button
151+
this.contextValue = groupIndex !== undefined ? 'cvGroup:paired' : 'cvGroup';
98152

99153
if (groupIndex !== undefined) {
100154
const color = COLORS[groupIndex % COLORS.length];
101-
this.iconPath = getColoredIcon('group', color);
155+
this.iconPath = getColoredCircleIcon(color);
102156
} else {
103-
this.iconPath = new vscode.ThemeIcon('symbol-group');
157+
this.iconPath = new vscode.ThemeIcon('circle-filled');
104158
}
105159
}
106160
}
107161

108-
export class CVVariablesProvider implements vscode.TreeDataProvider<CVVariable | CVGroup> {
109-
private _onDidChangeTreeData: vscode.EventEmitter<CVVariable | CVGroup | undefined | void> = new vscode.EventEmitter<CVVariable | CVGroup | undefined | void>();
110-
readonly onDidChangeTreeData: vscode.Event<CVVariable | CVGroup | undefined | void> = this._onDidChangeTreeData.event;
162+
export class CVVariablesProvider implements vscode.TreeDataProvider<CVVariable | CVGroup | CVCategoryGroup> {
163+
private _onDidChangeTreeData: vscode.EventEmitter<CVVariable | CVGroup | CVCategoryGroup | undefined | void> = new vscode.EventEmitter<CVVariable | CVGroup | CVCategoryGroup | undefined | void>();
164+
readonly onDidChangeTreeData: vscode.Event<CVVariable | CVGroup | CVCategoryGroup | undefined | void> = this._onDidChangeTreeData.event;
111165

112166
private variables: CVVariable[] = [];
113167
private groups: CVGroup[] = [];
@@ -134,19 +188,38 @@ export class CVVariablesProvider implements vscode.TreeDataProvider<CVVariable |
134188
return this.variables;
135189
}
136190

137-
getTreeItem(element: CVVariable | CVGroup): vscode.TreeItem {
191+
getTreeItem(element: CVVariable | CVGroup | CVCategoryGroup): vscode.TreeItem {
138192
return element;
139193
}
140194

141-
async getChildren(element?: CVVariable | CVGroup): Promise<(CVVariable | CVGroup)[]> {
195+
async getChildren(element?: CVVariable | CVGroup | CVCategoryGroup): Promise<(CVVariable | CVGroup | CVCategoryGroup)[]> {
196+
// Level 3: Variable children (no children)
197+
if (element instanceof CVVariable) {
198+
return [];
199+
}
200+
201+
// Level 2: Group children (return variables in the group)
142202
if (element instanceof CVGroup) {
143203
return element.variables;
144204
}
145205

146-
if (element instanceof CVVariable) {
147-
return [];
206+
// Level 1: Category children (return groups + unpaired variables)
207+
if (element instanceof CVCategoryGroup) {
208+
const result: (CVGroup | CVVariable)[] = [];
209+
210+
// Add paired groups
211+
const pairedGroups = element.children.filter(c => c instanceof CVGroup && (c as CVGroup).groupIndex !== undefined) as CVGroup[];
212+
pairedGroups.sort((a, b) => (a.groupIndex || 0) - (b.groupIndex || 0));
213+
result.push(...pairedGroups);
214+
215+
// Add unpaired variables
216+
const unpairedVars = element.children.filter(c => c instanceof CVVariable && !(c as CVVariable).isPaired) as CVVariable[];
217+
result.push(...unpairedVars);
218+
219+
return result;
148220
}
149-
221+
222+
// Level 0: Root (return category groups)
150223
const debugSession = vscode.debug.activeDebugSession;
151224
if (!debugSession) {
152225
this.variables = [];
@@ -561,7 +634,8 @@ export class CVVariablesProvider implements vscode.TreeDataProvider<CVVariable |
561634
pairedVars.length > 0 ? pairedVars.join(', ') : undefined,
562635
groupIndex,
563636
pointerInfo.isPointer,
564-
pointerInfo.baseType
637+
pointerInfo.baseType,
638+
debugSession.id // Pass sessionId
565639
);
566640
}
567641
return null;
@@ -578,39 +652,73 @@ export class CVVariablesProvider implements vscode.TreeDataProvider<CVVariable |
578652

579653
this.variables = visualizableVariables;
580654

581-
const groupMap = new Map<number | undefined, CVVariable[]>();
582-
for (const v of visualizableVariables) {
583-
const idx = v.groupIndex;
584-
if (!groupMap.has(idx)) {
585-
groupMap.set(idx, []);
655+
// Separate variables by type
656+
const matVars = visualizableVariables.filter(v => v.kind === 'mat');
657+
const plotVars = visualizableVariables.filter(v => v.kind === 'plot');
658+
const pointcloudVars = visualizableVariables.filter(v => v.kind === 'pointcloud');
659+
660+
const resultCategories: CVCategoryGroup[] = [];
661+
662+
// 1. Images (2D) category
663+
if (matVars.length > 0) {
664+
const matChildren: (CVGroup | CVVariable)[] = [];
665+
666+
// Group paired mat variables
667+
const groupMap = new Map<number, CVVariable[]>();
668+
for (const v of matVars) {
669+
if (v.isPaired && v.groupIndex !== undefined) {
670+
if (!groupMap.has(v.groupIndex)) {
671+
groupMap.set(v.groupIndex, []);
672+
}
673+
groupMap.get(v.groupIndex)!.push(v);
674+
}
675+
}
676+
677+
// Create paired groups
678+
const sortedIndices = Array.from(groupMap.keys()).sort((a, b) => a - b);
679+
for (const idx of sortedIndices) {
680+
const vars = groupMap.get(idx)!;
681+
matChildren.push(new CVGroup(
682+
`Group ${idx + 1}`,
683+
vscode.TreeItemCollapsibleState.Expanded,
684+
vars,
685+
idx
686+
));
586687
}
587-
groupMap.get(idx)!.push(v);
688+
689+
// Add unpaired mat variables
690+
const unpairedMatVars = matVars.filter(v => !v.isPaired);
691+
matChildren.push(...unpairedMatVars);
692+
693+
resultCategories.push(new CVCategoryGroup(
694+
'Images (2D)',
695+
vscode.TreeItemCollapsibleState.Expanded,
696+
'mat',
697+
matChildren
698+
));
588699
}
589-
590-
const resultGroups: CVGroup[] = [];
591-
const sortedIndices = Array.from(groupMap.keys())
592-
.filter((idx): idx is number => idx !== undefined)
593-
.sort((a, b) => a - b);
594700

595-
for (const idx of sortedIndices) {
596-
resultGroups.push(new CVGroup(
597-
`Group ${idx + 1}`,
701+
// 2. Plot (1D) category
702+
if (plotVars.length > 0) {
703+
resultCategories.push(new CVCategoryGroup(
704+
'Plot (1D)',
598705
vscode.TreeItemCollapsibleState.Expanded,
599-
groupMap.get(idx)!,
600-
idx
706+
'plot',
707+
plotVars
601708
));
602709
}
603-
604-
const ungrouped = groupMap.get(undefined);
605-
if (ungrouped && ungrouped.length > 0) {
606-
resultGroups.push(new CVGroup(
607-
'(ungrouped)',
710+
711+
// 3. Points (3D) category
712+
if (pointcloudVars.length > 0) {
713+
resultCategories.push(new CVCategoryGroup(
714+
'Points (3D)',
608715
vscode.TreeItemCollapsibleState.Expanded,
609-
ungrouped
716+
'pointcloud',
717+
pointcloudVars
610718
));
611719
}
612720

613-
return resultGroups;
721+
return resultCategories;
614722
} catch (error) {
615723
console.error('Error fetching variables:', error);
616724
this.variables = [];

0 commit comments

Comments
 (0)