Search Results Visualization

Important

Purpose: This guide is for Developers and Engineers who run CAD similarity search with CADSearch and want to visualize the returned hits.

For dataset exploration workflows (querying file IDs and viewing pre-generated previews), start with Data Visualization Experience.

../_images/search_results.png

Overview

DatasetViewer can visualize similarity search results returned by CADSearch.

Instead of requiring pre-generated PNG previews, show_search_results() generates preview images on-the-fly from the CAD file paths stored in each search hit.

Optionally, you can pass a query CAD file path to display the query preview alongside the results.

Use this when you have:

Prerequisites

  • A working HOOPS AI installation and valid license

  • A list of search hits (objects with at least .id and .score)

  • The CAD files referenced by hit.id must be readable from the current machine

Important: show_search_results() treats hit.id as a CAD file path. This works directly when you indexed embeddings using file paths as IDs. If you indexed embeddings using non-path IDs (for example, integers or database keys), map each hit to its CAD file path before visualizing.

Quick Start

import hoops_ai
from hoops_ai.insights import DatasetViewer
from hoops_ai.ml import CADSearch

# 1) Set license (only needed once in your process)
hoops_ai.set_license("...")

# 2) Initialize viewer (empty for search results)
viewer = DatasetViewer(file_ids=[], png_paths=[], scs_paths=[])

# 3) Run a CAD similarity search (any approach that returns hits with .id/.score)
query_path = "query.step"

searcher = CADSearch(shape_model=embedder)
hits = searcher.search_by_shape(cad_path=query_path, top_k=10)

# 4) Visualize results
fig = viewer.show_search_results(hits, query_file=query_path)

See Embeddings & Similarity Search for end-to-end retrieval examples.

What show_search_results does

At a high level, show_search_results():

  1. Reads the CAD file path from each hit (hit.id)

  2. Generates PNG preview images into an output folder

  3. Displays the results in a grid

  4. Optionally adds labels (scores and/or filenames)

  5. Optionally saves the figure to an image file

If query_file is provided, the query CAD file is loaded and displayed alongside the hit grid.

Common Patterns

Show the query part next to results

fig = viewer.show_search_results(hits, query_file=query_path)

Custom grid layout

fig = viewer.show_search_results(
    hits,
    k=16,           # Show top 16 results
    grid_cols=4,    # 4 columns (4x4 grid)
    title="Search Results for Gear Parts"
)

Scores only (hide filenames)

fig = viewer.show_search_results(
   hits,
   show_filenames=False,  # Hide filenames
   show_scores=True       # Show scores only
)

Limit and format a large result set

fig = viewer.show_search_results(
   hits,
   k=25,           # Show top 25
   grid_cols=5,    # 5x5 grid
   figsize=(20, 20)  # Larger figure
)

Save Results to File

fig = viewer.show_search_results(
   hits,
   save_path="results/search_output.png",
   output_dir="results/pngs"  # Where to save generated PNGs
)

Visualize multiple queries with organized output folders

When you run many searches, it is often helpful to keep each query’s generated previews in its own folder.

Here’s an example for how to organize generated PNGs by search query:

from pathlib import Path

viewer = DatasetViewer(file_ids=[], png_paths=[], scs_paths=[])

query_files = [
    "queries/part_a.step",
    "queries/part_b.step",
]

for query_file in query_files:
   hits = searcher.search_by_shape(query_file, top_k=10)
   output_dir = f"results/{Path(query_file).stem}"
   fig = viewer.show_search_results(hits, output_dir=output_dir)

Parameter Reference

Parameter

Type

Default

Description

hits

list

Required

Search hits returned by CADSearch (each hit must expose .id and .score)

query_file

str | Path

None

Optional CAD file path to render and display alongside the hit grid

output_dir

str | Path

Auto

Where generated PNG previews are written. If not provided, uses the viewer reference directory when available, otherwise ./out.

k

int

None

Maximum number of hits to display (None = show all)

