nooverlap 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.
- nooverlap-0.1.0/.github/workflows/CI.yml +169 -0
- nooverlap-0.1.0/.gitignore +72 -0
- nooverlap-0.1.0/Cargo.lock +171 -0
- nooverlap-0.1.0/Cargo.toml +12 -0
- nooverlap-0.1.0/PKG-INFO +7 -0
- nooverlap-0.1.0/pyproject.toml +15 -0
- nooverlap-0.1.0/python/animation.py +47 -0
- nooverlap-0.1.0/python/example.py +180 -0
- nooverlap-0.1.0/python/test.py +48 -0
- nooverlap-0.1.0/python/test_timing.py +45 -0
- nooverlap-0.1.0/src/lib.rs +255 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# This file is autogenerated by maturin v1.7.0
|
|
2
|
+
# To update, run
|
|
3
|
+
#
|
|
4
|
+
# maturin generate-ci github
|
|
5
|
+
#
|
|
6
|
+
name: CI
|
|
7
|
+
|
|
8
|
+
on:
|
|
9
|
+
push:
|
|
10
|
+
branches:
|
|
11
|
+
- main
|
|
12
|
+
- master
|
|
13
|
+
tags:
|
|
14
|
+
- '*'
|
|
15
|
+
pull_request:
|
|
16
|
+
workflow_dispatch:
|
|
17
|
+
|
|
18
|
+
permissions:
|
|
19
|
+
contents: read
|
|
20
|
+
|
|
21
|
+
jobs:
|
|
22
|
+
linux:
|
|
23
|
+
runs-on: ${{ matrix.platform.runner }}
|
|
24
|
+
strategy:
|
|
25
|
+
matrix:
|
|
26
|
+
platform:
|
|
27
|
+
- runner: ubuntu-latest
|
|
28
|
+
target: x86_64
|
|
29
|
+
- runner: ubuntu-latest
|
|
30
|
+
target: x86
|
|
31
|
+
- runner: ubuntu-latest
|
|
32
|
+
target: aarch64
|
|
33
|
+
- runner: ubuntu-latest
|
|
34
|
+
target: armv7
|
|
35
|
+
- runner: ubuntu-latest
|
|
36
|
+
target: s390x
|
|
37
|
+
- runner: ubuntu-latest
|
|
38
|
+
target: ppc64le
|
|
39
|
+
steps:
|
|
40
|
+
- uses: actions/checkout@v4
|
|
41
|
+
- uses: actions/setup-python@v5
|
|
42
|
+
with:
|
|
43
|
+
python-version: 3.x
|
|
44
|
+
- name: Build wheels
|
|
45
|
+
uses: PyO3/maturin-action@v1
|
|
46
|
+
with:
|
|
47
|
+
target: ${{ matrix.platform.target }}
|
|
48
|
+
args: --release --out dist --find-interpreter
|
|
49
|
+
sccache: 'true'
|
|
50
|
+
manylinux: auto
|
|
51
|
+
- name: Upload wheels
|
|
52
|
+
uses: actions/upload-artifact@v4
|
|
53
|
+
with:
|
|
54
|
+
name: wheels-linux-${{ matrix.platform.target }}
|
|
55
|
+
path: dist
|
|
56
|
+
|
|
57
|
+
musllinux:
|
|
58
|
+
runs-on: ${{ matrix.platform.runner }}
|
|
59
|
+
strategy:
|
|
60
|
+
matrix:
|
|
61
|
+
platform:
|
|
62
|
+
- runner: ubuntu-latest
|
|
63
|
+
target: x86_64
|
|
64
|
+
- runner: ubuntu-latest
|
|
65
|
+
target: x86
|
|
66
|
+
- runner: ubuntu-latest
|
|
67
|
+
target: aarch64
|
|
68
|
+
- runner: ubuntu-latest
|
|
69
|
+
target: armv7
|
|
70
|
+
steps:
|
|
71
|
+
- uses: actions/checkout@v4
|
|
72
|
+
- uses: actions/setup-python@v5
|
|
73
|
+
with:
|
|
74
|
+
python-version: 3.x
|
|
75
|
+
- name: Build wheels
|
|
76
|
+
uses: PyO3/maturin-action@v1
|
|
77
|
+
with:
|
|
78
|
+
target: ${{ matrix.platform.target }}
|
|
79
|
+
args: --release --out dist --find-interpreter
|
|
80
|
+
sccache: 'true'
|
|
81
|
+
manylinux: musllinux_1_2
|
|
82
|
+
- name: Upload wheels
|
|
83
|
+
uses: actions/upload-artifact@v4
|
|
84
|
+
with:
|
|
85
|
+
name: wheels-musllinux-${{ matrix.platform.target }}
|
|
86
|
+
path: dist
|
|
87
|
+
|
|
88
|
+
windows:
|
|
89
|
+
runs-on: ${{ matrix.platform.runner }}
|
|
90
|
+
strategy:
|
|
91
|
+
matrix:
|
|
92
|
+
platform:
|
|
93
|
+
- runner: windows-latest
|
|
94
|
+
target: x64
|
|
95
|
+
- runner: windows-latest
|
|
96
|
+
target: x86
|
|
97
|
+
steps:
|
|
98
|
+
- uses: actions/checkout@v4
|
|
99
|
+
- uses: actions/setup-python@v5
|
|
100
|
+
with:
|
|
101
|
+
python-version: 3.x
|
|
102
|
+
architecture: ${{ matrix.platform.target }}
|
|
103
|
+
- name: Build wheels
|
|
104
|
+
uses: PyO3/maturin-action@v1
|
|
105
|
+
with:
|
|
106
|
+
target: ${{ matrix.platform.target }}
|
|
107
|
+
args: --release --out dist --find-interpreter
|
|
108
|
+
sccache: 'true'
|
|
109
|
+
- name: Upload wheels
|
|
110
|
+
uses: actions/upload-artifact@v4
|
|
111
|
+
with:
|
|
112
|
+
name: wheels-windows-${{ matrix.platform.target }}
|
|
113
|
+
path: dist
|
|
114
|
+
|
|
115
|
+
macos:
|
|
116
|
+
runs-on: ${{ matrix.platform.runner }}
|
|
117
|
+
strategy:
|
|
118
|
+
matrix:
|
|
119
|
+
platform:
|
|
120
|
+
- runner: macos-12
|
|
121
|
+
target: x86_64
|
|
122
|
+
- runner: macos-14
|
|
123
|
+
target: aarch64
|
|
124
|
+
steps:
|
|
125
|
+
- uses: actions/checkout@v4
|
|
126
|
+
- uses: actions/setup-python@v5
|
|
127
|
+
with:
|
|
128
|
+
python-version: 3.x
|
|
129
|
+
- name: Build wheels
|
|
130
|
+
uses: PyO3/maturin-action@v1
|
|
131
|
+
with:
|
|
132
|
+
target: ${{ matrix.platform.target }}
|
|
133
|
+
args: --release --out dist --find-interpreter
|
|
134
|
+
sccache: 'true'
|
|
135
|
+
- name: Upload wheels
|
|
136
|
+
uses: actions/upload-artifact@v4
|
|
137
|
+
with:
|
|
138
|
+
name: wheels-macos-${{ matrix.platform.target }}
|
|
139
|
+
path: dist
|
|
140
|
+
|
|
141
|
+
sdist:
|
|
142
|
+
runs-on: ubuntu-latest
|
|
143
|
+
steps:
|
|
144
|
+
- uses: actions/checkout@v4
|
|
145
|
+
- name: Build sdist
|
|
146
|
+
uses: PyO3/maturin-action@v1
|
|
147
|
+
with:
|
|
148
|
+
command: sdist
|
|
149
|
+
args: --out dist
|
|
150
|
+
- name: Upload sdist
|
|
151
|
+
uses: actions/upload-artifact@v4
|
|
152
|
+
with:
|
|
153
|
+
name: wheels-sdist
|
|
154
|
+
path: dist
|
|
155
|
+
|
|
156
|
+
release:
|
|
157
|
+
name: Release
|
|
158
|
+
runs-on: ubuntu-latest
|
|
159
|
+
permissions:
|
|
160
|
+
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
|
|
161
|
+
if: "startsWith(github.ref, 'refs/tags/')"
|
|
162
|
+
needs: [linux, musllinux, windows, macos, sdist]
|
|
163
|
+
steps:
|
|
164
|
+
- uses: actions/download-artifact@v4
|
|
165
|
+
- name: Publish to PyPI
|
|
166
|
+
uses: PyO3/maturin-action@v1
|
|
167
|
+
with:
|
|
168
|
+
command: upload
|
|
169
|
+
args: --non-interactive --skip-existing wheels-*/*
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/target
|
|
2
|
+
|
|
3
|
+
# Byte-compiled / optimized / DLL files
|
|
4
|
+
__pycache__/
|
|
5
|
+
.pytest_cache/
|
|
6
|
+
*.py[cod]
|
|
7
|
+
|
|
8
|
+
# C extensions
|
|
9
|
+
*.so
|
|
10
|
+
|
|
11
|
+
# Distribution / packaging
|
|
12
|
+
.Python
|
|
13
|
+
.venv/
|
|
14
|
+
env/
|
|
15
|
+
bin/
|
|
16
|
+
build/
|
|
17
|
+
develop-eggs/
|
|
18
|
+
dist/
|
|
19
|
+
eggs/
|
|
20
|
+
lib/
|
|
21
|
+
lib64/
|
|
22
|
+
parts/
|
|
23
|
+
sdist/
|
|
24
|
+
var/
|
|
25
|
+
include/
|
|
26
|
+
man/
|
|
27
|
+
venv/
|
|
28
|
+
*.egg-info/
|
|
29
|
+
.installed.cfg
|
|
30
|
+
*.egg
|
|
31
|
+
|
|
32
|
+
# Installer logs
|
|
33
|
+
pip-log.txt
|
|
34
|
+
pip-delete-this-directory.txt
|
|
35
|
+
pip-selfcheck.json
|
|
36
|
+
|
|
37
|
+
# Unit test / coverage reports
|
|
38
|
+
htmlcov/
|
|
39
|
+
.tox/
|
|
40
|
+
.coverage
|
|
41
|
+
.cache
|
|
42
|
+
nosetests.xml
|
|
43
|
+
coverage.xml
|
|
44
|
+
|
|
45
|
+
# Translations
|
|
46
|
+
*.mo
|
|
47
|
+
|
|
48
|
+
# Mr Developer
|
|
49
|
+
.mr.developer.cfg
|
|
50
|
+
.project
|
|
51
|
+
.pydevproject
|
|
52
|
+
|
|
53
|
+
# Rope
|
|
54
|
+
.ropeproject
|
|
55
|
+
|
|
56
|
+
# Django stuff:
|
|
57
|
+
*.log
|
|
58
|
+
*.pot
|
|
59
|
+
|
|
60
|
+
.DS_Store
|
|
61
|
+
|
|
62
|
+
# Sphinx documentation
|
|
63
|
+
docs/_build/
|
|
64
|
+
|
|
65
|
+
# PyCharm
|
|
66
|
+
.idea/
|
|
67
|
+
|
|
68
|
+
# VSCode
|
|
69
|
+
.vscode/
|
|
70
|
+
|
|
71
|
+
# Pyenv
|
|
72
|
+
.python-version
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# This file is automatically @generated by Cargo.
|
|
2
|
+
# It is not intended for manual editing.
|
|
3
|
+
version = 3
|
|
4
|
+
|
|
5
|
+
[[package]]
|
|
6
|
+
name = "autocfg"
|
|
7
|
+
version = "1.3.0"
|
|
8
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
9
|
+
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
|
10
|
+
|
|
11
|
+
[[package]]
|
|
12
|
+
name = "cfg-if"
|
|
13
|
+
version = "1.0.0"
|
|
14
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
15
|
+
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|
16
|
+
|
|
17
|
+
[[package]]
|
|
18
|
+
name = "heck"
|
|
19
|
+
version = "0.5.0"
|
|
20
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
21
|
+
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
|
22
|
+
|
|
23
|
+
[[package]]
|
|
24
|
+
name = "indoc"
|
|
25
|
+
version = "2.0.5"
|
|
26
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
27
|
+
checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5"
|
|
28
|
+
|
|
29
|
+
[[package]]
|
|
30
|
+
name = "libc"
|
|
31
|
+
version = "0.2.158"
|
|
32
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
33
|
+
checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
|
|
34
|
+
|
|
35
|
+
[[package]]
|
|
36
|
+
name = "memoffset"
|
|
37
|
+
version = "0.9.1"
|
|
38
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
39
|
+
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
|
|
40
|
+
dependencies = [
|
|
41
|
+
"autocfg",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[[package]]
|
|
45
|
+
name = "nooverlap"
|
|
46
|
+
version = "0.1.0"
|
|
47
|
+
dependencies = [
|
|
48
|
+
"pyo3",
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
[[package]]
|
|
52
|
+
name = "once_cell"
|
|
53
|
+
version = "1.19.0"
|
|
54
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
55
|
+
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
|
56
|
+
|
|
57
|
+
[[package]]
|
|
58
|
+
name = "portable-atomic"
|
|
59
|
+
version = "1.7.0"
|
|
60
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
61
|
+
checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265"
|
|
62
|
+
|
|
63
|
+
[[package]]
|
|
64
|
+
name = "proc-macro2"
|
|
65
|
+
version = "1.0.86"
|
|
66
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
67
|
+
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
|
68
|
+
dependencies = [
|
|
69
|
+
"unicode-ident",
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
[[package]]
|
|
73
|
+
name = "pyo3"
|
|
74
|
+
version = "0.22.2"
|
|
75
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
76
|
+
checksum = "831e8e819a138c36e212f3af3fd9eeffed6bf1510a805af35b0edee5ffa59433"
|
|
77
|
+
dependencies = [
|
|
78
|
+
"cfg-if",
|
|
79
|
+
"indoc",
|
|
80
|
+
"libc",
|
|
81
|
+
"memoffset",
|
|
82
|
+
"once_cell",
|
|
83
|
+
"portable-atomic",
|
|
84
|
+
"pyo3-build-config",
|
|
85
|
+
"pyo3-ffi",
|
|
86
|
+
"pyo3-macros",
|
|
87
|
+
"unindent",
|
|
88
|
+
]
|
|
89
|
+
|
|
90
|
+
[[package]]
|
|
91
|
+
name = "pyo3-build-config"
|
|
92
|
+
version = "0.22.2"
|
|
93
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
94
|
+
checksum = "1e8730e591b14492a8945cdff32f089250b05f5accecf74aeddf9e8272ce1fa8"
|
|
95
|
+
dependencies = [
|
|
96
|
+
"once_cell",
|
|
97
|
+
"target-lexicon",
|
|
98
|
+
]
|
|
99
|
+
|
|
100
|
+
[[package]]
|
|
101
|
+
name = "pyo3-ffi"
|
|
102
|
+
version = "0.22.2"
|
|
103
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
104
|
+
checksum = "5e97e919d2df92eb88ca80a037969f44e5e70356559654962cbb3316d00300c6"
|
|
105
|
+
dependencies = [
|
|
106
|
+
"libc",
|
|
107
|
+
"pyo3-build-config",
|
|
108
|
+
]
|
|
109
|
+
|
|
110
|
+
[[package]]
|
|
111
|
+
name = "pyo3-macros"
|
|
112
|
+
version = "0.22.2"
|
|
113
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
114
|
+
checksum = "eb57983022ad41f9e683a599f2fd13c3664d7063a3ac5714cae4b7bee7d3f206"
|
|
115
|
+
dependencies = [
|
|
116
|
+
"proc-macro2",
|
|
117
|
+
"pyo3-macros-backend",
|
|
118
|
+
"quote",
|
|
119
|
+
"syn",
|
|
120
|
+
]
|
|
121
|
+
|
|
122
|
+
[[package]]
|
|
123
|
+
name = "pyo3-macros-backend"
|
|
124
|
+
version = "0.22.2"
|
|
125
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
126
|
+
checksum = "ec480c0c51ddec81019531705acac51bcdbeae563557c982aa8263bb96880372"
|
|
127
|
+
dependencies = [
|
|
128
|
+
"heck",
|
|
129
|
+
"proc-macro2",
|
|
130
|
+
"pyo3-build-config",
|
|
131
|
+
"quote",
|
|
132
|
+
"syn",
|
|
133
|
+
]
|
|
134
|
+
|
|
135
|
+
[[package]]
|
|
136
|
+
name = "quote"
|
|
137
|
+
version = "1.0.37"
|
|
138
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
139
|
+
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
|
140
|
+
dependencies = [
|
|
141
|
+
"proc-macro2",
|
|
142
|
+
]
|
|
143
|
+
|
|
144
|
+
[[package]]
|
|
145
|
+
name = "syn"
|
|
146
|
+
version = "2.0.77"
|
|
147
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
148
|
+
checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed"
|
|
149
|
+
dependencies = [
|
|
150
|
+
"proc-macro2",
|
|
151
|
+
"quote",
|
|
152
|
+
"unicode-ident",
|
|
153
|
+
]
|
|
154
|
+
|
|
155
|
+
[[package]]
|
|
156
|
+
name = "target-lexicon"
|
|
157
|
+
version = "0.12.16"
|
|
158
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
159
|
+
checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
|
160
|
+
|
|
161
|
+
[[package]]
|
|
162
|
+
name = "unicode-ident"
|
|
163
|
+
version = "1.0.12"
|
|
164
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
165
|
+
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
|
166
|
+
|
|
167
|
+
[[package]]
|
|
168
|
+
name = "unindent"
|
|
169
|
+
version = "0.2.3"
|
|
170
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
171
|
+
checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce"
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "nooverlap"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
edition = "2021"
|
|
5
|
+
|
|
6
|
+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
7
|
+
[lib]
|
|
8
|
+
name = "nooverlap"
|
|
9
|
+
crate-type = ["cdylib"]
|
|
10
|
+
|
|
11
|
+
[dependencies]
|
|
12
|
+
pyo3 = "0.22.0"
|
nooverlap-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: nooverlap
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Classifier: Programming Language :: Rust
|
|
5
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
6
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
7
|
+
Requires-Python: >=3.8
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["maturin>=1.7,<2.0"]
|
|
3
|
+
build-backend = "maturin"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "nooverlap"
|
|
7
|
+
requires-python = ">=3.8"
|
|
8
|
+
classifiers = [
|
|
9
|
+
"Programming Language :: Rust",
|
|
10
|
+
"Programming Language :: Python :: Implementation :: CPython",
|
|
11
|
+
"Programming Language :: Python :: Implementation :: PyPy",
|
|
12
|
+
]
|
|
13
|
+
dynamic = ["version"]
|
|
14
|
+
[tool.maturin]
|
|
15
|
+
features = ["pyo3/extension-module"]
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import nooverlap
|
|
2
|
+
import numpy as np
|
|
3
|
+
import matplotlib.pyplot as plt
|
|
4
|
+
from matplotlib.animation import FuncAnimation
|
|
5
|
+
|
|
6
|
+
np.random.seed(0)
|
|
7
|
+
|
|
8
|
+
# make some random box data
|
|
9
|
+
n = 70
|
|
10
|
+
x = 5*np.random.rand(n)
|
|
11
|
+
y = 2*np.random.rand(n)
|
|
12
|
+
w = np.random.rand(n)
|
|
13
|
+
h = 0.3*np.random.rand(n)
|
|
14
|
+
|
|
15
|
+
# create a nooverlap object
|
|
16
|
+
no = nooverlap.Pusher()
|
|
17
|
+
|
|
18
|
+
# add the boxes
|
|
19
|
+
for i in range(n):
|
|
20
|
+
no.add_box(x[i], y[i], w[i]/2, w[i]/2, h[i]/2, h[i]/2)
|
|
21
|
+
|
|
22
|
+
# draw the boxes
|
|
23
|
+
fig, ax = plt.subplots()
|
|
24
|
+
|
|
25
|
+
def step(i):
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
no.push_elements(0.01, 0.1)
|
|
29
|
+
|
|
30
|
+
ax.clear()
|
|
31
|
+
ax.set_xlim(-1,6)
|
|
32
|
+
ax.set_ylim(-0.5,2.5)
|
|
33
|
+
for i in range(n):
|
|
34
|
+
_x,_y = no.get_position(i)
|
|
35
|
+
_w = w[i]
|
|
36
|
+
_h = h[i]
|
|
37
|
+
ax.add_patch(plt.Rectangle((_x-0.5*_w, _y-0.5*_h), width=_w, height=_h , facecolor='lightblue', edgecolor='black'))
|
|
38
|
+
ax.plot([_x,x[i]],[_y,y[i]])
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
anim = FuncAnimation(fig,
|
|
43
|
+
step, frames=100, interval=1)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
plt.show()
|
|
47
|
+
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import matplotlib.pyplot as plt
|
|
3
|
+
|
|
4
|
+
np.random.seed(0)
|
|
5
|
+
x, y = np.random.random((2,80))
|
|
6
|
+
|
|
7
|
+
# x = [0.5488135039273248,0.7151893663724195,0.6027633760716439,0.5448831829968969,0.4236547993389047,0.6458941130666561,0.4375872112626925,0.8917730007820798,0.9636627605010293,0.3834415188257777,0.7917250380826646,0.5288949197529045,0.5680445610939323,0.925596638292661,0.07103605819788694,0.08712929970154071,0.02021839744032572,0.832619845547938,0.7781567509498505,0.8700121482468192,0.978618342232764,0.7991585642167236,0.46147936225293185,0.7805291762864555,0.11827442586893322,0.6399210213275238,0.1433532874090464,0.9446689170495839,0.5218483217500717,0.4146619399905236]
|
|
8
|
+
# y = [0.26455561210462697,0.7742336894342167,0.45615033221654855,0.5684339488686485,0.018789800436355142,0.6176354970758771,0.6120957227224214,0.6169339968747569,0.9437480785146242,0.6818202991034834,0.359507900573786,0.43703195379934145,0.6976311959272649,0.06022547162926983,0.6667667154456677,0.6706378696181594,0.2103825610738409,0.1289262976548533,0.31542835092418386,0.3637107709426226,0.5701967704178796,0.43860151346232035,0.9883738380592262,0.10204481074802807,0.2088767560948347,0.16130951788499626,0.6531083254653984,0.2532916025397821,0.4663107728563063,0.24442559200160274]
|
|
9
|
+
|
|
10
|
+
# print(','.join([str(n) for n in x]))
|
|
11
|
+
# print(','.join([str(n) for n in y]))
|
|
12
|
+
|
|
13
|
+
fig, ax = plt.subplots()
|
|
14
|
+
plt.plot(x, y, 'bo')
|
|
15
|
+
texts = [plt.text(x[i], y[i], 'Text%s' %i, ha='center', va='center') for i in range(len(x))]
|
|
16
|
+
|
|
17
|
+
# get text sizes
|
|
18
|
+
plt.draw()
|
|
19
|
+
r = fig.canvas.get_renderer()
|
|
20
|
+
expand = (1.0, 1.0)
|
|
21
|
+
|
|
22
|
+
class Box():
|
|
23
|
+
def __init__(self, position, left, right, bottom, top):
|
|
24
|
+
self.x = position[0]
|
|
25
|
+
self.y = position[1]
|
|
26
|
+
|
|
27
|
+
self.x0 = self.x
|
|
28
|
+
self.y0 = self.y
|
|
29
|
+
|
|
30
|
+
self.dl = self.x - left
|
|
31
|
+
self.dr = right - self.x
|
|
32
|
+
self.du = top - self.y
|
|
33
|
+
self.dd = self.y - bottom
|
|
34
|
+
self.to_be_moved = np.array((0,0), dtype=float)
|
|
35
|
+
|
|
36
|
+
def move(self, dx, dy):
|
|
37
|
+
self.x += dx
|
|
38
|
+
self.y += dy
|
|
39
|
+
|
|
40
|
+
def reset(self):
|
|
41
|
+
self.to_be_moved = np.array((0,0), dtype=float)
|
|
42
|
+
|
|
43
|
+
def do_move(self):
|
|
44
|
+
self.move(*self.to_be_moved)
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def cx(self):
|
|
48
|
+
return self.x + 0.5 * self.dr - 0.5*self.dl
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def cy(self):
|
|
52
|
+
return self.y + 0.5 * self.du - 0.5*self.dd
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def width(self):
|
|
56
|
+
return self.dr + self.dl
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def height(self):
|
|
60
|
+
return self.du + self.dd
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def r(self):
|
|
64
|
+
return ((0.5*self.width)**2 + (0.5*self.height)**2 ) ** (0.5)
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def left(self):
|
|
68
|
+
return self.x - self.dl
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def right(self):
|
|
72
|
+
return self.x + self.dr
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def top(self):
|
|
76
|
+
return self.y + self.du
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def bottom(self):
|
|
80
|
+
return self.y - self.dd
|
|
81
|
+
|
|
82
|
+
def plot_home(self, ax):
|
|
83
|
+
ax.plot((self.x, self.x0),
|
|
84
|
+
(self.y, self.y0))
|
|
85
|
+
|
|
86
|
+
def plot_box(self, ax):
|
|
87
|
+
|
|
88
|
+
if self.width == 0 or self.height == 0:
|
|
89
|
+
ax.plot(self.cx, self.cy, 'r.')
|
|
90
|
+
else:
|
|
91
|
+
ax.plot([self.left, self.left, self.right, self.right, self.left],
|
|
92
|
+
[self.bottom, self.top, self.top, self.bottom, self.bottom])
|
|
93
|
+
|
|
94
|
+
def add_force_to_home(self, factor = 0.1):
|
|
95
|
+
dx = self.cx - self.x0
|
|
96
|
+
dy = self.cy - self.y0
|
|
97
|
+
|
|
98
|
+
self.to_be_moved[0] = self.to_be_moved[0] - factor * dx
|
|
99
|
+
self.to_be_moved[1] = self.to_be_moved[1] - factor * dy
|
|
100
|
+
|
|
101
|
+
def add_force_from(self, other, factor = 1.2):
|
|
102
|
+
if other.left > self.right:
|
|
103
|
+
return
|
|
104
|
+
if other.right < self.left:
|
|
105
|
+
return
|
|
106
|
+
if other.top < self.bottom:
|
|
107
|
+
return
|
|
108
|
+
if other.bottom > self.top:
|
|
109
|
+
return
|
|
110
|
+
|
|
111
|
+
self_to_other = (other.cx - self.cx, other.cy - self.cy)
|
|
112
|
+
distance = np.linalg.norm(self_to_other)
|
|
113
|
+
|
|
114
|
+
if distance > 1e-10:
|
|
115
|
+
D = np.array(self_to_other) / np.linalg.norm(self_to_other)
|
|
116
|
+
else:
|
|
117
|
+
D = np.array((0,1),dtype=float)
|
|
118
|
+
|
|
119
|
+
overlap = distance - factor*self.r - factor*other.r
|
|
120
|
+
|
|
121
|
+
self.to_be_moved = self.to_be_moved + 0.5 * overlap * D
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
boxes = []
|
|
128
|
+
points = []
|
|
129
|
+
|
|
130
|
+
for i in texts:
|
|
131
|
+
ext = i.get_window_extent(r).expanded(*expand).transformed(ax.transData.inverted())
|
|
132
|
+
#
|
|
133
|
+
#
|
|
134
|
+
# plt.plot([ext.xmin, ext.xmax, ext.xmax, ext.xmin, ext.xmin],[ext.ymax, ext.ymax, ext.ymin, ext.ymin, ext.ymax])
|
|
135
|
+
# print(i.get_position())
|
|
136
|
+
# print(ext)
|
|
137
|
+
boxes.append(Box(i.get_position(), left = ext.xmin, right = ext.xmax, top = ext.ymax, bottom = ext.ymin))
|
|
138
|
+
|
|
139
|
+
x,y = i.get_position()
|
|
140
|
+
#
|
|
141
|
+
points.append(Box(i.get_position(), x,x,y,y))
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
# Position iteratively
|
|
145
|
+
for i in range(100):
|
|
146
|
+
|
|
147
|
+
for b in boxes:
|
|
148
|
+
dx = b.cx - b.x0
|
|
149
|
+
dy = b.cy - b.y0
|
|
150
|
+
b.move(-0.1*dx,-0.1*dy)
|
|
151
|
+
|
|
152
|
+
for b in boxes:
|
|
153
|
+
for bb in boxes:
|
|
154
|
+
if b == bb:
|
|
155
|
+
continue
|
|
156
|
+
b.add_force_from(bb)
|
|
157
|
+
|
|
158
|
+
for p in points:
|
|
159
|
+
b.add_force_from(p)
|
|
160
|
+
|
|
161
|
+
b.do_move()
|
|
162
|
+
b.reset()
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
for b in boxes:
|
|
167
|
+
b.plot_box(ax=ax)
|
|
168
|
+
b.plot_home(ax=ax)
|
|
169
|
+
|
|
170
|
+
for p in points:
|
|
171
|
+
p.plot_box(ax=ax)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
for t,b in zip(texts, boxes):
|
|
176
|
+
t.set_position((b.x, b.y))
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
plt.show()
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import nooverlap
|
|
2
|
+
import numpy as np
|
|
3
|
+
import matplotlib.pyplot as plt
|
|
4
|
+
from matplotlib.animation import FuncAnimation
|
|
5
|
+
|
|
6
|
+
np.random.seed(0)
|
|
7
|
+
n = 50
|
|
8
|
+
x = 5*np.random.rand(n)
|
|
9
|
+
y = 2*np.random.rand(n)
|
|
10
|
+
w = np.random.rand(n)
|
|
11
|
+
h = 0.3*np.random.rand(n)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# make some random box data
|
|
15
|
+
def make_data(fac):
|
|
16
|
+
|
|
17
|
+
# create a nooverlap object
|
|
18
|
+
no = nooverlap.Pusher()
|
|
19
|
+
|
|
20
|
+
# add the boxes
|
|
21
|
+
for i in range(n):
|
|
22
|
+
no.add_box(x[i], y[i], w[i]/2, w[i]/2, h[i]/2, h[i]/2)
|
|
23
|
+
|
|
24
|
+
no.push_free(fac, fac)
|
|
25
|
+
|
|
26
|
+
return no
|
|
27
|
+
|
|
28
|
+
# draw the boxes
|
|
29
|
+
fig, ax = plt.subplots()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
ax.set_xlim(-1,6)
|
|
34
|
+
ax.set_ylim(-0.5,2.5)
|
|
35
|
+
|
|
36
|
+
for fac in (0.001, 0.3):
|
|
37
|
+
no = make_data(fac)
|
|
38
|
+
for i in range(n):
|
|
39
|
+
_x,_y = no.get_position(i)
|
|
40
|
+
_w = w[i]
|
|
41
|
+
_h = h[i]
|
|
42
|
+
ax.add_patch(plt.Rectangle((_x-0.5*_w, _y-0.5*_h), width=_w, height=_h , facecolor=(3*fac,3*fac,3*fac), edgecolor='black'))
|
|
43
|
+
ax.plot([_x,x[i]],[_y,y[i]])
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
plt.show()
|
|
48
|
+
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import nooverlap
|
|
2
|
+
import numpy as np
|
|
3
|
+
import matplotlib.pyplot as plt
|
|
4
|
+
from matplotlib.animation import FuncAnimation
|
|
5
|
+
|
|
6
|
+
np.random.seed(0)
|
|
7
|
+
|
|
8
|
+
# make some random box data
|
|
9
|
+
for n in range(100):
|
|
10
|
+
|
|
11
|
+
x = 5*np.random.rand(n)
|
|
12
|
+
y = 2*np.random.rand(n)
|
|
13
|
+
w = np.random.rand(n)
|
|
14
|
+
h = 0.3*np.random.rand(n)
|
|
15
|
+
|
|
16
|
+
# create a nooverlap object
|
|
17
|
+
no = nooverlap.Pusher()
|
|
18
|
+
|
|
19
|
+
# add the boxes
|
|
20
|
+
for i in range(n):
|
|
21
|
+
no.add_box(x[i], y[i], w[i]/2, w[i]/2, h[i]/2, h[i]/2)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
from time import time
|
|
25
|
+
start = time()
|
|
26
|
+
no.push_free(0,(n+1)/1000)
|
|
27
|
+
elapsed = time() - start
|
|
28
|
+
print(f'number {n/1000} took {elapsed*1000} ms')
|
|
29
|
+
|
|
30
|
+
# draw the boxes
|
|
31
|
+
fig, ax = plt.subplots()
|
|
32
|
+
|
|
33
|
+
ax.set_xlim(-1,6)
|
|
34
|
+
ax.set_ylim(-0.5,2.5)
|
|
35
|
+
for i in range(n):
|
|
36
|
+
_x,_y = no.get_position(i)
|
|
37
|
+
_w = w[i]
|
|
38
|
+
_h = h[i]
|
|
39
|
+
ax.add_patch(plt.Rectangle((_x-0.5*_w, _y-0.5*_h), width=_w, height=_h , facecolor='lightblue', edgecolor='black'))
|
|
40
|
+
ax.plot([_x,x[i]],[_y,y[i]])
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
plt.show()
|
|
45
|
+
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
use pyo3::prelude::*;
|
|
2
|
+
|
|
3
|
+
/// Define a class "Box" with a position (x,y) and a side (d_left, d_right, d_top, d_bottom)
|
|
4
|
+
/// The class has a method "overlap" that returns True if the box overlaps with another box
|
|
5
|
+
///
|
|
6
|
+
/// x- x+
|
|
7
|
+
/// y+
|
|
8
|
+
/// ------ top -------
|
|
9
|
+
/// left right
|
|
10
|
+
/// ----- bottom -----
|
|
11
|
+
/// y-
|
|
12
|
+
|
|
13
|
+
#[pyclass]
|
|
14
|
+
struct Box {
|
|
15
|
+
x0 : f32,
|
|
16
|
+
y0 : f32,
|
|
17
|
+
x: f32,
|
|
18
|
+
y: f32,
|
|
19
|
+
d_left: f32,
|
|
20
|
+
d_right: f32,
|
|
21
|
+
d_top: f32,
|
|
22
|
+
d_bottom: f32,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
#[pymethods]
|
|
26
|
+
impl Box {
|
|
27
|
+
|
|
28
|
+
#[new]
|
|
29
|
+
fn new(x0: f32, y0: f32, d_left: f32, d_right: f32, d_top: f32, d_bottom: f32) -> Self {
|
|
30
|
+
let x = x0;
|
|
31
|
+
let y = y0;
|
|
32
|
+
Box { x,y,x0,y0,d_left, d_right, d_top, d_bottom }
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
fn width(&self) -> f32 {
|
|
36
|
+
self.d_left + self.d_right
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
fn height(&self) -> f32 {
|
|
40
|
+
self.d_top + self.d_bottom
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
fn left(&self) -> f32 {
|
|
44
|
+
self.x - self.d_left
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
fn right(&self) -> f32 {
|
|
48
|
+
self.x + self.d_right
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
fn top(&self) -> f32 {
|
|
52
|
+
self.y + self.d_top
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
fn bottom(&self) -> f32 {
|
|
56
|
+
self.y - self.d_bottom
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
fn overlap(&self, other: &Box) -> bool {
|
|
60
|
+
if self.right() < other.left()
|
|
61
|
+
{
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
if self.left() > other.right()
|
|
65
|
+
{
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
if self.top() < other.bottom()
|
|
69
|
+
{
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
if self.bottom() > other.top()
|
|
73
|
+
{
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return true;
|
|
78
|
+
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
fn move_towards_origin(&mut self, distance : f32)
|
|
82
|
+
{
|
|
83
|
+
if self.left() > self.x0
|
|
84
|
+
{
|
|
85
|
+
self.x -= distance;
|
|
86
|
+
}
|
|
87
|
+
if self.right() < self.x0
|
|
88
|
+
{
|
|
89
|
+
self.x += distance;
|
|
90
|
+
}
|
|
91
|
+
if self.top() < self.y0
|
|
92
|
+
{
|
|
93
|
+
self.y += distance;
|
|
94
|
+
}
|
|
95
|
+
if self.bottom() > self.y0
|
|
96
|
+
{
|
|
97
|
+
self.y -= distance;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
fn get_overlapping_distance(&self, other: &Box) -> f32 {
|
|
103
|
+
// overlap in x-direction is the minimum of the right side of the first box and the left side of the second box
|
|
104
|
+
let mut overlap_x = other.left() - self.right();
|
|
105
|
+
if overlap_x < 0.0 {
|
|
106
|
+
overlap_x = self.left() - other.right();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if overlap_x < 0.0 {
|
|
110
|
+
overlap_x = 0.0;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
let mut overlap_y = other.top() - self.bottom();
|
|
115
|
+
if overlap_y < 0.0 {
|
|
116
|
+
overlap_y = self.top() - other.bottom();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if overlap_y < 0.0 {
|
|
120
|
+
overlap_y = 0.0;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return (overlap_x*overlap_x + overlap_y*overlap_y).sqrt();
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
fn line_to_center(&self, other: &Box) -> (f32, f32) {
|
|
129
|
+
let dx = self.x - other.x;
|
|
130
|
+
let dy = self.y - other.y;
|
|
131
|
+
return (dx, dy)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
fn distance_to_center(&self, other: &Box) -> f32 {
|
|
135
|
+
let (dx, dy) = self.line_to_center(other);
|
|
136
|
+
return (dx*dx + dy*dy).sqrt();
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
#[pyclass]
|
|
142
|
+
struct Pusher {
|
|
143
|
+
boxes : Vec<Box>,
|
|
144
|
+
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
#[pymethods]
|
|
148
|
+
impl Pusher {
|
|
149
|
+
|
|
150
|
+
#[new]
|
|
151
|
+
fn new() -> Self {
|
|
152
|
+
Pusher { boxes: Vec::new()}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/// Add a box to the pusher,
|
|
156
|
+
/// returns the index of the newly added box
|
|
157
|
+
|
|
158
|
+
fn add_box(&mut self, x0: f32, y0: f32, d_left: f32, d_right: f32, d_top: f32, d_bottom: f32) -> usize {
|
|
159
|
+
let new_box = Box::new(x0, y0, d_left, d_right, d_top, d_bottom);
|
|
160
|
+
self.boxes.push(new_box);
|
|
161
|
+
|
|
162
|
+
return self.boxes.len() - 1;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/// Push the boxes so that they don't overlap
|
|
166
|
+
/// returns true if the boxes were pushed
|
|
167
|
+
/// returns false if the boxes don't overlap
|
|
168
|
+
fn push_elements(&mut self, push_factor_horizontal:f32, push_factor_vertical:f32) -> bool{
|
|
169
|
+
let mut push = false;
|
|
170
|
+
for i in 0..self.boxes.len() {
|
|
171
|
+
|
|
172
|
+
for j in 0..self.boxes.len() {
|
|
173
|
+
if i == j {
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
if self.boxes[i].overlap(&self.boxes[j]) {
|
|
177
|
+
|
|
178
|
+
let (mut dx, mut dy) = self.boxes[i].line_to_center(&self.boxes[j]);
|
|
179
|
+
|
|
180
|
+
let overlap = self.boxes[i].get_overlapping_distance(&self.boxes[j]);
|
|
181
|
+
|
|
182
|
+
dx = dx * overlap;
|
|
183
|
+
dy = dy * overlap;
|
|
184
|
+
|
|
185
|
+
// maximize the move distance to the push factor
|
|
186
|
+
// times the size of the box
|
|
187
|
+
|
|
188
|
+
if dx.abs() > push_factor_horizontal * self.boxes[i].width()
|
|
189
|
+
{
|
|
190
|
+
dx = dx.signum() * push_factor_horizontal * self.boxes[i].width();
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if dy.abs() > push_factor_vertical * self.boxes[i].height()
|
|
194
|
+
{
|
|
195
|
+
dy = dy.signum() * push_factor_vertical * self.boxes[i].height();
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
self.boxes[i].x += dx;
|
|
200
|
+
self.boxes[i].y += dy;
|
|
201
|
+
self.boxes[j].x -= dx;
|
|
202
|
+
self.boxes[j].y -= dy;
|
|
203
|
+
|
|
204
|
+
push = true
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
push
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/// Pull all elements towards their original position
|
|
212
|
+
/// if they so not overlap it
|
|
213
|
+
fn pull_elements(&mut self, distance : f32)
|
|
214
|
+
{
|
|
215
|
+
// loop over all boxes
|
|
216
|
+
|
|
217
|
+
for b in self.boxes.iter_mut()
|
|
218
|
+
{
|
|
219
|
+
b.move_towards_origin(distance);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/// Get the position of the box
|
|
225
|
+
/// returns the position of the box
|
|
226
|
+
fn get_position(&self, index: usize) -> (f32, f32) {
|
|
227
|
+
(self.boxes[index].x, self.boxes[index].y)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
fn push_free(&mut self, push_factor_horizontal : f32, push_factor_vertical : f32)
|
|
231
|
+
|
|
232
|
+
{
|
|
233
|
+
if push_factor_horizontal <= 0.0 && push_factor_vertical <= 0.0
|
|
234
|
+
{
|
|
235
|
+
panic!("At least one of the push factors should be larger than 0.0");
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
loop {
|
|
239
|
+
let pushed = self.push_elements(push_factor_horizontal, push_factor_vertical);
|
|
240
|
+
if !pushed
|
|
241
|
+
{
|
|
242
|
+
break;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/// A Python module implemented in Rust.
|
|
250
|
+
#[pymodule]
|
|
251
|
+
fn nooverlap(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
|
252
|
+
m.add_class::<Box>()?;
|
|
253
|
+
m.add_class::<Pusher>()?;
|
|
254
|
+
Ok(())
|
|
255
|
+
}
|