88 Save an interactive HTML visualization of the tree structure using plotly.
89 Creates separate subplots for each level with distinctive colors for subtrees.
92 input_tree: Tree object to visualize
93 dimensions: 2 or 3 for 2D/3D visualization
94 filename: Base filename for output files (without extension)
97 None (saves an HTML file)
99 if not PLOTLY_AVAILABLE:
100 print(
"Plotly not available - skipping HTML export")
105 '#FF0000',
'#0000FF',
'#00FF00',
'#FFA500',
'#800080',
106 '#A52A2A',
'#FFC0CB',
'#808080',
'#808000',
'#00FFFF',
107 '#FF1493',
'#32CD32',
'#FFD700',
'#DC143C',
'#4169E1',
108 '#FF8C00',
'#9932CC',
'#2E8B57',
'#FF6347',
'#4682B4'
112 subtrees_shown_in_legend = set()
118 cols=input_tree._max_level,
119 subplot_titles=[f
"Level {i+1}" for i
in range(input_tree._max_level)],
120 specs=[[{
'type':
'scatter3d'}
for _
in range(input_tree._max_level)]]
123 for level
in range(input_tree._max_level):
124 plot_array = input_tree._tree_arrays[level]
129 for a
in range(3**(level+1)):
130 for b
in range(3**(level+1)):
131 for c
in range(3**(level+1)):
132 node, subtree_id = plot_array[a][b][c]
134 if subtree_id
not in subtree_data:
135 subtree_data[subtree_id] = {
'x': [],
'y': [],
'z': []}
136 subtree_data[subtree_id][
'x'].append(a)
137 subtree_data[subtree_id][
'y'].append(b)
138 subtree_data[subtree_id][
'z'].append(c)
141 for subtree_id, coords
in subtree_data.items():
142 color = subtree_colors[subtree_id % len(subtree_colors)]
143 marker_size = max(3, 20/(level+1)**1.5)
146 show_in_legend = subtree_id
not in subtrees_shown_in_legend
148 subtrees_shown_in_legend.add(subtree_id)
161 line=dict(width=1, color=
'black')
163 name=f
"Subtree {subtree_id}",
164 hovertemplate=f
"<b>Level {level+1} - Subtree {subtree_id}</b><br>" +
168 f
"Marker size: {marker_size:.1f}<br>" +
170 legendgroup=f
"subtree_{subtree_id}",
171 showlegend=show_in_legend
179 text=f
"Interactive 3D Tree Structure - {input_tree._name}<br>" +
180 f
"<span style='font-size: 12px'>Use mouse to rotate, zoom, and pan each level</span>",
185 margin=dict(t=100, b=50, l=20, r=20),
190 for i
in range(input_tree._max_level):
191 scene_name =
'scene' if i == 0
else f
'scene{i+1}'
192 fig.update_layout(**{
194 xaxis_title=
"X Coordinate",
195 yaxis_title=
"Y Coordinate",
196 zaxis_title=
"Z Coordinate",
197 xaxis=dict(range=[0, 3**(i+1)-1]),
198 yaxis=dict(range=[0, 3**(i+1)-1]),
199 zaxis=dict(range=[0, 3**(i+1)-1]),
202 eye=dict(x=1.5, y=1.5, z=1.5)
207 elif dimensions == 2:
211 cols=input_tree._max_level,
212 subplot_titles=[f
"Level {i+1}" for i
in range(input_tree._max_level)]
215 for level
in range(input_tree._max_level):
216 plot_array = input_tree._tree_arrays[level]
221 for a
in range(3**(level+1)):
222 for b
in range(3**(level+1)):
223 node, subtree_id = plot_array[a][b]
225 if subtree_id
not in subtree_data:
226 subtree_data[subtree_id] = {
'x': [],
'y': []}
227 subtree_data[subtree_id][
'x'].append(a)
228 subtree_data[subtree_id][
'y'].append(b)
231 for subtree_id, coords
in subtree_data.items():
232 color = subtree_colors[subtree_id % len(subtree_colors)]
233 marker_size = max(5, 30/(level+1)**2)
236 show_in_legend = subtree_id
not in subtrees_shown_in_legend
238 subtrees_shown_in_legend.add(subtree_id)
250 line=dict(width=1, color=
'black')
252 name=f
"Subtree {subtree_id}",
253 hovertemplate=f
"<b>Level {level+1} - Subtree {subtree_id}</b><br>" +
256 f
"Marker size: {marker_size:.1f}<br>" +
258 legendgroup=f
"subtree_{subtree_id}",
259 showlegend=show_in_legend
267 text=f
"Interactive 2D Tree Structure - {input_tree._name}<br>" +
268 f
"<span style='font-size: 12px'>Use mouse to zoom and pan each level</span>",
273 margin=dict(t=100, b=50, l=20, r=20),
278 for i
in range(input_tree._max_level):
280 title_text=
"X Coordinate",
281 range=[-1, 3**(i+1)],
285 title_text=
"Y Coordinate",
286 range=[-1, 3**(i+1)],
292 xref=
"paper", yref=
"paper",
294 text=f
"<b>Tree Statistics:</b><br>" +
295 f
"Name: {input_tree._name}<br>" +
296 f
"Dimensions: {dimensions}D<br>" +
297 f
"Max Level: {input_tree._max_level}<br>" +
298 f
"Total Leaves: {int(input_tree._leaf_count)}<br>" +
299 f
"Subtrees: {input_tree._subtree_count}<br>" +
300 f
"Split Pattern: {[int(x) for x in input_tree._split_pattern]}",
303 bgcolor=
"rgba(255,255,255,0.9)",
311 'displayModeBar':
True,
312 'displaylogo':
False,
313 'modeBarButtonsToRemove': [
'pan2d',
'lasso2d',
'select2d'],
321 include_plotlyjs=
True,
325 print(f
"Interactive HTML visualization saved as {filename}.html")
327 print(f
" • Mouse rotation and zoom ({'3D rotation' if dimensions == 3 else '2D zoom/pan'})")
328 print(f
" • Distinctive colors for {input_tree._subtree_count} subtrees")
329 print(f
" • Separate subplots for each of {input_tree._max_level} levels")
330 print(f
" • Hover information for each node")
331 print(f
" • Full browser width layout with responsive design")
336 Visualize tree structure with customizable output filename.
337 Saves in PNG, PDF, and interactive HTML formats.
340 input_tree: Tree object to visualize
341 dimensions: 2 or 3 for 2D/3D visualization
342 filename: Base filename for output files (without extension)
345 None (saves files and displays plot)
348 fig = plt.figure(figsize=(9*input_tree._max_level, 8))
351 for level
in range(input_tree._max_level):
352 plot_array = input_tree._tree_arrays[level]
359 for a
in range(3**(level+1)):
360 for b
in range(3**(level+1)):
361 for c
in range(3**(level+1)):
362 node, subtree_id = plot_array[a][b][c]
364 indices.append((a, b, c))
365 subtree_ids.append(subtree_id)
368 indices = np.array(indices)
369 subtree_ids = np.array(subtree_ids)
371 axes.append(fig.add_subplot(1, input_tree._max_level, level+1, projection=
'3d'))
372 scatter = axes[level].scatter(indices[:, 0], indices[:, 1], indices[:, 2],
373 c=subtree_ids, cmap=
'tab10', s=1000/(level+1)**3,
374 depthshade=
True, marker=
's')
376 axes[level].set_xlabel(
'X')
377 axes[level].set_ylabel(
'Y')
378 axes[level].set_zlabel(
'Z')
380 axes[level].set_xlim([0, 3**(1+level)-1])
381 axes[level].set_ylim([0, 3**(1+level)-1])
382 axes[level].set_zlim([0, 3**(1+level)-1])
383 axes[level].set_title(
"Level="+str(level+1))
385 elif dimensions == 2:
386 for a
in range(3**(level+1)):
387 for b
in range(3**(level+1)):
388 node, subtree_id = plot_array[a][b]
390 indices.append((a, b))
391 subtree_ids.append(subtree_id)
394 indices = np.array(indices)
395 subtree_ids = np.array(subtree_ids)
397 axes.append(fig.add_subplot(1, input_tree._max_level, level+1))
398 scatter = axes[level].scatter(indices[:, 0], indices[:, 1],
399 c=subtree_ids, cmap=
'tab10', s=5000/(level+1)**4,
402 axes[level].set_xlabel(
'X')
403 axes[level].set_ylabel(
'Y')
405 axes[level].set_xlim([-1, 3**(1+level)])
406 axes[level].set_ylim([-1, 3**(1+level)])
407 axes[level].set_title(
"Level="+str(level+1))
411 plt.savefig(f
"{filename}.pdf", dpi=300, bbox_inches=
'tight')
412 plt.savefig(f
"{filename}.png", dpi=300, bbox_inches=
'tight')
413 print(f
"Static visualizations saved as {filename}.pdf and {filename}.png")
419 print(
"Plotly not available. Install with: pip install plotly")
420 print(
"HTML export skipped - only PDF and PNG saved")