gemmology-cdl-parser 1.0.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.
- gemmology_cdl_parser-1.0.0/LICENSE +21 -0
- gemmology_cdl_parser-1.0.0/PKG-INFO +345 -0
- gemmology_cdl_parser-1.0.0/README.md +304 -0
- gemmology_cdl_parser-1.0.0/pyproject.toml +117 -0
- gemmology_cdl_parser-1.0.0/setup.cfg +4 -0
- gemmology_cdl_parser-1.0.0/src/cdl_parser/__init__.py +87 -0
- gemmology_cdl_parser-1.0.0/src/cdl_parser/cli.py +140 -0
- gemmology_cdl_parser-1.0.0/src/cdl_parser/constants.py +133 -0
- gemmology_cdl_parser-1.0.0/src/cdl_parser/exceptions.py +67 -0
- gemmology_cdl_parser-1.0.0/src/cdl_parser/models.py +216 -0
- gemmology_cdl_parser-1.0.0/src/cdl_parser/parser.py +601 -0
- gemmology_cdl_parser-1.0.0/src/cdl_parser/py.typed +0 -0
- gemmology_cdl_parser-1.0.0/src/gemmology_cdl_parser.egg-info/PKG-INFO +345 -0
- gemmology_cdl_parser-1.0.0/src/gemmology_cdl_parser.egg-info/SOURCES.txt +17 -0
- gemmology_cdl_parser-1.0.0/src/gemmology_cdl_parser.egg-info/dependency_links.txt +1 -0
- gemmology_cdl_parser-1.0.0/src/gemmology_cdl_parser.egg-info/entry_points.txt +2 -0
- gemmology_cdl_parser-1.0.0/src/gemmology_cdl_parser.egg-info/requires.txt +11 -0
- gemmology_cdl_parser-1.0.0/src/gemmology_cdl_parser.egg-info/top_level.txt +1 -0
- gemmology_cdl_parser-1.0.0/tests/test_parser.py +526 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 gemmology.dev
|
|
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,345 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gemmology-cdl-parser
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Crystal Description Language (CDL) parser for crystallographic visualization
|
|
5
|
+
Author-email: Fabian Schuh <fabian@gemmology.dev>
|
|
6
|
+
Maintainer-email: Fabian Schuh <fabian@gemmology.dev>
|
|
7
|
+
License: MIT
|
|
8
|
+
Project-URL: Homepage, https://cdl-parser.gemmology.dev
|
|
9
|
+
Project-URL: Documentation, https://cdl-parser.gemmology.dev/docs
|
|
10
|
+
Project-URL: Repository, https://github.com/gemmology-dev/cdl-parser
|
|
11
|
+
Project-URL: Issues, https://github.com/gemmology-dev/cdl-parser/issues
|
|
12
|
+
Project-URL: Changelog, https://github.com/gemmology-dev/cdl-parser/blob/main/CHANGELOG.md
|
|
13
|
+
Keywords: crystallography,mineralogy,gemmology,parser,crystal,miller-index,crystal-morphology,visualization
|
|
14
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
15
|
+
Classifier: Intended Audience :: Science/Research
|
|
16
|
+
Classifier: Intended Audience :: Education
|
|
17
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
+
Classifier: Topic :: Scientific/Engineering
|
|
25
|
+
Classifier: Topic :: Scientific/Engineering :: Chemistry
|
|
26
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
27
|
+
Classifier: Typing :: Typed
|
|
28
|
+
Requires-Python: >=3.10
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
License-File: LICENSE
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
33
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
34
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
35
|
+
Requires-Dist: mypy>=1.0; extra == "dev"
|
|
36
|
+
Provides-Extra: docs
|
|
37
|
+
Requires-Dist: mkdocs>=1.5; extra == "docs"
|
|
38
|
+
Requires-Dist: mkdocs-material>=9.0; extra == "docs"
|
|
39
|
+
Requires-Dist: mkdocstrings[python]>=0.24; extra == "docs"
|
|
40
|
+
Dynamic: license-file
|
|
41
|
+
|
|
42
|
+
# cdl-parser
|
|
43
|
+
|
|
44
|
+
[](https://badge.fury.io/py/cdl-parser)
|
|
45
|
+
[](https://pypi.org/project/cdl-parser/)
|
|
46
|
+
[](https://opensource.org/licenses/MIT)
|
|
47
|
+
|
|
48
|
+
**Crystal Description Language (CDL) Parser** - A Python library for parsing compact string notation describing crystal morphology for gemmological and mineralogical visualization.
|
|
49
|
+
|
|
50
|
+
Part of the [Gemmology Project](https://gemmology.dev).
|
|
51
|
+
|
|
52
|
+
## Installation
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
pip install cdl-parser
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Quick Start
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from cdl_parser import parse_cdl
|
|
62
|
+
|
|
63
|
+
# Parse a simple octahedron
|
|
64
|
+
desc = parse_cdl("cubic[m3m]:{111}")
|
|
65
|
+
print(desc.system) # 'cubic'
|
|
66
|
+
print(desc.point_group) # 'm3m'
|
|
67
|
+
|
|
68
|
+
# Parse a truncated octahedron (diamond-like)
|
|
69
|
+
desc = parse_cdl("cubic[m3m]:{111}@1.0 + {100}@1.3")
|
|
70
|
+
print(len(desc.forms)) # 2
|
|
71
|
+
|
|
72
|
+
# Parse with twin specification
|
|
73
|
+
desc = parse_cdl("cubic[m3m]:{111} | twin(spinel)")
|
|
74
|
+
print(desc.twin.law) # 'spinel'
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## CDL Specification
|
|
78
|
+
|
|
79
|
+
### Syntax Overview
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
system[point_group]:{form}@scale + {form}@scale | modification | twin(law)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Crystal Systems
|
|
86
|
+
|
|
87
|
+
All 7 crystal systems are supported with their standard point groups:
|
|
88
|
+
|
|
89
|
+
| System | Default Point Group | All Point Groups |
|
|
90
|
+
|--------|---------------------|------------------|
|
|
91
|
+
| cubic | m3m | m3m, 432, -43m, m-3, 23 |
|
|
92
|
+
| hexagonal | 6/mmm | 6/mmm, 622, 6mm, -6m2, 6/m, -6, 6 |
|
|
93
|
+
| trigonal | -3m | -3m, 32, 3m, -3, 3 |
|
|
94
|
+
| tetragonal | 4/mmm | 4/mmm, 422, 4mm, -42m, 4/m, -4, 4 |
|
|
95
|
+
| orthorhombic | mmm | mmm, 222, mm2 |
|
|
96
|
+
| monoclinic | 2/m | 2/m, m, 2 |
|
|
97
|
+
| triclinic | -1 | -1, 1 |
|
|
98
|
+
|
|
99
|
+
### Miller Indices
|
|
100
|
+
|
|
101
|
+
Forms are specified using Miller indices in curly braces:
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
# 3-index notation (hkl)
|
|
105
|
+
"{111}" # Octahedron face
|
|
106
|
+
"{100}" # Cube face
|
|
107
|
+
"{110}" # Dodecahedron face
|
|
108
|
+
|
|
109
|
+
# 4-index notation for hexagonal/trigonal (hkil where i = -(h+k))
|
|
110
|
+
"{10-10}" # Hexagonal prism
|
|
111
|
+
"{10-11}" # Rhombohedron
|
|
112
|
+
"{0001}" # Basal pinacoid
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Named Forms
|
|
116
|
+
|
|
117
|
+
Common forms can be referenced by name:
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
# Cubic named forms
|
|
121
|
+
"octahedron" # → {111}
|
|
122
|
+
"cube" # → {100}
|
|
123
|
+
"dodecahedron" # → {110}
|
|
124
|
+
"trapezohedron" # → {211}
|
|
125
|
+
|
|
126
|
+
# Hexagonal/Trigonal named forms
|
|
127
|
+
"prism" # → {10-10}
|
|
128
|
+
"basal" # → {0001}
|
|
129
|
+
"rhombohedron" # → {10-11}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Scale Values
|
|
133
|
+
|
|
134
|
+
The `@scale` parameter controls the relative prominence of forms:
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
# Larger scale = form appears more (more truncation)
|
|
138
|
+
"{111}@1.0 + {100}@1.3" # Cube truncates the octahedron
|
|
139
|
+
"{111}@1.0 + {100}@0.3" # Octahedron with small cube facets
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Twin Laws
|
|
143
|
+
|
|
144
|
+
Named twin laws for common crystal twins:
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
# Contact twins
|
|
148
|
+
"twin(spinel)" # Spinel law (111) twin
|
|
149
|
+
"twin(brazil)" # Brazil law quartz twin
|
|
150
|
+
"twin(japan)" # Japan law quartz twin
|
|
151
|
+
|
|
152
|
+
# Penetration twins
|
|
153
|
+
"twin(fluorite)" # Fluorite interpenetration twin
|
|
154
|
+
"twin(iron_cross)" # Iron cross pyrite twin
|
|
155
|
+
|
|
156
|
+
# Cyclic twins
|
|
157
|
+
"twin(trilling,3)" # Three-fold cyclic twin
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Complete Examples
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
# Diamond (octahedron with cube truncation)
|
|
164
|
+
"cubic[m3m]:{111}@1.0 + {100}@0.3"
|
|
165
|
+
|
|
166
|
+
# Quartz prism with rhombohedron termination
|
|
167
|
+
"trigonal[-3m]:{10-10}@1.0 + {10-11}@0.8"
|
|
168
|
+
|
|
169
|
+
# Garnet (dodecahedron + trapezohedron)
|
|
170
|
+
"cubic[m3m]:{110}@1.0 + {211}@0.6"
|
|
171
|
+
|
|
172
|
+
# Spinel-law twinned octahedron
|
|
173
|
+
"cubic[m3m]:{111} | twin(spinel)"
|
|
174
|
+
|
|
175
|
+
# Fluorite cube
|
|
176
|
+
"cubic[m3m]:{100}"
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## API Reference
|
|
180
|
+
|
|
181
|
+
### Core Functions
|
|
182
|
+
|
|
183
|
+
#### `parse_cdl(text: str) -> CrystalDescription`
|
|
184
|
+
|
|
185
|
+
Parse a CDL string into a structured description.
|
|
186
|
+
|
|
187
|
+
```python
|
|
188
|
+
from cdl_parser import parse_cdl
|
|
189
|
+
|
|
190
|
+
desc = parse_cdl("cubic[m3m]:{111}@1.0 + {100}@1.3")
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
#### `validate_cdl(text: str) -> tuple[bool, str | None]`
|
|
194
|
+
|
|
195
|
+
Validate a CDL string without parsing.
|
|
196
|
+
|
|
197
|
+
```python
|
|
198
|
+
from cdl_parser import validate_cdl
|
|
199
|
+
|
|
200
|
+
is_valid, error = validate_cdl("cubic[m3m]:{111}")
|
|
201
|
+
if not is_valid:
|
|
202
|
+
print(f"Error: {error}")
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Data Classes
|
|
206
|
+
|
|
207
|
+
#### `CrystalDescription`
|
|
208
|
+
|
|
209
|
+
Main output of CDL parsing.
|
|
210
|
+
|
|
211
|
+
```python
|
|
212
|
+
@dataclass
|
|
213
|
+
class CrystalDescription:
|
|
214
|
+
system: str # Crystal system
|
|
215
|
+
point_group: str # Point group symbol
|
|
216
|
+
forms: List[CrystalForm] # Crystal forms
|
|
217
|
+
modifications: List[Modification] # Morphological mods
|
|
218
|
+
twin: Optional[TwinSpec] # Twin specification
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
#### `MillerIndex`
|
|
222
|
+
|
|
223
|
+
Miller index representation.
|
|
224
|
+
|
|
225
|
+
```python
|
|
226
|
+
@dataclass
|
|
227
|
+
class MillerIndex:
|
|
228
|
+
h: int
|
|
229
|
+
k: int
|
|
230
|
+
l: int
|
|
231
|
+
i: Optional[int] = None # For 4-index notation
|
|
232
|
+
|
|
233
|
+
def as_tuple(self) -> tuple[int, ...]
|
|
234
|
+
def as_3index(self) -> tuple[int, int, int]
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
#### `CrystalForm`
|
|
238
|
+
|
|
239
|
+
A crystal form with scale.
|
|
240
|
+
|
|
241
|
+
```python
|
|
242
|
+
@dataclass
|
|
243
|
+
class CrystalForm:
|
|
244
|
+
miller: MillerIndex
|
|
245
|
+
scale: float = 1.0
|
|
246
|
+
name: Optional[str] = None
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Constants
|
|
250
|
+
|
|
251
|
+
```python
|
|
252
|
+
from cdl_parser import (
|
|
253
|
+
CRYSTAL_SYSTEMS, # Set of system names
|
|
254
|
+
POINT_GROUPS, # Dict[system, Set[groups]]
|
|
255
|
+
DEFAULT_POINT_GROUPS, # Dict[system, default_group]
|
|
256
|
+
NAMED_FORMS, # Dict[name, (h, k, l)]
|
|
257
|
+
TWIN_LAWS, # Set of twin law names
|
|
258
|
+
)
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Exceptions
|
|
262
|
+
|
|
263
|
+
```python
|
|
264
|
+
from cdl_parser import ParseError, ValidationError
|
|
265
|
+
|
|
266
|
+
try:
|
|
267
|
+
desc = parse_cdl("invalid{{{")
|
|
268
|
+
except ParseError as e:
|
|
269
|
+
print(f"Syntax error at position {e.position}: {e.message}")
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
## CLI Usage
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
# Parse and display
|
|
276
|
+
cdl parse "cubic[m3m]:{111}@1.0 + {100}@1.3"
|
|
277
|
+
|
|
278
|
+
# Validate
|
|
279
|
+
cdl validate "cubic[m3m]:{111}"
|
|
280
|
+
|
|
281
|
+
# Output as JSON
|
|
282
|
+
cdl parse "cubic[m3m]:{111}" --json
|
|
283
|
+
|
|
284
|
+
# List available options
|
|
285
|
+
cdl --list-systems
|
|
286
|
+
cdl --list-point-groups
|
|
287
|
+
cdl --list-forms
|
|
288
|
+
cdl --list-twins
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
## Integration with Other Packages
|
|
292
|
+
|
|
293
|
+
cdl-parser is designed to work with the Gemmology Project ecosystem:
|
|
294
|
+
|
|
295
|
+
```python
|
|
296
|
+
from cdl_parser import parse_cdl
|
|
297
|
+
from crystal_geometry import cdl_to_geometry
|
|
298
|
+
from crystal_renderer import generate_cdl_svg, generate_geometry_svg
|
|
299
|
+
|
|
300
|
+
# Option 1: Direct CDL string to SVG
|
|
301
|
+
cdl = "cubic[m3m]:{111}@1.0 + {100}@1.3"
|
|
302
|
+
generate_cdl_svg(cdl, "crystal.svg")
|
|
303
|
+
|
|
304
|
+
# Option 2: Parse, then generate geometry for custom processing
|
|
305
|
+
desc = parse_cdl(cdl)
|
|
306
|
+
geometry = cdl_to_geometry(desc)
|
|
307
|
+
|
|
308
|
+
# Render geometry directly
|
|
309
|
+
generate_geometry_svg(geometry.vertices, geometry.faces, "geometry.svg")
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
## Development
|
|
313
|
+
|
|
314
|
+
```bash
|
|
315
|
+
# Clone the repository
|
|
316
|
+
git clone https://github.com/gemmology-dev/cdl-parser.git
|
|
317
|
+
cd cdl-parser
|
|
318
|
+
|
|
319
|
+
# Install with dev dependencies
|
|
320
|
+
pip install -e ".[dev]"
|
|
321
|
+
|
|
322
|
+
# Run tests
|
|
323
|
+
pytest
|
|
324
|
+
|
|
325
|
+
# Type checking
|
|
326
|
+
mypy src/cdl_parser
|
|
327
|
+
|
|
328
|
+
# Linting
|
|
329
|
+
ruff check src/cdl_parser
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
## Documentation
|
|
333
|
+
|
|
334
|
+
Full documentation is available at [cdl-parser.gemmology.dev](https://cdl-parser.gemmology.dev).
|
|
335
|
+
|
|
336
|
+
## License
|
|
337
|
+
|
|
338
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
339
|
+
|
|
340
|
+
## Related Projects
|
|
341
|
+
|
|
342
|
+
- [crystal-geometry](https://github.com/gemmology-dev/crystal-geometry) - 3D geometry from CDL
|
|
343
|
+
- [mineral-database](https://github.com/gemmology-dev/mineral-database) - Mineral presets
|
|
344
|
+
- [crystal-renderer](https://github.com/gemmology-dev/crystal-renderer) - SVG/3D rendering
|
|
345
|
+
- [gemmology-plugin](https://github.com/gemmology-dev/gemmology-plugin) - Claude Code plugin
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
# cdl-parser
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/py/cdl-parser)
|
|
4
|
+
[](https://pypi.org/project/cdl-parser/)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
7
|
+
**Crystal Description Language (CDL) Parser** - A Python library for parsing compact string notation describing crystal morphology for gemmological and mineralogical visualization.
|
|
8
|
+
|
|
9
|
+
Part of the [Gemmology Project](https://gemmology.dev).
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install cdl-parser
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Quick Start
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
from cdl_parser import parse_cdl
|
|
21
|
+
|
|
22
|
+
# Parse a simple octahedron
|
|
23
|
+
desc = parse_cdl("cubic[m3m]:{111}")
|
|
24
|
+
print(desc.system) # 'cubic'
|
|
25
|
+
print(desc.point_group) # 'm3m'
|
|
26
|
+
|
|
27
|
+
# Parse a truncated octahedron (diamond-like)
|
|
28
|
+
desc = parse_cdl("cubic[m3m]:{111}@1.0 + {100}@1.3")
|
|
29
|
+
print(len(desc.forms)) # 2
|
|
30
|
+
|
|
31
|
+
# Parse with twin specification
|
|
32
|
+
desc = parse_cdl("cubic[m3m]:{111} | twin(spinel)")
|
|
33
|
+
print(desc.twin.law) # 'spinel'
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## CDL Specification
|
|
37
|
+
|
|
38
|
+
### Syntax Overview
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
system[point_group]:{form}@scale + {form}@scale | modification | twin(law)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Crystal Systems
|
|
45
|
+
|
|
46
|
+
All 7 crystal systems are supported with their standard point groups:
|
|
47
|
+
|
|
48
|
+
| System | Default Point Group | All Point Groups |
|
|
49
|
+
|--------|---------------------|------------------|
|
|
50
|
+
| cubic | m3m | m3m, 432, -43m, m-3, 23 |
|
|
51
|
+
| hexagonal | 6/mmm | 6/mmm, 622, 6mm, -6m2, 6/m, -6, 6 |
|
|
52
|
+
| trigonal | -3m | -3m, 32, 3m, -3, 3 |
|
|
53
|
+
| tetragonal | 4/mmm | 4/mmm, 422, 4mm, -42m, 4/m, -4, 4 |
|
|
54
|
+
| orthorhombic | mmm | mmm, 222, mm2 |
|
|
55
|
+
| monoclinic | 2/m | 2/m, m, 2 |
|
|
56
|
+
| triclinic | -1 | -1, 1 |
|
|
57
|
+
|
|
58
|
+
### Miller Indices
|
|
59
|
+
|
|
60
|
+
Forms are specified using Miller indices in curly braces:
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
# 3-index notation (hkl)
|
|
64
|
+
"{111}" # Octahedron face
|
|
65
|
+
"{100}" # Cube face
|
|
66
|
+
"{110}" # Dodecahedron face
|
|
67
|
+
|
|
68
|
+
# 4-index notation for hexagonal/trigonal (hkil where i = -(h+k))
|
|
69
|
+
"{10-10}" # Hexagonal prism
|
|
70
|
+
"{10-11}" # Rhombohedron
|
|
71
|
+
"{0001}" # Basal pinacoid
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Named Forms
|
|
75
|
+
|
|
76
|
+
Common forms can be referenced by name:
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
# Cubic named forms
|
|
80
|
+
"octahedron" # → {111}
|
|
81
|
+
"cube" # → {100}
|
|
82
|
+
"dodecahedron" # → {110}
|
|
83
|
+
"trapezohedron" # → {211}
|
|
84
|
+
|
|
85
|
+
# Hexagonal/Trigonal named forms
|
|
86
|
+
"prism" # → {10-10}
|
|
87
|
+
"basal" # → {0001}
|
|
88
|
+
"rhombohedron" # → {10-11}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Scale Values
|
|
92
|
+
|
|
93
|
+
The `@scale` parameter controls the relative prominence of forms:
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
# Larger scale = form appears more (more truncation)
|
|
97
|
+
"{111}@1.0 + {100}@1.3" # Cube truncates the octahedron
|
|
98
|
+
"{111}@1.0 + {100}@0.3" # Octahedron with small cube facets
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Twin Laws
|
|
102
|
+
|
|
103
|
+
Named twin laws for common crystal twins:
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
# Contact twins
|
|
107
|
+
"twin(spinel)" # Spinel law (111) twin
|
|
108
|
+
"twin(brazil)" # Brazil law quartz twin
|
|
109
|
+
"twin(japan)" # Japan law quartz twin
|
|
110
|
+
|
|
111
|
+
# Penetration twins
|
|
112
|
+
"twin(fluorite)" # Fluorite interpenetration twin
|
|
113
|
+
"twin(iron_cross)" # Iron cross pyrite twin
|
|
114
|
+
|
|
115
|
+
# Cyclic twins
|
|
116
|
+
"twin(trilling,3)" # Three-fold cyclic twin
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Complete Examples
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
# Diamond (octahedron with cube truncation)
|
|
123
|
+
"cubic[m3m]:{111}@1.0 + {100}@0.3"
|
|
124
|
+
|
|
125
|
+
# Quartz prism with rhombohedron termination
|
|
126
|
+
"trigonal[-3m]:{10-10}@1.0 + {10-11}@0.8"
|
|
127
|
+
|
|
128
|
+
# Garnet (dodecahedron + trapezohedron)
|
|
129
|
+
"cubic[m3m]:{110}@1.0 + {211}@0.6"
|
|
130
|
+
|
|
131
|
+
# Spinel-law twinned octahedron
|
|
132
|
+
"cubic[m3m]:{111} | twin(spinel)"
|
|
133
|
+
|
|
134
|
+
# Fluorite cube
|
|
135
|
+
"cubic[m3m]:{100}"
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## API Reference
|
|
139
|
+
|
|
140
|
+
### Core Functions
|
|
141
|
+
|
|
142
|
+
#### `parse_cdl(text: str) -> CrystalDescription`
|
|
143
|
+
|
|
144
|
+
Parse a CDL string into a structured description.
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
from cdl_parser import parse_cdl
|
|
148
|
+
|
|
149
|
+
desc = parse_cdl("cubic[m3m]:{111}@1.0 + {100}@1.3")
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
#### `validate_cdl(text: str) -> tuple[bool, str | None]`
|
|
153
|
+
|
|
154
|
+
Validate a CDL string without parsing.
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
from cdl_parser import validate_cdl
|
|
158
|
+
|
|
159
|
+
is_valid, error = validate_cdl("cubic[m3m]:{111}")
|
|
160
|
+
if not is_valid:
|
|
161
|
+
print(f"Error: {error}")
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Data Classes
|
|
165
|
+
|
|
166
|
+
#### `CrystalDescription`
|
|
167
|
+
|
|
168
|
+
Main output of CDL parsing.
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
@dataclass
|
|
172
|
+
class CrystalDescription:
|
|
173
|
+
system: str # Crystal system
|
|
174
|
+
point_group: str # Point group symbol
|
|
175
|
+
forms: List[CrystalForm] # Crystal forms
|
|
176
|
+
modifications: List[Modification] # Morphological mods
|
|
177
|
+
twin: Optional[TwinSpec] # Twin specification
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
#### `MillerIndex`
|
|
181
|
+
|
|
182
|
+
Miller index representation.
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
@dataclass
|
|
186
|
+
class MillerIndex:
|
|
187
|
+
h: int
|
|
188
|
+
k: int
|
|
189
|
+
l: int
|
|
190
|
+
i: Optional[int] = None # For 4-index notation
|
|
191
|
+
|
|
192
|
+
def as_tuple(self) -> tuple[int, ...]
|
|
193
|
+
def as_3index(self) -> tuple[int, int, int]
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
#### `CrystalForm`
|
|
197
|
+
|
|
198
|
+
A crystal form with scale.
|
|
199
|
+
|
|
200
|
+
```python
|
|
201
|
+
@dataclass
|
|
202
|
+
class CrystalForm:
|
|
203
|
+
miller: MillerIndex
|
|
204
|
+
scale: float = 1.0
|
|
205
|
+
name: Optional[str] = None
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Constants
|
|
209
|
+
|
|
210
|
+
```python
|
|
211
|
+
from cdl_parser import (
|
|
212
|
+
CRYSTAL_SYSTEMS, # Set of system names
|
|
213
|
+
POINT_GROUPS, # Dict[system, Set[groups]]
|
|
214
|
+
DEFAULT_POINT_GROUPS, # Dict[system, default_group]
|
|
215
|
+
NAMED_FORMS, # Dict[name, (h, k, l)]
|
|
216
|
+
TWIN_LAWS, # Set of twin law names
|
|
217
|
+
)
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Exceptions
|
|
221
|
+
|
|
222
|
+
```python
|
|
223
|
+
from cdl_parser import ParseError, ValidationError
|
|
224
|
+
|
|
225
|
+
try:
|
|
226
|
+
desc = parse_cdl("invalid{{{")
|
|
227
|
+
except ParseError as e:
|
|
228
|
+
print(f"Syntax error at position {e.position}: {e.message}")
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## CLI Usage
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
# Parse and display
|
|
235
|
+
cdl parse "cubic[m3m]:{111}@1.0 + {100}@1.3"
|
|
236
|
+
|
|
237
|
+
# Validate
|
|
238
|
+
cdl validate "cubic[m3m]:{111}"
|
|
239
|
+
|
|
240
|
+
# Output as JSON
|
|
241
|
+
cdl parse "cubic[m3m]:{111}" --json
|
|
242
|
+
|
|
243
|
+
# List available options
|
|
244
|
+
cdl --list-systems
|
|
245
|
+
cdl --list-point-groups
|
|
246
|
+
cdl --list-forms
|
|
247
|
+
cdl --list-twins
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## Integration with Other Packages
|
|
251
|
+
|
|
252
|
+
cdl-parser is designed to work with the Gemmology Project ecosystem:
|
|
253
|
+
|
|
254
|
+
```python
|
|
255
|
+
from cdl_parser import parse_cdl
|
|
256
|
+
from crystal_geometry import cdl_to_geometry
|
|
257
|
+
from crystal_renderer import generate_cdl_svg, generate_geometry_svg
|
|
258
|
+
|
|
259
|
+
# Option 1: Direct CDL string to SVG
|
|
260
|
+
cdl = "cubic[m3m]:{111}@1.0 + {100}@1.3"
|
|
261
|
+
generate_cdl_svg(cdl, "crystal.svg")
|
|
262
|
+
|
|
263
|
+
# Option 2: Parse, then generate geometry for custom processing
|
|
264
|
+
desc = parse_cdl(cdl)
|
|
265
|
+
geometry = cdl_to_geometry(desc)
|
|
266
|
+
|
|
267
|
+
# Render geometry directly
|
|
268
|
+
generate_geometry_svg(geometry.vertices, geometry.faces, "geometry.svg")
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## Development
|
|
272
|
+
|
|
273
|
+
```bash
|
|
274
|
+
# Clone the repository
|
|
275
|
+
git clone https://github.com/gemmology-dev/cdl-parser.git
|
|
276
|
+
cd cdl-parser
|
|
277
|
+
|
|
278
|
+
# Install with dev dependencies
|
|
279
|
+
pip install -e ".[dev]"
|
|
280
|
+
|
|
281
|
+
# Run tests
|
|
282
|
+
pytest
|
|
283
|
+
|
|
284
|
+
# Type checking
|
|
285
|
+
mypy src/cdl_parser
|
|
286
|
+
|
|
287
|
+
# Linting
|
|
288
|
+
ruff check src/cdl_parser
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
## Documentation
|
|
292
|
+
|
|
293
|
+
Full documentation is available at [cdl-parser.gemmology.dev](https://cdl-parser.gemmology.dev).
|
|
294
|
+
|
|
295
|
+
## License
|
|
296
|
+
|
|
297
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
298
|
+
|
|
299
|
+
## Related Projects
|
|
300
|
+
|
|
301
|
+
- [crystal-geometry](https://github.com/gemmology-dev/crystal-geometry) - 3D geometry from CDL
|
|
302
|
+
- [mineral-database](https://github.com/gemmology-dev/mineral-database) - Mineral presets
|
|
303
|
+
- [crystal-renderer](https://github.com/gemmology-dev/crystal-renderer) - SVG/3D rendering
|
|
304
|
+
- [gemmology-plugin](https://github.com/gemmology-dev/gemmology-plugin) - Claude Code plugin
|