grid_cols

int

4

Number of columns in the results grid

figsize

tuple

Auto

Matplotlib figure size (width, height)

show_scores

bool

True

Show similarity scores in the per-item labels

show_filenames

bool

True

Show filenames in the per-item labels

title

str

“CAD Similarity Search Results”

Figure title

missing_color

tuple

(200, 200, 200)

RGB color for placeholder tiles when a preview cannot be generated

save_path

str | Path

None

Save the figure to this file path

is_white_background

bool

True

Use a white background when exporting PNG previews

overwrite

bool

True

Overwrite existing generated PNG previews in output_dir

Comparison with other DatasetViewer methods

Method

Input

Use case

show_preview_as_image()

File IDs

Dataset exploration using pre-generated PNG previews

show_search_results()

Search hits

Similarity search visualization with on-the-fly PNG generation

show_preview_as_3d()

File IDs

Interactive 3D viewing of dataset items

Tips and Best Practices

Minimal viewer initialization for search results

If you only need to visualize search hits (not explore a dataset by file IDs), an empty viewer is sufficient:

viewer = DatasetViewer(file_ids=[], png_paths=[], scs_paths=[])

Performance for large result sets

For displaying many results, generate PNGs beforehand:

# Generate all PNGs first
for hit in hits[:k]:
   model = loader.create_from_file(hit.id)
   tools.exportStreamCache(model, f"pngs/{hit.id}")

# Then visualize (will be faster)
fig = viewer.show_search_results(hits, output_dir="pngs")

Error Handling

The method handles failed CAD loads gracefully with placeholders:

# Will show gray placeholders for any files that fail to load
fig = viewer.show_search_results(
    hits,
    missing_color=(220, 220, 220)  # Light gray for failed loads
)

Troubleshooting

No search hits provided

If hits is empty, nothing can be visualized. Check that your search call returned results.

Note: when hits is empty, show_search_results(...) returns None.

if hits:
    fig = viewer.show_search_results(hits)
else:
    print("No search results to display")

Gray placeholders instead of images

If you get placeholder tiles (or previews fail to generate), enable logging to see the underlying load/export failures.

Placeholders typically indicate the CAD file could not be loaded or a PNG could not be generated.

Common causes:

  1. The path stored in hit.id does not exist on this machine

  2. The CAD file format cannot be loaded

  3. The file is corrupted

Solutions: Check logs for specific error messages:

import logging
logging.basicConfig(level=logging.INFO)
fig = viewer.show_search_results(hits)

Slow performance for many results

Solution: Limit results with k parameter:

# Only show top 10 even if you have 100 hits
fig = viewer.show_search_results(hits, k=10)

Resource cleanup

DatasetViewer initializes a process pool for parallel preview generation. When you are done with a viewer, call close() to release resources.

viewer = DatasetViewer(file_ids=[], png_paths=[], scs_paths=[])
viewer.show_search_results(hits)
viewer.close()

Or use a context manager:

with DatasetViewer(file_ids=[], png_paths=[], scs_paths=[]) as viewer:
    viewer.show_search_results(hits)

Migrating from legacy visualization helpers

If you previously had a notebook-only helper that took a list of hits and manually exported preview images, you can typically replace it with a single call to show_search_results().

# Old approach (Notebook Function)
def visualize_cad_hits(hits, loader, output_dir=None, cols=4, figsize_per_col=4):
   # ... implementation ...

fig = visualize_cad_hits(hits, loader, "out", cols=5, figsize_per_col=3)

# New approach (DatasetViewer Method)
viewer = DatasetViewer([], [], [])
fig = viewer.show_search_results(
   hits,
   output_dir="out",
   grid_cols=5
)

Benefits of the new approach:

  • ✅ No need to pass loader separately (handled internally)

  • ✅ Better error handling

  • ✅ More customization options

  • ✅ Consistent API with other DatasetViewer methods

  • ✅ Automatic figure sizing