reframodel 2025.11.1__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.
- reframodel-2025.11.1/LICENSE +21 -0
- reframodel-2025.11.1/MANIFEST.in +7 -0
- reframodel-2025.11.1/PKG-INFO +90 -0
- reframodel-2025.11.1/README.md +58 -0
- reframodel-2025.11.1/RefraModel/__init__.py +4 -0
- reframodel-2025.11.1/RefraModel/__main__.py +7 -0
- reframodel-2025.11.1/RefraModel/editors/body_editor.py +20 -0
- reframodel-2025.11.1/RefraModel/editors/node_editor.py +20 -0
- reframodel-2025.11.1/RefraModel/editors/property_editor.py +24 -0
- reframodel-2025.11.1/RefraModel/geometry/bodies.py +83 -0
- reframodel-2025.11.1/RefraModel/geometry/lines.py +94 -0
- reframodel-2025.11.1/RefraModel/geometry/points.py +96 -0
- reframodel-2025.11.1/RefraModel/inversion/forward_model.py +208 -0
- reframodel-2025.11.1/RefraModel/inversion/inversion.py +846 -0
- reframodel-2025.11.1/RefraModel/inversion/mesh_builder.py +13 -0
- reframodel-2025.11.1/RefraModel/main.py +343 -0
- reframodel-2025.11.1/RefraModel/model_builder.py +2910 -0
- reframodel-2025.11.1/RefraModel/ui/dialogs.py +870 -0
- reframodel-2025.11.1/RefraModel/ui/menu.py +1045 -0
- reframodel-2025.11.1/RefraModel/ui/plot_manager.py +17 -0
- reframodel-2025.11.1/RefraModel/utils/file_io.py +23 -0
- reframodel-2025.11.1/RefraModel/utils/geometry_utils.py +9 -0
- reframodel-2025.11.1/pyproject.toml +53 -0
- reframodel-2025.11.1/reframodel.egg-info/PKG-INFO +90 -0
- reframodel-2025.11.1/reframodel.egg-info/SOURCES.txt +29 -0
- reframodel-2025.11.1/reframodel.egg-info/dependency_links.txt +1 -0
- reframodel-2025.11.1/reframodel.egg-info/entry_points.txt +2 -0
- reframodel-2025.11.1/reframodel.egg-info/requires.txt +9 -0
- reframodel-2025.11.1/reframodel.egg-info/top_level.txt +1 -0
- reframodel-2025.11.1/setup.cfg +4 -0
- reframodel-2025.11.1/setup.py +10 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 [Your Name]
|
|
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,90 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: reframodel
|
|
3
|
+
Version: 2025.11.1
|
|
4
|
+
Summary: Seismic refraction modeling and inversion tool
|
|
5
|
+
Author-email: Hermann Zeyen <hermann.zeyen@universite-paris-saclay.fr>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/HZeyen/reframodel
|
|
8
|
+
Project-URL: Documentation, https://github.com/HZeyen/reframodel/wiki
|
|
9
|
+
Project-URL: Repository, https://github.com/HZeyen/reframodel
|
|
10
|
+
Project-URL: Issues, https://github.com/HZeyen/reframodel/issues
|
|
11
|
+
Keywords: seismic,refraction,geophysics,inversion,modeling
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Requires-Python: >=3.8
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: numpy>=1.20.0
|
|
24
|
+
Requires-Dist: matplotlib>=3.3.0
|
|
25
|
+
Requires-Dist: PyQt5>=5.15.0
|
|
26
|
+
Requires-Dist: pygimli>=1.3.0
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
29
|
+
Requires-Dist: black>=22.0.0; extra == "dev"
|
|
30
|
+
Requires-Dist: flake8>=4.0.0; extra == "dev"
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
|
|
33
|
+
# RefraModel
|
|
34
|
+
|
|
35
|
+
Seismic refraction modeling and inversion tool using PyGIMLi.
|
|
36
|
+
|
|
37
|
+
## Features
|
|
38
|
+
|
|
39
|
+
- Interactive geological model builder
|
|
40
|
+
- Visual model editing and manipulation
|
|
41
|
+
- Forward modeling of seismic refraction data
|
|
42
|
+
- Inversion with flexible regularization
|
|
43
|
+
- Body-specific velocity constraints
|
|
44
|
+
- Anisotropic smoothing support
|
|
45
|
+
|
|
46
|
+
## Installation
|
|
47
|
+
|
|
48
|
+
### From source
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
git clone https://github.com/yourusername/reframodel.git
|
|
52
|
+
cd reframodel
|
|
53
|
+
pip install -e .
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### From PyPI (when published)
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pip install reframodel
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Usage
|
|
63
|
+
|
|
64
|
+
After installation, run the application:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
reframodel
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Or as a Python module:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
python -m RefraModel
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Requirements
|
|
77
|
+
|
|
78
|
+
- Python >= 3.8
|
|
79
|
+
- numpy >= 1.20.0
|
|
80
|
+
- matplotlib >= 3.3.0
|
|
81
|
+
- PyQt5 >= 5.15.0
|
|
82
|
+
- pygimli >= 1.3.0
|
|
83
|
+
|
|
84
|
+
## License
|
|
85
|
+
|
|
86
|
+
MIT License
|
|
87
|
+
|
|
88
|
+
## Author
|
|
89
|
+
|
|
90
|
+
Hermann Zeyen, University Paris-Saclay <hermann.zeyen@universite-paris-saclay.fr>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# RefraModel
|
|
2
|
+
|
|
3
|
+
Seismic refraction modeling and inversion tool using PyGIMLi.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Interactive geological model builder
|
|
8
|
+
- Visual model editing and manipulation
|
|
9
|
+
- Forward modeling of seismic refraction data
|
|
10
|
+
- Inversion with flexible regularization
|
|
11
|
+
- Body-specific velocity constraints
|
|
12
|
+
- Anisotropic smoothing support
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
### From source
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
git clone https://github.com/yourusername/reframodel.git
|
|
20
|
+
cd reframodel
|
|
21
|
+
pip install -e .
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### From PyPI (when published)
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pip install reframodel
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Usage
|
|
31
|
+
|
|
32
|
+
After installation, run the application:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
reframodel
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Or as a Python module:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
python -m RefraModel
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Requirements
|
|
45
|
+
|
|
46
|
+
- Python >= 3.8
|
|
47
|
+
- numpy >= 1.20.0
|
|
48
|
+
- matplotlib >= 3.3.0
|
|
49
|
+
- PyQt5 >= 5.15.0
|
|
50
|
+
- pygimli >= 1.3.0
|
|
51
|
+
|
|
52
|
+
## License
|
|
53
|
+
|
|
54
|
+
MIT License
|
|
55
|
+
|
|
56
|
+
## Author
|
|
57
|
+
|
|
58
|
+
Hermann Zeyen, University Paris-Saclay <hermann.zeyen@universite-paris-saclay.fr>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Body editing operations for geological model
|
|
3
|
+
"""
|
|
4
|
+
from ..geometry.bodies import BodyManager
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class BodyEditor:
|
|
8
|
+
"""Handles editing of geological bodies"""
|
|
9
|
+
|
|
10
|
+
def __init__(self):
|
|
11
|
+
self.body_manager = BodyManager()
|
|
12
|
+
|
|
13
|
+
def edit_body(self, body_index, new_properties):
|
|
14
|
+
"""Edit properties of a specific body"""
|
|
15
|
+
if 0 <= body_index < len(self.body_manager.bodies):
|
|
16
|
+
body = self.body_manager.bodies[body_index]
|
|
17
|
+
body.update(new_properties)
|
|
18
|
+
print(f"Updated body {body_index} with properties: {new_properties}")
|
|
19
|
+
else:
|
|
20
|
+
print("Body index out of range.")
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Node editing operations for geological model
|
|
3
|
+
"""
|
|
4
|
+
from ..geometry.points import PointManager
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class NodeEditor:
|
|
8
|
+
"""Handles editing of nodes (points) in the model"""
|
|
9
|
+
|
|
10
|
+
def __init__(self):
|
|
11
|
+
self.point_manager = PointManager(0, 100, 0, 100, 1, 1)
|
|
12
|
+
|
|
13
|
+
def edit_node(self, node_index, new_coordinates):
|
|
14
|
+
"""Edit coordinates of a specific node"""
|
|
15
|
+
if 0 <= node_index < len(self.point_manager.points):
|
|
16
|
+
point = self.point_manager.points[node_index]
|
|
17
|
+
point["x"], point["y"] = new_coordinates
|
|
18
|
+
print(f"Updated node {node_index} to coordinates: {new_coordinates}")
|
|
19
|
+
else:
|
|
20
|
+
print("Node index out of range.")
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Property editing operations for geological model
|
|
3
|
+
"""
|
|
4
|
+
from ..geometry.bodies import BodyManager
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class PropertyEditor:
|
|
8
|
+
"""Handles editing of properties for geological bodies"""
|
|
9
|
+
|
|
10
|
+
def __init__(self):
|
|
11
|
+
self.body_manager = BodyManager()
|
|
12
|
+
|
|
13
|
+
def edit_property(self, body_index, property_name, new_value):
|
|
14
|
+
"""Edit a specific property of a body"""
|
|
15
|
+
if 0 <= body_index < len(self.body_manager.bodies):
|
|
16
|
+
body = self.body_manager.bodies[body_index]
|
|
17
|
+
if property_name in body["prop_names"]:
|
|
18
|
+
prop_index = body["prop_names"].index(property_name)
|
|
19
|
+
body["props"][prop_index] = new_value
|
|
20
|
+
print(f"Updated property '{property_name}' of body {body_index} to {new_value}")
|
|
21
|
+
else:
|
|
22
|
+
print(f"Property '{property_name}' not found in body {body_index}.")
|
|
23
|
+
else:
|
|
24
|
+
print("Body index out of range.")
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Body management for geological model
|
|
3
|
+
"""
|
|
4
|
+
import numpy as np
|
|
5
|
+
from matplotlib.path import Path
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class BodyManager:
|
|
9
|
+
"""Manages geological bodies in the model"""
|
|
10
|
+
|
|
11
|
+
def __init__(self):
|
|
12
|
+
self.bodies = []
|
|
13
|
+
self.nbody = -1
|
|
14
|
+
|
|
15
|
+
def append_body(self, lines, directions, para_names, para_values=None, name=None):
|
|
16
|
+
"""Add a new body to the list of bodies"""
|
|
17
|
+
self.nbody += 1
|
|
18
|
+
print(f"Body {self.nbody} created")
|
|
19
|
+
|
|
20
|
+
body = {
|
|
21
|
+
"lines": lines,
|
|
22
|
+
"sense": directions,
|
|
23
|
+
"name": name or f"Body_{self.nbody}",
|
|
24
|
+
"prop_names": para_names,
|
|
25
|
+
"props": para_values or [0.] * len(para_names),
|
|
26
|
+
"cuts": [],
|
|
27
|
+
"constraint": "free",
|
|
28
|
+
"limits": [0., 10000.],
|
|
29
|
+
"corr_lengths": [0., 0.]
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
self.bodies.append(body)
|
|
33
|
+
|
|
34
|
+
def get_polygon(self, nb, points, lines):
|
|
35
|
+
"""Get polygon coordinates for a body"""
|
|
36
|
+
b = self.bodies[nb]
|
|
37
|
+
line = lines[b["lines"][0]]
|
|
38
|
+
|
|
39
|
+
if b["sense"][0] > 0:
|
|
40
|
+
x = [points[line["point1"]]["x"]]
|
|
41
|
+
y = [points[line["point1"]]["y"]]
|
|
42
|
+
else:
|
|
43
|
+
x = [points[line["point2"]]["x"]]
|
|
44
|
+
y = [points[line["point2"]]["y"]]
|
|
45
|
+
|
|
46
|
+
for j, lin in enumerate(b["lines"]):
|
|
47
|
+
line = lines[lin]
|
|
48
|
+
if b["sense"][j] > 0:
|
|
49
|
+
x.append(points[line["point2"]]["x"])
|
|
50
|
+
y.append(points[line["point2"]]["y"])
|
|
51
|
+
else:
|
|
52
|
+
x.append(points[line["point1"]]["x"])
|
|
53
|
+
y.append(points[line["point1"]]["y"])
|
|
54
|
+
|
|
55
|
+
return x, y
|
|
56
|
+
|
|
57
|
+
def inside_body(self, xp, yp, points, lines):
|
|
58
|
+
"""Check whether a point is inside a body"""
|
|
59
|
+
for i, b in enumerate(self.bodies):
|
|
60
|
+
x, y = self.get_polygon(i, points, lines)
|
|
61
|
+
path = Path(list(zip(x, y)))
|
|
62
|
+
if xp is None:
|
|
63
|
+
return -1, path
|
|
64
|
+
if path.contains_point((xp, yp)):
|
|
65
|
+
return i, path
|
|
66
|
+
return -1, None
|
|
67
|
+
|
|
68
|
+
def get_vel_limits(self):
|
|
69
|
+
"""Calculate minimum and maximum velocities"""
|
|
70
|
+
if not self.bodies:
|
|
71
|
+
return 1000., 5000.
|
|
72
|
+
|
|
73
|
+
vmin = self.bodies[0]["props"][0]
|
|
74
|
+
vmax = vmin
|
|
75
|
+
for b in self.bodies:
|
|
76
|
+
vmin = min(vmin, b["props"][0])
|
|
77
|
+
vmax = max(vmax, b["props"][0])
|
|
78
|
+
|
|
79
|
+
if vmin == vmax:
|
|
80
|
+
vmin -= 100.
|
|
81
|
+
vmax += 100.
|
|
82
|
+
|
|
83
|
+
return vmin, vmax
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Line management for geological model
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class LineManager:
|
|
7
|
+
"""Manages lines connecting points"""
|
|
8
|
+
|
|
9
|
+
def __init__(self):
|
|
10
|
+
self.lines = []
|
|
11
|
+
self.nline = -1
|
|
12
|
+
|
|
13
|
+
def append_line(self, pt1, pt2, body=None):
|
|
14
|
+
"""Append a line to the list"""
|
|
15
|
+
self.nline += 1
|
|
16
|
+
|
|
17
|
+
line = {
|
|
18
|
+
"point1": pt1,
|
|
19
|
+
"point2": pt2,
|
|
20
|
+
"bodies": body or [],
|
|
21
|
+
"topo": False,
|
|
22
|
+
"bottom": False,
|
|
23
|
+
"left": False,
|
|
24
|
+
"right": False
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
self.lines.append(line)
|
|
28
|
+
|
|
29
|
+
def find_nearest_line(self, xp, yp, points):
|
|
30
|
+
"""Find line nearest to a point in screen coordinates"""
|
|
31
|
+
lmin = -1
|
|
32
|
+
dmin = 1000000000.
|
|
33
|
+
|
|
34
|
+
for i, lin in enumerate(self.lines):
|
|
35
|
+
pt = points[lin["point1"]]
|
|
36
|
+
x1 = pt["xscreen"]
|
|
37
|
+
y1 = pt["yscreen"]
|
|
38
|
+
pt = points[lin["point2"]]
|
|
39
|
+
x2 = pt["xscreen"]
|
|
40
|
+
y2 = pt["yscreen"]
|
|
41
|
+
|
|
42
|
+
xcross, ycross, x_flag = self.crossing(x1, y1, x2, y2, xp, yp)
|
|
43
|
+
if x_flag:
|
|
44
|
+
d = (xp - xcross) ** 2 + (yp - ycross) ** 2
|
|
45
|
+
if d < dmin:
|
|
46
|
+
dmin = d
|
|
47
|
+
lmin = i
|
|
48
|
+
|
|
49
|
+
return lmin
|
|
50
|
+
|
|
51
|
+
@staticmethod
|
|
52
|
+
def crossing(x1, y1, x2, y2, xp, yp):
|
|
53
|
+
"""Calculate crossing point of perpendicular line"""
|
|
54
|
+
dx = x2 - x1
|
|
55
|
+
dy = y2 - y1
|
|
56
|
+
|
|
57
|
+
if dx == dy == 0:
|
|
58
|
+
return None, None, False
|
|
59
|
+
|
|
60
|
+
vx = xp - x1
|
|
61
|
+
vy = yp - y1
|
|
62
|
+
|
|
63
|
+
t = (vx * dx + vy * dy) / (dx ** 2 + dy ** 2)
|
|
64
|
+
xq = x1 + t * dx
|
|
65
|
+
yq = y1 + t * dy
|
|
66
|
+
|
|
67
|
+
flag = True
|
|
68
|
+
if dx > dy:
|
|
69
|
+
if xq < min(x1, x2) or xq > max(x1, x2):
|
|
70
|
+
flag = False
|
|
71
|
+
else:
|
|
72
|
+
if yq < min(y1, y2) or yq > max(y1, y2):
|
|
73
|
+
flag = False
|
|
74
|
+
|
|
75
|
+
return xq, yq, flag
|
|
76
|
+
|
|
77
|
+
def check_line_exist(self, x, y, points):
|
|
78
|
+
"""Check if a line already exists"""
|
|
79
|
+
import numpy as np
|
|
80
|
+
|
|
81
|
+
for i, lin in enumerate(self.lines):
|
|
82
|
+
x1 = points[lin["point1"]]["x"]
|
|
83
|
+
x2 = points[lin["point2"]]["x"]
|
|
84
|
+
y1 = points[lin["point1"]]["y"]
|
|
85
|
+
y2 = points[lin["point2"]]["y"]
|
|
86
|
+
|
|
87
|
+
if (np.isclose(x[0], x1) and np.isclose(y[0], y1) and
|
|
88
|
+
np.isclose(x[1], x2) and np.isclose(y[1], y2)):
|
|
89
|
+
return i, 1
|
|
90
|
+
elif (np.isclose(x[1], x1) and np.isclose(y[1], y1) and
|
|
91
|
+
np.isclose(x[0], x2) and np.isclose(y[0], y2)):
|
|
92
|
+
return i, -1
|
|
93
|
+
|
|
94
|
+
return -1, None
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Point management for geological model
|
|
3
|
+
"""
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class PointManager:
|
|
8
|
+
"""Manages points in the model"""
|
|
9
|
+
|
|
10
|
+
def __init__(self, xmin, xmax, ymin, ymax, eps_x, eps_y):
|
|
11
|
+
self.points = []
|
|
12
|
+
self.npoint = -1
|
|
13
|
+
self.xmin = xmin
|
|
14
|
+
self.xmax = xmax
|
|
15
|
+
self.ymin = ymin
|
|
16
|
+
self.ymax = ymax
|
|
17
|
+
self.eps_x = eps_x
|
|
18
|
+
self.eps_y = eps_y
|
|
19
|
+
|
|
20
|
+
def append_point(self, xp, yp, xs, ys, body=None, line=None):
|
|
21
|
+
"""Append a point to the list of points"""
|
|
22
|
+
self.npoint += 1
|
|
23
|
+
# Round coordinates to 2 decimals at creation
|
|
24
|
+
try:
|
|
25
|
+
xp = float(round(xp, 2))
|
|
26
|
+
yp = float(round(yp, 2))
|
|
27
|
+
except Exception:
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
point = {
|
|
31
|
+
"x": xp,
|
|
32
|
+
"y": yp,
|
|
33
|
+
"xscreen": xs,
|
|
34
|
+
"yscreen": ys,
|
|
35
|
+
"lines": line or [],
|
|
36
|
+
"bodies": body or [],
|
|
37
|
+
"topo": False,
|
|
38
|
+
"bottom": False,
|
|
39
|
+
"left": False,
|
|
40
|
+
"right": False
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
# Check special positions
|
|
44
|
+
if np.isclose(yp, self.ymin):
|
|
45
|
+
point["bottom"] = True
|
|
46
|
+
if xp <= self.xmin + 0.1:
|
|
47
|
+
point["left"] = True
|
|
48
|
+
if xp >= self.xmax - 0.01:
|
|
49
|
+
point["right"] = True
|
|
50
|
+
|
|
51
|
+
self.points.append(point)
|
|
52
|
+
|
|
53
|
+
def find_nearest_point(self, xp, yp):
|
|
54
|
+
"""Find the nearest point to given coordinates"""
|
|
55
|
+
if not self.points:
|
|
56
|
+
return -1
|
|
57
|
+
|
|
58
|
+
pmin = -1
|
|
59
|
+
dmin = 1000000000.
|
|
60
|
+
|
|
61
|
+
for i, p in enumerate(self.points):
|
|
62
|
+
d = (p["x"] - xp) ** 2 + (p["y"] - yp) ** 2
|
|
63
|
+
if d < dmin:
|
|
64
|
+
pmin = i
|
|
65
|
+
dmin = d
|
|
66
|
+
|
|
67
|
+
return pmin
|
|
68
|
+
|
|
69
|
+
def find_nearest_screen_point(self, xp, yp):
|
|
70
|
+
"""Find nearest point in screen coordinates"""
|
|
71
|
+
if not self.points:
|
|
72
|
+
return -1
|
|
73
|
+
|
|
74
|
+
pmin = -1
|
|
75
|
+
dmin = 1000000000.
|
|
76
|
+
|
|
77
|
+
for i, p in enumerate(self.points):
|
|
78
|
+
d = (p["xscreen"] - xp) ** 2 + (p["yscreen"] - yp) ** 2
|
|
79
|
+
if d < dmin:
|
|
80
|
+
pmin = i
|
|
81
|
+
dmin = d
|
|
82
|
+
|
|
83
|
+
return pmin
|
|
84
|
+
|
|
85
|
+
def check_close(self, x, y):
|
|
86
|
+
"""Check if point is near existing point"""
|
|
87
|
+
if not self.points:
|
|
88
|
+
return x, y, -1
|
|
89
|
+
|
|
90
|
+
for i, p in enumerate(self.points):
|
|
91
|
+
xb = p["x"]
|
|
92
|
+
yb = p["y"]
|
|
93
|
+
if abs(x - xb) < self.eps_x and abs(y - yb) < self.eps_y:
|
|
94
|
+
return xb, yb, i
|
|
95
|
+
|
|
96
|
+
return x, y, -1
|