cloudglancer 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cloudglancer-0.1.0/LICENSE +21 -0
- cloudglancer-0.1.0/MANIFEST.in +4 -0
- cloudglancer-0.1.0/PKG-INFO +174 -0
- cloudglancer-0.1.0/README.md +142 -0
- cloudglancer-0.1.0/cloudglancer/__init__.py +6 -0
- cloudglancer-0.1.0/cloudglancer/scatter.py +129 -0
- cloudglancer-0.1.0/cloudglancer.egg-info/PKG-INFO +174 -0
- cloudglancer-0.1.0/cloudglancer.egg-info/SOURCES.txt +12 -0
- cloudglancer-0.1.0/cloudglancer.egg-info/dependency_links.txt +1 -0
- cloudglancer-0.1.0/cloudglancer.egg-info/requires.txt +9 -0
- cloudglancer-0.1.0/cloudglancer.egg-info/top_level.txt +1 -0
- cloudglancer-0.1.0/pyproject.toml +58 -0
- cloudglancer-0.1.0/setup.cfg +4 -0
- cloudglancer-0.1.0/tests/test_scatter.py +91 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 AI@HHMI
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cloudglancer
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Simple interactive visualization of 3D point clouds
|
|
5
|
+
Author-email: Your Name <your.email@example.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/yourusername/cloudglancer
|
|
8
|
+
Project-URL: Repository, https://github.com/yourusername/cloudglancer
|
|
9
|
+
Project-URL: Issues, https://github.com/yourusername/cloudglancer/issues
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
20
|
+
Requires-Python: >=3.9
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: plotly>=5.0.0
|
|
24
|
+
Requires-Dist: pandas>=2.0.0
|
|
25
|
+
Requires-Dist: numpy>=1.24.0
|
|
26
|
+
Requires-Dist: nbformat>=4.2.0
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
29
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
30
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
|
|
33
|
+
# cloudglancer
|
|
34
|
+
|
|
35
|
+
Simple interactive visualization of 3D point clouds using Plotly.
|
|
36
|
+
|
|
37
|
+
## Features
|
|
38
|
+
|
|
39
|
+
- Interactive 3D scatter plots with pan, zoom, and rotation
|
|
40
|
+
- Support for categorical and continuous color mapping
|
|
41
|
+
- Combine multiple plots into subplot grids
|
|
42
|
+
- Easy-to-use API with sensible defaults
|
|
43
|
+
- Type hints for better IDE support
|
|
44
|
+
|
|
45
|
+
## Installation
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install cloudglancer
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Quick Start
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
import numpy as np
|
|
55
|
+
import cloudglancer
|
|
56
|
+
|
|
57
|
+
# Generate random 3D points
|
|
58
|
+
points = np.random.randn(500, 3)
|
|
59
|
+
|
|
60
|
+
# Create and display the plot
|
|
61
|
+
fig = cloudglancer.plot(points, title="My Point Cloud", size=2.0)
|
|
62
|
+
fig.show()
|
|
63
|
+
```
|
|
64
|
+
More examples are in the `examples` folder.
|
|
65
|
+
|
|
66
|
+
### Development Installation
|
|
67
|
+
|
|
68
|
+
Clone the repository and install in editable mode:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
git clone https://github.com/yourusername/cloudglancer.git
|
|
72
|
+
cd cloudglancer
|
|
73
|
+
python -m venv venv
|
|
74
|
+
source venv/bin/activate
|
|
75
|
+
pip install -e .
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Install with Development Dependencies
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
pip install -e ".[dev]"
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Requirements
|
|
85
|
+
|
|
86
|
+
- Python >= 3.9
|
|
87
|
+
- plotly >= 5.0.0
|
|
88
|
+
- pandas >= 2.0.0
|
|
89
|
+
- numpy >= 1.24.0
|
|
90
|
+
|
|
91
|
+
## API Reference
|
|
92
|
+
|
|
93
|
+
### `cloudglancer.plot()`
|
|
94
|
+
|
|
95
|
+
Create an interactive 3D scatter plot.
|
|
96
|
+
|
|
97
|
+
**Parameters:**
|
|
98
|
+
|
|
99
|
+
- `points` (np.ndarray): Array of shape (n_points, 3) containing 3D coordinates.
|
|
100
|
+
- `labels` (np.ndarray, optional): Array of labels for color grouping.
|
|
101
|
+
- `label_map` (dict, optional): Maps label values to display names. Enables discrete color mapping.
|
|
102
|
+
- `color_map` (list or float, optional):
|
|
103
|
+
- With `label_map`: List of color strings for discrete coloring
|
|
104
|
+
- Without `label_map`: Float specifying the continuous color scale midpoint
|
|
105
|
+
- `size` (float, optional): Size of scatter plot markers. Default is 1.5.
|
|
106
|
+
- `title` (str, optional): Title of the plot.
|
|
107
|
+
|
|
108
|
+
**Returns:**
|
|
109
|
+
|
|
110
|
+
- `plotly.graph_objects.Figure`: Interactive 3D scatter plot.
|
|
111
|
+
|
|
112
|
+
**Raises:**
|
|
113
|
+
|
|
114
|
+
- `ValueError`: If points array is not of shape (n_points, 3).
|
|
115
|
+
|
|
116
|
+
### `cloudglancer.combine_plots()`
|
|
117
|
+
|
|
118
|
+
Combine multiple 3D plots into a single figure with subplots.
|
|
119
|
+
|
|
120
|
+
**Parameters:**
|
|
121
|
+
|
|
122
|
+
- `figs` (list): List of Plotly figures to combine.
|
|
123
|
+
- `rows` (int, optional): Number of rows in the subplot grid. Default is 1.
|
|
124
|
+
- `cols` (int, optional): Number of columns in the subplot grid. Default is 2.
|
|
125
|
+
|
|
126
|
+
**Returns:**
|
|
127
|
+
|
|
128
|
+
- `plotly.graph_objects.Figure`: Combined figure with all plots arranged in a grid.
|
|
129
|
+
|
|
130
|
+
**Raises:**
|
|
131
|
+
|
|
132
|
+
- `ValueError`: If the number of figures doesn't match rows * cols.
|
|
133
|
+
|
|
134
|
+
## Examples
|
|
135
|
+
|
|
136
|
+
See the [examples/](examples/) directory for more detailed usage examples.
|
|
137
|
+
|
|
138
|
+
Run the examples:
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
python examples/basic_usage.py
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Development
|
|
145
|
+
|
|
146
|
+
### Running Tests
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
pytest tests/ -v
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Code Formatting
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
black cloudglancer tests examples
|
|
156
|
+
ruff check cloudglancer tests examples
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Building the Package
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
pip install build
|
|
163
|
+
python -m build
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
This will create both wheel and source distributions in the `dist/` directory.
|
|
167
|
+
|
|
168
|
+
## License
|
|
169
|
+
|
|
170
|
+
MIT License - see [LICENSE](LICENSE) file for details.
|
|
171
|
+
|
|
172
|
+
## Contributing
|
|
173
|
+
|
|
174
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# cloudglancer
|
|
2
|
+
|
|
3
|
+
Simple interactive visualization of 3D point clouds using Plotly.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Interactive 3D scatter plots with pan, zoom, and rotation
|
|
8
|
+
- Support for categorical and continuous color mapping
|
|
9
|
+
- Combine multiple plots into subplot grids
|
|
10
|
+
- Easy-to-use API with sensible defaults
|
|
11
|
+
- Type hints for better IDE support
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install cloudglancer
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
import numpy as np
|
|
23
|
+
import cloudglancer
|
|
24
|
+
|
|
25
|
+
# Generate random 3D points
|
|
26
|
+
points = np.random.randn(500, 3)
|
|
27
|
+
|
|
28
|
+
# Create and display the plot
|
|
29
|
+
fig = cloudglancer.plot(points, title="My Point Cloud", size=2.0)
|
|
30
|
+
fig.show()
|
|
31
|
+
```
|
|
32
|
+
More examples are in the `examples` folder.
|
|
33
|
+
|
|
34
|
+
### Development Installation
|
|
35
|
+
|
|
36
|
+
Clone the repository and install in editable mode:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
git clone https://github.com/yourusername/cloudglancer.git
|
|
40
|
+
cd cloudglancer
|
|
41
|
+
python -m venv venv
|
|
42
|
+
source venv/bin/activate
|
|
43
|
+
pip install -e .
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Install with Development Dependencies
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pip install -e ".[dev]"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Requirements
|
|
53
|
+
|
|
54
|
+
- Python >= 3.9
|
|
55
|
+
- plotly >= 5.0.0
|
|
56
|
+
- pandas >= 2.0.0
|
|
57
|
+
- numpy >= 1.24.0
|
|
58
|
+
|
|
59
|
+
## API Reference
|
|
60
|
+
|
|
61
|
+
### `cloudglancer.plot()`
|
|
62
|
+
|
|
63
|
+
Create an interactive 3D scatter plot.
|
|
64
|
+
|
|
65
|
+
**Parameters:**
|
|
66
|
+
|
|
67
|
+
- `points` (np.ndarray): Array of shape (n_points, 3) containing 3D coordinates.
|
|
68
|
+
- `labels` (np.ndarray, optional): Array of labels for color grouping.
|
|
69
|
+
- `label_map` (dict, optional): Maps label values to display names. Enables discrete color mapping.
|
|
70
|
+
- `color_map` (list or float, optional):
|
|
71
|
+
- With `label_map`: List of color strings for discrete coloring
|
|
72
|
+
- Without `label_map`: Float specifying the continuous color scale midpoint
|
|
73
|
+
- `size` (float, optional): Size of scatter plot markers. Default is 1.5.
|
|
74
|
+
- `title` (str, optional): Title of the plot.
|
|
75
|
+
|
|
76
|
+
**Returns:**
|
|
77
|
+
|
|
78
|
+
- `plotly.graph_objects.Figure`: Interactive 3D scatter plot.
|
|
79
|
+
|
|
80
|
+
**Raises:**
|
|
81
|
+
|
|
82
|
+
- `ValueError`: If points array is not of shape (n_points, 3).
|
|
83
|
+
|
|
84
|
+
### `cloudglancer.combine_plots()`
|
|
85
|
+
|
|
86
|
+
Combine multiple 3D plots into a single figure with subplots.
|
|
87
|
+
|
|
88
|
+
**Parameters:**
|
|
89
|
+
|
|
90
|
+
- `figs` (list): List of Plotly figures to combine.
|
|
91
|
+
- `rows` (int, optional): Number of rows in the subplot grid. Default is 1.
|
|
92
|
+
- `cols` (int, optional): Number of columns in the subplot grid. Default is 2.
|
|
93
|
+
|
|
94
|
+
**Returns:**
|
|
95
|
+
|
|
96
|
+
- `plotly.graph_objects.Figure`: Combined figure with all plots arranged in a grid.
|
|
97
|
+
|
|
98
|
+
**Raises:**
|
|
99
|
+
|
|
100
|
+
- `ValueError`: If the number of figures doesn't match rows * cols.
|
|
101
|
+
|
|
102
|
+
## Examples
|
|
103
|
+
|
|
104
|
+
See the [examples/](examples/) directory for more detailed usage examples.
|
|
105
|
+
|
|
106
|
+
Run the examples:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
python examples/basic_usage.py
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Development
|
|
113
|
+
|
|
114
|
+
### Running Tests
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
pytest tests/ -v
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Code Formatting
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
black cloudglancer tests examples
|
|
124
|
+
ruff check cloudglancer tests examples
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Building the Package
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
pip install build
|
|
131
|
+
python -m build
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
This will create both wheel and source distributions in the `dist/` directory.
|
|
135
|
+
|
|
136
|
+
## License
|
|
137
|
+
|
|
138
|
+
MIT License - see [LICENSE](LICENSE) file for details.
|
|
139
|
+
|
|
140
|
+
## Contributing
|
|
141
|
+
|
|
142
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"""Core scatter plotting functionality for 3D point clouds."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional, List, Dict, Union
|
|
4
|
+
import plotly.express as px
|
|
5
|
+
import pandas as pd
|
|
6
|
+
import numpy as np
|
|
7
|
+
from plotly.graph_objects import Figure
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def plot(
|
|
11
|
+
points: np.ndarray,
|
|
12
|
+
labels: Optional[np.ndarray] = None,
|
|
13
|
+
label_map: Optional[Dict] = None,
|
|
14
|
+
color_map: Optional[Union[List[str], float]] = None,
|
|
15
|
+
size: float = 1.5,
|
|
16
|
+
title: Optional[str] = None
|
|
17
|
+
) -> Figure:
|
|
18
|
+
"""
|
|
19
|
+
Render an interactive 3D scatter plot using Plotly.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
points (np.ndarray): Array of shape (n_points, 3) containing 3D coordinates.
|
|
23
|
+
labels (np.ndarray, optional): Array of labels for color grouping. When provided
|
|
24
|
+
with label_map, enables discrete color mapping. Without label_map, creates
|
|
25
|
+
a continuous color scale.
|
|
26
|
+
label_map (dict, optional): Maps label values to display names. When provided,
|
|
27
|
+
enables discrete color mapping.
|
|
28
|
+
color_map (list or float, optional): When label_map is provided, this should be
|
|
29
|
+
a list of color strings for discrete coloring. Without label_map, this can be
|
|
30
|
+
a float specifying the continuous color scale midpoint.
|
|
31
|
+
size (float, optional): Size of the scatter plot markers. Default is 1.5.
|
|
32
|
+
title (str, optional): Title of the plot.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
plotly.graph_objects.Figure: Interactive 3D scatter plot.
|
|
36
|
+
|
|
37
|
+
Raises:
|
|
38
|
+
ValueError: If points array is not of shape (n_points, 3).
|
|
39
|
+
|
|
40
|
+
Examples:
|
|
41
|
+
Basic scatter plot:
|
|
42
|
+
>>> import numpy as np
|
|
43
|
+
>>> points = np.random.randn(100, 3)
|
|
44
|
+
>>> fig = plot(points, title="Random Points")
|
|
45
|
+
>>> fig.show()
|
|
46
|
+
|
|
47
|
+
Plot with categorical labels:
|
|
48
|
+
>>> labels = np.random.choice([0, 1, 2], size=100)
|
|
49
|
+
>>> label_map = {0: "Class A", 1: "Class B", 2: "Class C"}
|
|
50
|
+
>>> color_map = ["red", "blue", "green"]
|
|
51
|
+
>>> fig = plot(points, labels=labels, label_map=label_map, color_map=color_map)
|
|
52
|
+
>>> fig.show()
|
|
53
|
+
"""
|
|
54
|
+
if points.shape[1] != 3:
|
|
55
|
+
raise ValueError("points must be of shape (n_points, 3)")
|
|
56
|
+
|
|
57
|
+
# Create a DataFrame for easier plotting
|
|
58
|
+
df = pd.DataFrame(points, columns=["x", "y", "z"])
|
|
59
|
+
|
|
60
|
+
if labels is not None:
|
|
61
|
+
df["label"] = labels
|
|
62
|
+
if label_map:
|
|
63
|
+
df["label"] = df["label"].map(label_map).fillna(df["label"])
|
|
64
|
+
fig = px.scatter_3d(df, x="x", y="y", z="z", color="label",
|
|
65
|
+
color_discrete_sequence=color_map)
|
|
66
|
+
else:
|
|
67
|
+
fig = px.scatter_3d(df, x="x", y="y", z="z", color="label",
|
|
68
|
+
color_continuous_midpoint=color_map, range_color=[0, 1])
|
|
69
|
+
else:
|
|
70
|
+
fig = px.scatter_3d(df, x="x", y="y", z="z")
|
|
71
|
+
|
|
72
|
+
fig.update_traces(marker=dict(size=size))
|
|
73
|
+
|
|
74
|
+
# Fix: Apply title if provided
|
|
75
|
+
if title:
|
|
76
|
+
fig.update_layout(title=title)
|
|
77
|
+
|
|
78
|
+
return fig
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def combine_plots(figs: List[Figure], rows: int = 1, cols: int = 2) -> Figure:
|
|
82
|
+
"""
|
|
83
|
+
Combine multiple 3D plots into a single figure with subplots.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
figs (list): List of Plotly figures to combine.
|
|
87
|
+
rows (int, optional): Number of rows in the subplot grid. Default is 1.
|
|
88
|
+
cols (int, optional): Number of columns in the subplot grid. Default is 2.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
plotly.graph_objects.Figure: Combined figure with all plots arranged in a grid.
|
|
92
|
+
|
|
93
|
+
Raises:
|
|
94
|
+
ValueError: If the number of figures doesn't match rows * cols.
|
|
95
|
+
|
|
96
|
+
Examples:
|
|
97
|
+
Combine two plots side by side:
|
|
98
|
+
>>> points1 = np.random.randn(100, 3)
|
|
99
|
+
>>> points2 = np.random.randn(100, 3) + 5
|
|
100
|
+
>>> fig1 = plot(points1, title="Dataset 1")
|
|
101
|
+
>>> fig2 = plot(points2, title="Dataset 2")
|
|
102
|
+
>>> combined = combine_plots([fig1, fig2], rows=1, cols=2)
|
|
103
|
+
>>> combined.show()
|
|
104
|
+
"""
|
|
105
|
+
from plotly.subplots import make_subplots
|
|
106
|
+
|
|
107
|
+
# Fix: Validate figure count
|
|
108
|
+
if len(figs) != rows * cols:
|
|
109
|
+
raise ValueError(
|
|
110
|
+
f"Number of figures ({len(figs)}) must equal rows * cols ({rows * cols})"
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
# Fix: Generate specs dynamically based on rows and cols
|
|
114
|
+
specs = [[{"type": "scene"} for _ in range(cols)] for _ in range(rows)]
|
|
115
|
+
|
|
116
|
+
combined_fig = make_subplots(
|
|
117
|
+
rows=rows, cols=cols,
|
|
118
|
+
specs=specs
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
for i, fig in enumerate(figs):
|
|
122
|
+
for trace in fig.data:
|
|
123
|
+
combined_fig.add_trace(trace, row=(i // cols) + 1, col=(i % cols) + 1)
|
|
124
|
+
|
|
125
|
+
combined_fig.update_layout(
|
|
126
|
+
margin=dict(l=0, r=0, t=0, b=0)
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
return combined_fig
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cloudglancer
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Simple interactive visualization of 3D point clouds
|
|
5
|
+
Author-email: Your Name <your.email@example.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/yourusername/cloudglancer
|
|
8
|
+
Project-URL: Repository, https://github.com/yourusername/cloudglancer
|
|
9
|
+
Project-URL: Issues, https://github.com/yourusername/cloudglancer/issues
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
20
|
+
Requires-Python: >=3.9
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: plotly>=5.0.0
|
|
24
|
+
Requires-Dist: pandas>=2.0.0
|
|
25
|
+
Requires-Dist: numpy>=1.24.0
|
|
26
|
+
Requires-Dist: nbformat>=4.2.0
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
29
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
30
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
|
|
33
|
+
# cloudglancer
|
|
34
|
+
|
|
35
|
+
Simple interactive visualization of 3D point clouds using Plotly.
|
|
36
|
+
|
|
37
|
+
## Features
|
|
38
|
+
|
|
39
|
+
- Interactive 3D scatter plots with pan, zoom, and rotation
|
|
40
|
+
- Support for categorical and continuous color mapping
|
|
41
|
+
- Combine multiple plots into subplot grids
|
|
42
|
+
- Easy-to-use API with sensible defaults
|
|
43
|
+
- Type hints for better IDE support
|
|
44
|
+
|
|
45
|
+
## Installation
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install cloudglancer
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Quick Start
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
import numpy as np
|
|
55
|
+
import cloudglancer
|
|
56
|
+
|
|
57
|
+
# Generate random 3D points
|
|
58
|
+
points = np.random.randn(500, 3)
|
|
59
|
+
|
|
60
|
+
# Create and display the plot
|
|
61
|
+
fig = cloudglancer.plot(points, title="My Point Cloud", size=2.0)
|
|
62
|
+
fig.show()
|
|
63
|
+
```
|
|
64
|
+
More examples are in the `examples` folder.
|
|
65
|
+
|
|
66
|
+
### Development Installation
|
|
67
|
+
|
|
68
|
+
Clone the repository and install in editable mode:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
git clone https://github.com/yourusername/cloudglancer.git
|
|
72
|
+
cd cloudglancer
|
|
73
|
+
python -m venv venv
|
|
74
|
+
source venv/bin/activate
|
|
75
|
+
pip install -e .
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Install with Development Dependencies
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
pip install -e ".[dev]"
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Requirements
|
|
85
|
+
|
|
86
|
+
- Python >= 3.9
|
|
87
|
+
- plotly >= 5.0.0
|
|
88
|
+
- pandas >= 2.0.0
|
|
89
|
+
- numpy >= 1.24.0
|
|
90
|
+
|
|
91
|
+
## API Reference
|
|
92
|
+
|
|
93
|
+
### `cloudglancer.plot()`
|
|
94
|
+
|
|
95
|
+
Create an interactive 3D scatter plot.
|
|
96
|
+
|
|
97
|
+
**Parameters:**
|
|
98
|
+
|
|
99
|
+
- `points` (np.ndarray): Array of shape (n_points, 3) containing 3D coordinates.
|
|
100
|
+
- `labels` (np.ndarray, optional): Array of labels for color grouping.
|
|
101
|
+
- `label_map` (dict, optional): Maps label values to display names. Enables discrete color mapping.
|
|
102
|
+
- `color_map` (list or float, optional):
|
|
103
|
+
- With `label_map`: List of color strings for discrete coloring
|
|
104
|
+
- Without `label_map`: Float specifying the continuous color scale midpoint
|
|
105
|
+
- `size` (float, optional): Size of scatter plot markers. Default is 1.5.
|
|
106
|
+
- `title` (str, optional): Title of the plot.
|
|
107
|
+
|
|
108
|
+
**Returns:**
|
|
109
|
+
|
|
110
|
+
- `plotly.graph_objects.Figure`: Interactive 3D scatter plot.
|
|
111
|
+
|
|
112
|
+
**Raises:**
|
|
113
|
+
|
|
114
|
+
- `ValueError`: If points array is not of shape (n_points, 3).
|
|
115
|
+
|
|
116
|
+
### `cloudglancer.combine_plots()`
|
|
117
|
+
|
|
118
|
+
Combine multiple 3D plots into a single figure with subplots.
|
|
119
|
+
|
|
120
|
+
**Parameters:**
|
|
121
|
+
|
|
122
|
+
- `figs` (list): List of Plotly figures to combine.
|
|
123
|
+
- `rows` (int, optional): Number of rows in the subplot grid. Default is 1.
|
|
124
|
+
- `cols` (int, optional): Number of columns in the subplot grid. Default is 2.
|
|
125
|
+
|
|
126
|
+
**Returns:**
|
|
127
|
+
|
|
128
|
+
- `plotly.graph_objects.Figure`: Combined figure with all plots arranged in a grid.
|
|
129
|
+
|
|
130
|
+
**Raises:**
|
|
131
|
+
|
|
132
|
+
- `ValueError`: If the number of figures doesn't match rows * cols.
|
|
133
|
+
|
|
134
|
+
## Examples
|
|
135
|
+
|
|
136
|
+
See the [examples/](examples/) directory for more detailed usage examples.
|
|
137
|
+
|
|
138
|
+
Run the examples:
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
python examples/basic_usage.py
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Development
|
|
145
|
+
|
|
146
|
+
### Running Tests
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
pytest tests/ -v
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Code Formatting
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
black cloudglancer tests examples
|
|
156
|
+
ruff check cloudglancer tests examples
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Building the Package
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
pip install build
|
|
163
|
+
python -m build
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
This will create both wheel and source distributions in the `dist/` directory.
|
|
167
|
+
|
|
168
|
+
## License
|
|
169
|
+
|
|
170
|
+
MIT License - see [LICENSE](LICENSE) file for details.
|
|
171
|
+
|
|
172
|
+
## Contributing
|
|
173
|
+
|
|
174
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
MANIFEST.in
|
|
3
|
+
README.md
|
|
4
|
+
pyproject.toml
|
|
5
|
+
cloudglancer/__init__.py
|
|
6
|
+
cloudglancer/scatter.py
|
|
7
|
+
cloudglancer.egg-info/PKG-INFO
|
|
8
|
+
cloudglancer.egg-info/SOURCES.txt
|
|
9
|
+
cloudglancer.egg-info/dependency_links.txt
|
|
10
|
+
cloudglancer.egg-info/requires.txt
|
|
11
|
+
cloudglancer.egg-info/top_level.txt
|
|
12
|
+
tests/test_scatter.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
cloudglancer
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "cloudglancer"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Simple interactive visualization of 3D point clouds"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Your Name", email = "your.email@example.com"}
|
|
14
|
+
]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 3 - Alpha",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"Intended Audience :: Science/Research",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.9",
|
|
22
|
+
"Programming Language :: Python :: 3.10",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Topic :: Scientific/Engineering :: Visualization",
|
|
26
|
+
]
|
|
27
|
+
dependencies = [
|
|
28
|
+
"plotly>=5.0.0",
|
|
29
|
+
"pandas>=2.0.0",
|
|
30
|
+
"numpy>=1.24.0",
|
|
31
|
+
"nbformat>=4.2.0",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[project.optional-dependencies]
|
|
35
|
+
dev = [
|
|
36
|
+
"pytest>=7.0.0",
|
|
37
|
+
"black>=23.0.0",
|
|
38
|
+
"ruff>=0.1.0",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
[project.urls]
|
|
42
|
+
Homepage = "https://github.com/yourusername/cloudglancer"
|
|
43
|
+
Repository = "https://github.com/yourusername/cloudglancer"
|
|
44
|
+
Issues = "https://github.com/yourusername/cloudglancer/issues"
|
|
45
|
+
|
|
46
|
+
[tool.setuptools.packages.find]
|
|
47
|
+
include = ["cloudglancer*"]
|
|
48
|
+
|
|
49
|
+
[tool.setuptools.package-data]
|
|
50
|
+
cloudglancer = ["py.typed"]
|
|
51
|
+
|
|
52
|
+
[tool.black]
|
|
53
|
+
line-length = 100
|
|
54
|
+
target-version = ["py39"]
|
|
55
|
+
|
|
56
|
+
[tool.ruff]
|
|
57
|
+
line-length = 100
|
|
58
|
+
target-version = "py39"
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""Unit tests for cloudglancer.scatter module."""
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import pytest
|
|
5
|
+
from cloudglancer import plot, combine_plots
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_plot_basic():
|
|
9
|
+
"""Test basic plot creation with random points."""
|
|
10
|
+
points = np.random.randn(100, 3)
|
|
11
|
+
fig = plot(points)
|
|
12
|
+
assert fig is not None
|
|
13
|
+
assert len(fig.data) == 1
|
|
14
|
+
assert fig.data[0].type == "scatter3d"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_plot_with_labels():
|
|
18
|
+
"""Test plot with continuous labels."""
|
|
19
|
+
points = np.random.randn(100, 3)
|
|
20
|
+
labels = np.random.rand(100)
|
|
21
|
+
fig = plot(points, labels=labels)
|
|
22
|
+
assert fig is not None
|
|
23
|
+
assert len(fig.data) == 1
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_plot_with_label_map():
|
|
27
|
+
"""Test plot with discrete color mapping."""
|
|
28
|
+
points = np.random.randn(100, 3)
|
|
29
|
+
labels = np.random.choice([0, 1, 2], size=100)
|
|
30
|
+
label_map = {0: "Class A", 1: "Class B", 2: "Class C"}
|
|
31
|
+
color_map = ["red", "blue", "green"]
|
|
32
|
+
fig = plot(points, labels=labels, label_map=label_map, color_map=color_map)
|
|
33
|
+
assert fig is not None
|
|
34
|
+
assert len(fig.data) > 0
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_plot_invalid_shape():
|
|
38
|
+
"""Test that ValueError is raised for incorrect point shape."""
|
|
39
|
+
points = np.random.randn(100, 2) # Wrong shape
|
|
40
|
+
with pytest.raises(ValueError, match="points must be of shape"):
|
|
41
|
+
plot(points)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_plot_title():
|
|
45
|
+
"""Test that title is correctly applied to the figure."""
|
|
46
|
+
points = np.random.randn(100, 3)
|
|
47
|
+
title = "Test Plot Title"
|
|
48
|
+
fig = plot(points, title=title)
|
|
49
|
+
assert fig.layout.title.text == title
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def test_plot_title_none():
|
|
53
|
+
"""Test that plot works without a title."""
|
|
54
|
+
points = np.random.randn(100, 3)
|
|
55
|
+
fig = plot(points, title=None)
|
|
56
|
+
assert fig is not None
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def test_combine_plots():
|
|
60
|
+
"""Test combining multiple plots."""
|
|
61
|
+
points1 = np.random.randn(50, 3)
|
|
62
|
+
points2 = np.random.randn(50, 3)
|
|
63
|
+
fig1 = plot(points1)
|
|
64
|
+
fig2 = plot(points2)
|
|
65
|
+
combined = combine_plots([fig1, fig2], rows=1, cols=2)
|
|
66
|
+
assert combined is not None
|
|
67
|
+
assert len(combined.data) == 2
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_combine_plots_grid():
|
|
71
|
+
"""Test combining plots in a 2x2 grid."""
|
|
72
|
+
figs = [plot(np.random.randn(30, 3)) for _ in range(4)]
|
|
73
|
+
combined = combine_plots(figs, rows=2, cols=2)
|
|
74
|
+
assert combined is not None
|
|
75
|
+
assert len(combined.data) == 4
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def test_combine_plots_invalid_count():
|
|
79
|
+
"""Test that ValueError is raised when figure count doesn't match grid."""
|
|
80
|
+
fig1 = plot(np.random.randn(50, 3))
|
|
81
|
+
fig2 = plot(np.random.randn(50, 3))
|
|
82
|
+
with pytest.raises(ValueError, match="Number of figures"):
|
|
83
|
+
combine_plots([fig1, fig2], rows=2, cols=2) # 2 figs, but 2x2 = 4 spaces
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def test_plot_size_parameter():
|
|
87
|
+
"""Test that marker size parameter is applied."""
|
|
88
|
+
points = np.random.randn(100, 3)
|
|
89
|
+
size = 3.0
|
|
90
|
+
fig = plot(points, size=size)
|
|
91
|
+
assert fig.data[0].marker.size == size
|