pycivil 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.
- pycivil-0.1.0/PKG-INFO +152 -0
- pycivil-0.1.0/README.md +121 -0
- pycivil-0.1.0/pycivil/EXAExceptions.py +8 -0
- pycivil-0.1.0/pycivil/EXAGeometry/__init__.py +0 -0
- pycivil-0.1.0/pycivil/EXAGeometry/clouds.py +298 -0
- pycivil-0.1.0/pycivil/EXAGeometry/geometry.py +742 -0
- pycivil-0.1.0/pycivil/EXAGeometry/shapes.py +489 -0
- pycivil-0.1.0/pycivil/EXAGeotechnical/base.py +437 -0
- pycivil-0.1.0/pycivil/EXAMicroServices/__init__.py +0 -0
- pycivil-0.1.0/pycivil/EXAMicroServices/dbutils.py +159 -0
- pycivil-0.1.0/pycivil/EXAMicroServices/fast-aster.py +149 -0
- pycivil-0.1.0/pycivil/EXAMicroServices/fast.py +708 -0
- pycivil-0.1.0/pycivil/EXAMicroServices/models.py +397 -0
- pycivil-0.1.0/pycivil/EXAMicroServices/server-settings.json +4 -0
- pycivil-0.1.0/pycivil/EXAMicroServices/settings.py +31 -0
- pycivil-0.1.0/pycivil/EXAMicroServices/srvRcSecCheck.py +1177 -0
- pycivil-0.1.0/pycivil/EXAParametric/box.py +518 -0
- pycivil-0.1.0/pycivil/EXAParametric/tube.py +665 -0
- pycivil-0.1.0/pycivil/EXAStructural/__init__.py +0 -0
- pycivil-0.1.0/pycivil/EXAStructural/checkable.py +107 -0
- pycivil-0.1.0/pycivil/EXAStructural/codeEC211.py +128 -0
- pycivil-0.1.0/pycivil/EXAStructural/codeEC212.py +640 -0
- pycivil-0.1.0/pycivil/EXAStructural/codeEC2Rules.py +272 -0
- pycivil-0.1.0/pycivil/EXAStructural/codeNTC2018.py +1883 -0
- pycivil-0.1.0/pycivil/EXAStructural/codeNTC2018Rules.py +239 -0
- pycivil-0.1.0/pycivil/EXAStructural/codes.py +226 -0
- pycivil-0.1.0/pycivil/EXAStructural/loads.py +129 -0
- pycivil-0.1.0/pycivil/EXAStructural/materials.py +1019 -0
- pycivil-0.1.0/pycivil/EXAStructural/modeler.py +541 -0
- pycivil-0.1.0/pycivil/EXAStructural/plot.py +1039 -0
- pycivil-0.1.0/pycivil/EXAStructural/sections.py +1514 -0
- pycivil-0.1.0/pycivil/EXAStructural/templateRCGen.py +1500 -0
- pycivil-0.1.0/pycivil/EXAStructural/templateRCRect.py +1625 -0
- pycivil-0.1.0/pycivil/EXAStructural/templateRCRectFire.py +846 -0
- pycivil-0.1.0/pycivil/EXAStructuralCheckable/RcsRectangular.py +954 -0
- pycivil-0.1.0/pycivil/EXAStructuralCheckable/__init__.py +0 -0
- pycivil-0.1.0/pycivil/EXAStructuralModel/fe.py +1316 -0
- pycivil-0.1.0/pycivil/EXAUtils/__init__.py +0 -0
- pycivil-0.1.0/pycivil/EXAUtils/binaries-runner-settings.json +6 -0
- pycivil-0.1.0/pycivil/EXAUtils/codeAster.py +1082 -0
- pycivil-0.1.0/pycivil/EXAUtils/dbtools.py +270 -0
- pycivil-0.1.0/pycivil/EXAUtils/gmsh.py +131 -0
- pycivil-0.1.0/pycivil/EXAUtils/latexCheatSheets.py +197 -0
- pycivil-0.1.0/pycivil/EXAUtils/latexReportMakers.py +433 -0
- pycivil-0.1.0/pycivil/EXAUtils/logging.py +47 -0
- pycivil-0.1.0/pycivil/EXAUtils/math.py +148 -0
- pycivil-0.1.0/pycivil/EXAUtils/report.py +312 -0
- pycivil-0.1.0/pycivil/EXAUtils/solver.py +119 -0
- pycivil-0.1.0/pycivil/EXAUtils/tables.py +126 -0
- pycivil-0.1.0/pycivil/EXAUtils/validator.py +61 -0
- pycivil-0.1.0/pycivil/EXAUtils/vtk.py +438 -0
- pycivil-0.1.0/pycivil/__init__.py +1 -0
- pycivil-0.1.0/pyproject.toml +142 -0
pycivil-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: pycivil
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Structural Engineering Utilities
|
|
5
|
+
Author: Luigi Paone
|
|
6
|
+
Author-email: ppc.luigi.paone@gmail.com
|
|
7
|
+
Requires-Python: >=3.12,<4.0
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
10
|
+
Requires-Dist: Jinja2 (>=3.1.2,<4.0.0)
|
|
11
|
+
Requires-Dist: fastapi (>=0.115.6,<0.116.0)
|
|
12
|
+
Requires-Dist: gmsh (>=4.11.1,<5.0.0)
|
|
13
|
+
Requires-Dist: matplotlib (>=3.7.2,<4.0.0)
|
|
14
|
+
Requires-Dist: numpy (>=1.25.2,<2.0.0)
|
|
15
|
+
Requires-Dist: odfpy (>=1.4.0,<2.0.0)
|
|
16
|
+
Requires-Dist: pandas (>=2.1.0,<3.0.0)
|
|
17
|
+
Requires-Dist: pexpect (>=4.8.0,<5.0.0)
|
|
18
|
+
Requires-Dist: pydantic (>=2.7,<3.0)
|
|
19
|
+
Requires-Dist: pydantic-settings (>=2.1.0,<3.0.0)
|
|
20
|
+
Requires-Dist: pymongo (>=4.4.1,<5.0.0)
|
|
21
|
+
Requires-Dist: pytest (>=8.4.1,<9.0.0)
|
|
22
|
+
Requires-Dist: python-multipart (==0.0.6)
|
|
23
|
+
Requires-Dist: requests (>=2.31.0,<3.0.0)
|
|
24
|
+
Requires-Dist: rich (>=13.7.1,<14.0.0)
|
|
25
|
+
Requires-Dist: typing-extensions (>=4.11.0,<5.0.0)
|
|
26
|
+
Requires-Dist: uvicorn (>=0.34.0,<0.35.0)
|
|
27
|
+
Requires-Dist: vtk (>=9.2.6,<10.0.0)
|
|
28
|
+
Requires-Dist: xstrumodeler (==0.0.1)
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# sws-mind
|
|
32
|
+
|
|
33
|
+
## Descrizione del progetto
|
|
34
|
+
|
|
35
|
+
Il progetto consiste nella realizzazione di una infrastruttura hardware e di contenuto software. Lo scopo sarà quello di consolidare il knowhow tecnico ingegneristico e per dare la possibilità ai professionisti di sws di utilizzarlo in modo confortevole per velocità ed aspetto.
|
|
36
|
+
|
|
37
|
+
L'infrastruttura hardware sarà un server in grado di processare richieste di vario tipo da parte di un client. Quest'ultimo sarà totalmente agnostico delle modalità di esecuzione del server e potrà unicamente effettuare chiamate ed attendere risposte su uno dei protocolli progettati per tale scopo. Le risposte potranno essere flussi di dati o interi file.
|
|
38
|
+
|
|
39
|
+
Tale suddivisione risulta necessaria per varie ragioni.
|
|
40
|
+
|
|
41
|
+
1. **indipendenza di sviluppo:** significa che una volta stabilito il protocollo di comunicazione si potrà sviluppare in contemporanea sia lato server che lato client. Ad esempio durante l'aggiunta di codice nel server necessario all'esecuzione di un preciso task, il client potrà contemporaneamente essere preparato per ricevere i risultati in forma visuale.
|
|
42
|
+
1. **separazione di competenze:** poichè spesso per il contenuto del codice sorgente sarà necessario il lavoro di ingegneri esperti nelle varie materie (gallerie, ponti, F.E.M.), nel lavoro sul client si potranno utilizzare anche solo esperti informatici.
|
|
43
|
+
1. **gestione multiutente** da varie parti del mondo potrebbe essere necessario effettuare richieste al server anche contemporaneamente. In tal caso sarà necessario il possesso del software all'interno dei vari client e tale software (ad esempio su tablet o pc) invierà richieste allo stesso server.
|
|
44
|
+
1. **gestione della qualità** con la scoperta di errori o bugs all'interno del codice, ci si potrà concentrare sulla loro risoluzione senza necessità di aggiornare il software sul client.
|
|
45
|
+
1. **closed source per clienti** sarà possibile vendere servizi o microservizi ad imprese o amministrazioni pubbliche senza che queste possano conoscere il contenuto tecnico ingegneristico e gli algoritmi necessari alla realizzazione del prodotto-servizio.
|
|
46
|
+
1. **gestione delle autorizzazioni** sarà possibile ad esempio fornire l'autorizzazione ad un utente dell'organizzazione ad effettuare dei task per un determinato periodo di tempo. Potrà inoltre essere venduto un prodotto-servizio all'esterno con licenza a tempo.
|
|
47
|
+
|
|
48
|
+
## Fasi di sviluppo
|
|
49
|
+
|
|
50
|
+
1. **individuazione degli strumenti informatici hard e soft client e server**
|
|
51
|
+
1. Selezionare l'hardware del server e del client
|
|
52
|
+
1. Decidere il protocollo di comunicazione client/server
|
|
53
|
+
1. **progettazione di alcuni piccoli prodotto-servizio o di qualche microservizio**
|
|
54
|
+
1. **realizzazione dell'hardware e software necessario al server**
|
|
55
|
+
1. Realizzazione del software
|
|
56
|
+
1. Realizzazione dell'hardware
|
|
57
|
+
1. **realizzazione del microservizio sul server**
|
|
58
|
+
1. implementazione del microervizio
|
|
59
|
+
1. collegare il microservizio al protocollo di comunicazione scelto
|
|
60
|
+
1. **realizzazione del software su un tipo di client e testare la comunicazione col server**
|
|
61
|
+
|
|
62
|
+
## Docker container
|
|
63
|
+
|
|
64
|
+
You can run the REST API inside a docker container with the following command:
|
|
65
|
+
|
|
66
|
+
```shell
|
|
67
|
+
docker run -d Dockerfile --name pycivile -p 8000:8000 -e UVICORN_BIND=0.0.0.0
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
> **NOTE**: the environment variable UVICORN_BIND tells the web server on which address to listen to.
|
|
71
|
+
> 0.0.0.0 allows you to reach the API from everywhere, but can be a security risk!
|
|
72
|
+
|
|
73
|
+
Or, if you don't want to clone the repository:
|
|
74
|
+
|
|
75
|
+
```shell
|
|
76
|
+
docker build -t pycivile https://gitlab.com/luigi_paone/pycivile.git
|
|
77
|
+
docker run -d pycivile --name pycivile -p 8000:8000 -e UVICORN_BIND=0.0.0.0
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Or, if you're a docker-compose guy, you can run the [docker-compose.yml](docker-compose.yml) file with:
|
|
81
|
+
|
|
82
|
+
```shell
|
|
83
|
+
docker-compose up
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
This will also create the database (mongodb) and Code_Aster containers.
|
|
87
|
+
|
|
88
|
+
### Environment variables
|
|
89
|
+
|
|
90
|
+
| Name | Description | Default |
|
|
91
|
+
| ---------------------- | -------------------------------------------- | ---------------------------------- |
|
|
92
|
+
| `WORKING_DATA_PATH` | Path for data storage | `/app/working-data` |
|
|
93
|
+
| `XLS_SHEETS_DATA_PATH` | Folder with structural calculation workbooks | `/app/res/excel-structural-sheets` |
|
|
94
|
+
| `DB_HOST` | Database hostname | `localhost` |
|
|
95
|
+
| `DB_PORT` | Database Port | `27013` |
|
|
96
|
+
| `DB_USERNAME` | Database username | empty (no user) |
|
|
97
|
+
| `DB_PASSWORD` | Database password | empty (no password) |
|
|
98
|
+
|
|
99
|
+
## Development
|
|
100
|
+
|
|
101
|
+
- Install [poetry](https://python-poetry.org/docs/#installation) and [task](https://taskfile.dev/installation/)
|
|
102
|
+
- run `task init` do initialize the python environment and install the pre-commit hooks
|
|
103
|
+
- before committing code changes, you can run `task` to perform automated checks. You can also run them separately:
|
|
104
|
+
- `task lint` fixes and checks the code and documentation
|
|
105
|
+
- `task mypy` performs type checking
|
|
106
|
+
- `task test` runs the tests with `pytest`
|
|
107
|
+
- `task security` scans the dependencies for known vulnerabilities
|
|
108
|
+
|
|
109
|
+
> **NOTE**: the `lint` task is executed automatically when you commit the changes to ensure that only good quality code is added to the repository.
|
|
110
|
+
|
|
111
|
+
## Launch tests with unittest
|
|
112
|
+
|
|
113
|
+
### Local development
|
|
114
|
+
|
|
115
|
+
You need to use
|
|
116
|
+
|
|
117
|
+
```shell
|
|
118
|
+
export BASE_PATH_RES=<resources path> && export WORKING_DATA_PATH=<working data>
|
|
119
|
+
source goTests
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
where *resources path* typically is sibling of pycivil module and
|
|
123
|
+
*working data* were backend can write singles job.
|
|
124
|
+
|
|
125
|
+
Normally on my pc:
|
|
126
|
+
|
|
127
|
+
```shell
|
|
128
|
+
export BASE_PATH_RES=./res && export WORKING_DATA_PATH=$HOME/Documents/dev/run/pycivile/working-data
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Container development
|
|
132
|
+
|
|
133
|
+
Go into container shell with:
|
|
134
|
+
|
|
135
|
+
```shell
|
|
136
|
+
docker compose exec -ti pycivile /bin/bash
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
and then
|
|
140
|
+
|
|
141
|
+
```shell
|
|
142
|
+
/bin/bash goTests
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
#### Remove all volumes
|
|
146
|
+
|
|
147
|
+
This remove all volumes and data. Next relaunch the volumes will be build
|
|
148
|
+
|
|
149
|
+
```shell
|
|
150
|
+
docker-compose down -v
|
|
151
|
+
```
|
|
152
|
+
|
pycivil-0.1.0/README.md
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# sws-mind
|
|
2
|
+
|
|
3
|
+
## Descrizione del progetto
|
|
4
|
+
|
|
5
|
+
Il progetto consiste nella realizzazione di una infrastruttura hardware e di contenuto software. Lo scopo sarà quello di consolidare il knowhow tecnico ingegneristico e per dare la possibilità ai professionisti di sws di utilizzarlo in modo confortevole per velocità ed aspetto.
|
|
6
|
+
|
|
7
|
+
L'infrastruttura hardware sarà un server in grado di processare richieste di vario tipo da parte di un client. Quest'ultimo sarà totalmente agnostico delle modalità di esecuzione del server e potrà unicamente effettuare chiamate ed attendere risposte su uno dei protocolli progettati per tale scopo. Le risposte potranno essere flussi di dati o interi file.
|
|
8
|
+
|
|
9
|
+
Tale suddivisione risulta necessaria per varie ragioni.
|
|
10
|
+
|
|
11
|
+
1. **indipendenza di sviluppo:** significa che una volta stabilito il protocollo di comunicazione si potrà sviluppare in contemporanea sia lato server che lato client. Ad esempio durante l'aggiunta di codice nel server necessario all'esecuzione di un preciso task, il client potrà contemporaneamente essere preparato per ricevere i risultati in forma visuale.
|
|
12
|
+
1. **separazione di competenze:** poichè spesso per il contenuto del codice sorgente sarà necessario il lavoro di ingegneri esperti nelle varie materie (gallerie, ponti, F.E.M.), nel lavoro sul client si potranno utilizzare anche solo esperti informatici.
|
|
13
|
+
1. **gestione multiutente** da varie parti del mondo potrebbe essere necessario effettuare richieste al server anche contemporaneamente. In tal caso sarà necessario il possesso del software all'interno dei vari client e tale software (ad esempio su tablet o pc) invierà richieste allo stesso server.
|
|
14
|
+
1. **gestione della qualità** con la scoperta di errori o bugs all'interno del codice, ci si potrà concentrare sulla loro risoluzione senza necessità di aggiornare il software sul client.
|
|
15
|
+
1. **closed source per clienti** sarà possibile vendere servizi o microservizi ad imprese o amministrazioni pubbliche senza che queste possano conoscere il contenuto tecnico ingegneristico e gli algoritmi necessari alla realizzazione del prodotto-servizio.
|
|
16
|
+
1. **gestione delle autorizzazioni** sarà possibile ad esempio fornire l'autorizzazione ad un utente dell'organizzazione ad effettuare dei task per un determinato periodo di tempo. Potrà inoltre essere venduto un prodotto-servizio all'esterno con licenza a tempo.
|
|
17
|
+
|
|
18
|
+
## Fasi di sviluppo
|
|
19
|
+
|
|
20
|
+
1. **individuazione degli strumenti informatici hard e soft client e server**
|
|
21
|
+
1. Selezionare l'hardware del server e del client
|
|
22
|
+
1. Decidere il protocollo di comunicazione client/server
|
|
23
|
+
1. **progettazione di alcuni piccoli prodotto-servizio o di qualche microservizio**
|
|
24
|
+
1. **realizzazione dell'hardware e software necessario al server**
|
|
25
|
+
1. Realizzazione del software
|
|
26
|
+
1. Realizzazione dell'hardware
|
|
27
|
+
1. **realizzazione del microservizio sul server**
|
|
28
|
+
1. implementazione del microervizio
|
|
29
|
+
1. collegare il microservizio al protocollo di comunicazione scelto
|
|
30
|
+
1. **realizzazione del software su un tipo di client e testare la comunicazione col server**
|
|
31
|
+
|
|
32
|
+
## Docker container
|
|
33
|
+
|
|
34
|
+
You can run the REST API inside a docker container with the following command:
|
|
35
|
+
|
|
36
|
+
```shell
|
|
37
|
+
docker run -d Dockerfile --name pycivile -p 8000:8000 -e UVICORN_BIND=0.0.0.0
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
> **NOTE**: the environment variable UVICORN_BIND tells the web server on which address to listen to.
|
|
41
|
+
> 0.0.0.0 allows you to reach the API from everywhere, but can be a security risk!
|
|
42
|
+
|
|
43
|
+
Or, if you don't want to clone the repository:
|
|
44
|
+
|
|
45
|
+
```shell
|
|
46
|
+
docker build -t pycivile https://gitlab.com/luigi_paone/pycivile.git
|
|
47
|
+
docker run -d pycivile --name pycivile -p 8000:8000 -e UVICORN_BIND=0.0.0.0
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Or, if you're a docker-compose guy, you can run the [docker-compose.yml](docker-compose.yml) file with:
|
|
51
|
+
|
|
52
|
+
```shell
|
|
53
|
+
docker-compose up
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
This will also create the database (mongodb) and Code_Aster containers.
|
|
57
|
+
|
|
58
|
+
### Environment variables
|
|
59
|
+
|
|
60
|
+
| Name | Description | Default |
|
|
61
|
+
| ---------------------- | -------------------------------------------- | ---------------------------------- |
|
|
62
|
+
| `WORKING_DATA_PATH` | Path for data storage | `/app/working-data` |
|
|
63
|
+
| `XLS_SHEETS_DATA_PATH` | Folder with structural calculation workbooks | `/app/res/excel-structural-sheets` |
|
|
64
|
+
| `DB_HOST` | Database hostname | `localhost` |
|
|
65
|
+
| `DB_PORT` | Database Port | `27013` |
|
|
66
|
+
| `DB_USERNAME` | Database username | empty (no user) |
|
|
67
|
+
| `DB_PASSWORD` | Database password | empty (no password) |
|
|
68
|
+
|
|
69
|
+
## Development
|
|
70
|
+
|
|
71
|
+
- Install [poetry](https://python-poetry.org/docs/#installation) and [task](https://taskfile.dev/installation/)
|
|
72
|
+
- run `task init` do initialize the python environment and install the pre-commit hooks
|
|
73
|
+
- before committing code changes, you can run `task` to perform automated checks. You can also run them separately:
|
|
74
|
+
- `task lint` fixes and checks the code and documentation
|
|
75
|
+
- `task mypy` performs type checking
|
|
76
|
+
- `task test` runs the tests with `pytest`
|
|
77
|
+
- `task security` scans the dependencies for known vulnerabilities
|
|
78
|
+
|
|
79
|
+
> **NOTE**: the `lint` task is executed automatically when you commit the changes to ensure that only good quality code is added to the repository.
|
|
80
|
+
|
|
81
|
+
## Launch tests with unittest
|
|
82
|
+
|
|
83
|
+
### Local development
|
|
84
|
+
|
|
85
|
+
You need to use
|
|
86
|
+
|
|
87
|
+
```shell
|
|
88
|
+
export BASE_PATH_RES=<resources path> && export WORKING_DATA_PATH=<working data>
|
|
89
|
+
source goTests
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
where *resources path* typically is sibling of pycivil module and
|
|
93
|
+
*working data* were backend can write singles job.
|
|
94
|
+
|
|
95
|
+
Normally on my pc:
|
|
96
|
+
|
|
97
|
+
```shell
|
|
98
|
+
export BASE_PATH_RES=./res && export WORKING_DATA_PATH=$HOME/Documents/dev/run/pycivile/working-data
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Container development
|
|
102
|
+
|
|
103
|
+
Go into container shell with:
|
|
104
|
+
|
|
105
|
+
```shell
|
|
106
|
+
docker compose exec -ti pycivile /bin/bash
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
and then
|
|
110
|
+
|
|
111
|
+
```shell
|
|
112
|
+
/bin/bash goTests
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
#### Remove all volumes
|
|
116
|
+
|
|
117
|
+
This remove all volumes and data. Next relaunch the volumes will be build
|
|
118
|
+
|
|
119
|
+
```shell
|
|
120
|
+
docker-compose down -v
|
|
121
|
+
```
|
|
File without changes
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import copy
|
|
2
|
+
import pickle
|
|
3
|
+
from typing import List, Any, Tuple, Union, Optional
|
|
4
|
+
from warnings import warn
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from pydantic import BaseModel
|
|
7
|
+
|
|
8
|
+
from pycivil.EXAGeometry.geometry import Point2d, Vector2d, Seg2d, Point2dMdl
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class PointCloud2dMdl(BaseModel):
|
|
12
|
+
points: Optional[List[Point2dMdl]] = None
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class PointCloud2d:
|
|
16
|
+
def __init__(self, *args):
|
|
17
|
+
|
|
18
|
+
if len(args) == 0:
|
|
19
|
+
self.__points: List[Point2d] = []
|
|
20
|
+
self.__metaData: List[List[Any]] = []
|
|
21
|
+
self.__signs: List[float] = []
|
|
22
|
+
|
|
23
|
+
elif len(args) == 1 and isinstance(args[0], list):
|
|
24
|
+
if len(args[0]) == 0:
|
|
25
|
+
self.__points: List[Point2d] = []
|
|
26
|
+
self.__metaData: List[List[Any]] = []
|
|
27
|
+
self.__signs: List[float] = []
|
|
28
|
+
else:
|
|
29
|
+
self.__points: List[Point2d] = [Point2d()] * len(args[0])
|
|
30
|
+
self.__signs: List[float] = [0.0] * (len(args[0]) - 2)
|
|
31
|
+
if isinstance(args[0][0], list):
|
|
32
|
+
self.__metaData: List[List[Any]] = [[]] * len(args[0])
|
|
33
|
+
for idx in range(len(self.__points)):
|
|
34
|
+
metaLenght = len(args[0][idx])
|
|
35
|
+
assert metaLenght >= 2
|
|
36
|
+
|
|
37
|
+
assert isinstance(args[0][idx][0], (float, int))
|
|
38
|
+
assert isinstance(args[0][idx][1], (float, int))
|
|
39
|
+
self.__points[idx] = Point2d(args[0][idx][0], args[0][idx][1])
|
|
40
|
+
|
|
41
|
+
if metaLenght >= 2:
|
|
42
|
+
self.__metaData[idx] = args[0][idx][2:(metaLenght + 1)]
|
|
43
|
+
|
|
44
|
+
if idx >= 2:
|
|
45
|
+
vecStart = Vector2d(
|
|
46
|
+
self.__points[idx - 2], self.__points[idx - 1]
|
|
47
|
+
)
|
|
48
|
+
vecEnd = Vector2d(self.__points[idx - 1], self.__points[idx])
|
|
49
|
+
self.__signs[idx - 2] = vecStart.cross(vecEnd)
|
|
50
|
+
|
|
51
|
+
elif isinstance(args[0][0], Point2d):
|
|
52
|
+
self.__metaData: List[List[Any]] = []
|
|
53
|
+
for idx in range(len(self.__points)):
|
|
54
|
+
assert isinstance(args[0][idx], Point2d)
|
|
55
|
+
self.__points[idx] = args[0][idx]
|
|
56
|
+
if idx >= 2:
|
|
57
|
+
vecStart = Vector2d(
|
|
58
|
+
self.__points[idx - 2], self.__points[idx - 1]
|
|
59
|
+
)
|
|
60
|
+
vecEnd = Vector2d(self.__points[idx - 1], self.__points[idx])
|
|
61
|
+
self.__signs[idx - 2] = vecStart.cross(vecEnd)
|
|
62
|
+
else:
|
|
63
|
+
raise ValueError("Only list or Point2d for first arg !!!")
|
|
64
|
+
|
|
65
|
+
elif len(args) == 1 and isinstance(args[0], PointCloud2dMdl):
|
|
66
|
+
self.__metaData: List[List[Any]] = []
|
|
67
|
+
self.__signs: List[float] = []
|
|
68
|
+
self.__points: List[Point2d] = []
|
|
69
|
+
if args[0].points is not None:
|
|
70
|
+
for p in args[0].points:
|
|
71
|
+
self.__points.append(Point2d(x=p.xy[0],y=p.xy[1]))
|
|
72
|
+
else:
|
|
73
|
+
raise ValueError("Only one arg list or PointCloud2dMdl !!!")
|
|
74
|
+
|
|
75
|
+
self.__index = 0
|
|
76
|
+
|
|
77
|
+
def __iter__(self):
|
|
78
|
+
return self
|
|
79
|
+
|
|
80
|
+
def __next__(self):
|
|
81
|
+
if self.__index < len(self.__points):
|
|
82
|
+
item = self.__points[self.__index]
|
|
83
|
+
self.__index += 1
|
|
84
|
+
return item
|
|
85
|
+
else:
|
|
86
|
+
raise StopIteration
|
|
87
|
+
|
|
88
|
+
def __getitem__(self, key):
|
|
89
|
+
print(f"self.__points = {self.__points}")
|
|
90
|
+
return self.__points[key]
|
|
91
|
+
|
|
92
|
+
def __str__(self):
|
|
93
|
+
dispstr = self.__points.__str__()
|
|
94
|
+
return dispstr
|
|
95
|
+
|
|
96
|
+
def __len__(self):
|
|
97
|
+
return len(self.__points)
|
|
98
|
+
|
|
99
|
+
def isConvex(self) -> bool:
|
|
100
|
+
if len(self.__signs) > 0:
|
|
101
|
+
s = self.__signs[0] > 0
|
|
102
|
+
for i in range(1, len(self.__signs)):
|
|
103
|
+
if (self.__signs[i] > 0) != s:
|
|
104
|
+
return False
|
|
105
|
+
return True
|
|
106
|
+
print(
|
|
107
|
+
"WARNING: Can't know about convexity because lenght is null !!!. Return False"
|
|
108
|
+
)
|
|
109
|
+
return False
|
|
110
|
+
|
|
111
|
+
def __eq__(self, __value) -> bool:
|
|
112
|
+
if len(__value) != len(self):
|
|
113
|
+
return False
|
|
114
|
+
for idx in range(len(__value)):
|
|
115
|
+
if self.__points[idx] != __value[idx]:
|
|
116
|
+
return False
|
|
117
|
+
return True
|
|
118
|
+
|
|
119
|
+
def getPoints(self):
|
|
120
|
+
return self.__points
|
|
121
|
+
|
|
122
|
+
def getMetaAt(self, idx: int) -> List[Any]:
|
|
123
|
+
return self.__metaData[idx]
|
|
124
|
+
|
|
125
|
+
def boundingBox(self) -> Tuple[float, float, float, float]:
|
|
126
|
+
bb = [0.0, 0.0, 0.0, 0.0]
|
|
127
|
+
for p in self.__points:
|
|
128
|
+
if p.x < bb[0]:
|
|
129
|
+
bb[0] = p.x
|
|
130
|
+
if p.x > bb[1]:
|
|
131
|
+
bb[1] = p.x
|
|
132
|
+
if p.y < bb[2]:
|
|
133
|
+
bb[2] = p.y
|
|
134
|
+
if p.y > bb[3]:
|
|
135
|
+
bb[3] = p.y
|
|
136
|
+
|
|
137
|
+
return bb[0], bb[1], bb[2], bb[3]
|
|
138
|
+
|
|
139
|
+
def contains(
|
|
140
|
+
self,
|
|
141
|
+
xp: float,
|
|
142
|
+
yp: float,
|
|
143
|
+
ro: Tuple[float, float],
|
|
144
|
+
convex: bool = True,
|
|
145
|
+
rayFromCenter=True,
|
|
146
|
+
center: Union[Point2d, None] = None,
|
|
147
|
+
):
|
|
148
|
+
|
|
149
|
+
if center is None:
|
|
150
|
+
center = Point2d(0.0, 0.0)
|
|
151
|
+
|
|
152
|
+
if not isinstance(xp, float) and not isinstance(yp, int):
|
|
153
|
+
raise ValueError("Only float or int type for N!!!")
|
|
154
|
+
|
|
155
|
+
if not isinstance(xp, float) and not isinstance(yp, int):
|
|
156
|
+
raise ValueError("Only float or int type for M!!!")
|
|
157
|
+
|
|
158
|
+
if not convex:
|
|
159
|
+
raise ValueError("Only algorithm for convex shape !!!")
|
|
160
|
+
|
|
161
|
+
if len(self.__points) < 3:
|
|
162
|
+
raise ValueError("Containts at least 3 points !!!")
|
|
163
|
+
|
|
164
|
+
p = Point2d(xp, yp)
|
|
165
|
+
|
|
166
|
+
# ro useful for intersection point. Normally ro is estimed
|
|
167
|
+
# with boudingbox calculated during forming interaction
|
|
168
|
+
# domain.
|
|
169
|
+
rox = ro[0]
|
|
170
|
+
roy = ro[1]
|
|
171
|
+
|
|
172
|
+
contained: bool = True
|
|
173
|
+
pintersect: List[Point2d] = []
|
|
174
|
+
pintersectFactor: float = 0.0
|
|
175
|
+
pintersectFactors: List[float] = []
|
|
176
|
+
pindex: List[int] = [] # Index that intersection found
|
|
177
|
+
|
|
178
|
+
p1 = self.__points[0]
|
|
179
|
+
p2 = self.__points[0 + 1]
|
|
180
|
+
v2 = Vector2d(p2, p1)
|
|
181
|
+
v1 = Vector2d(p1, p)
|
|
182
|
+
|
|
183
|
+
minLenght = v1.norm_roxroy(rox, roy)
|
|
184
|
+
if rayFromCenter:
|
|
185
|
+
normro = False
|
|
186
|
+
seg = Seg2d(p2, p1)
|
|
187
|
+
ray = Seg2d(p, center)
|
|
188
|
+
hasIntersection, pint = seg.intersectAndContaints(ray)
|
|
189
|
+
if hasIntersection:
|
|
190
|
+
pintersect.append(pint)
|
|
191
|
+
pintersectFactors.append(ray.extensionFactor(pint))
|
|
192
|
+
pindex.append(0)
|
|
193
|
+
else:
|
|
194
|
+
normro = True
|
|
195
|
+
|
|
196
|
+
minPoint = p1
|
|
197
|
+
cross = v1.cross(v2)
|
|
198
|
+
check_contained = True
|
|
199
|
+
for i, _val in enumerate(self.__points[0: len(self.__points) - 2], 1):
|
|
200
|
+
p1 = self.__points[i]
|
|
201
|
+
p2 = self.__points[i + 1]
|
|
202
|
+
v2 = Vector2d(p2, p1)
|
|
203
|
+
v1 = Vector2d(p1, p)
|
|
204
|
+
if normro:
|
|
205
|
+
minLenght_i = v1.norm_roxroy(rox, roy)
|
|
206
|
+
if minLenght_i < minLenght:
|
|
207
|
+
minLenght = minLenght_i
|
|
208
|
+
minPoint = p1
|
|
209
|
+
elif rayFromCenter:
|
|
210
|
+
# if (p2 != p1):
|
|
211
|
+
seg = Seg2d(p2, p1)
|
|
212
|
+
ray = Seg2d(p, center)
|
|
213
|
+
hasIntersection, pint = seg.intersectAndContaints(ray)
|
|
214
|
+
if hasIntersection:
|
|
215
|
+
found = False
|
|
216
|
+
for ii in pintersect:
|
|
217
|
+
# Relative precision for excluding nearest points for
|
|
218
|
+
# some case precision issue
|
|
219
|
+
# We can not use i==pint
|
|
220
|
+
if ii.isEqualTo(pint, rox, roy, 1e-9):
|
|
221
|
+
found = True
|
|
222
|
+
if not found:
|
|
223
|
+
pintersect.append(pint)
|
|
224
|
+
pintersectFactors.append(ray.extensionFactor(pint))
|
|
225
|
+
pindex.append(i)
|
|
226
|
+
else:
|
|
227
|
+
minLenght_i = v1.norm()
|
|
228
|
+
if minLenght_i < minLenght:
|
|
229
|
+
minLenght = minLenght_i
|
|
230
|
+
minPoint = p1
|
|
231
|
+
|
|
232
|
+
cross_new = v1.cross(v2)
|
|
233
|
+
if cross * cross_new < 0 and check_contained:
|
|
234
|
+
contained = False
|
|
235
|
+
check_contained = False
|
|
236
|
+
cross = cross_new
|
|
237
|
+
|
|
238
|
+
pindexn = -1
|
|
239
|
+
d1 = 0
|
|
240
|
+
d2 = 0
|
|
241
|
+
d3 = 0
|
|
242
|
+
|
|
243
|
+
if normro:
|
|
244
|
+
warn("normro not tested !!!")
|
|
245
|
+
return contained, minLenght, minPoint, None
|
|
246
|
+
|
|
247
|
+
if len(pintersect) != 2:
|
|
248
|
+
print("WARNING: intersection are %1.2i not 2 !!!" % len(pintersect))
|
|
249
|
+
for p in pintersect:
|
|
250
|
+
print(p)
|
|
251
|
+
return contained, pintersect, pintersectFactor, [pindexn, d1, d2, d3]
|
|
252
|
+
|
|
253
|
+
if pintersectFactors[0] < 0:
|
|
254
|
+
pintersectFactor = pintersectFactors[1]
|
|
255
|
+
pindexn = pindex[1]
|
|
256
|
+
pleft = self.__points[pindex[1]]
|
|
257
|
+
pright = self.__points[pindex[1] + 1]
|
|
258
|
+
d1 = pintersect[1].distance(pleft)
|
|
259
|
+
d2 = pintersect[1].distance(pright)
|
|
260
|
+
d3 = pleft.distance(pright)
|
|
261
|
+
ptemp = copy.deepcopy(pintersect)
|
|
262
|
+
pintersect[0] = ptemp[1]
|
|
263
|
+
pintersect[1] = ptemp[0]
|
|
264
|
+
else:
|
|
265
|
+
pintersectFactor = pintersectFactors[0]
|
|
266
|
+
pindexn = pindex[0]
|
|
267
|
+
pleft = self.__points[pindex[0]]
|
|
268
|
+
pright = self.__points[pindex[0] + 1]
|
|
269
|
+
d1 = pintersect[0].distance(pleft)
|
|
270
|
+
d2 = pintersect[0].distance(pright)
|
|
271
|
+
d3 = pleft.distance(pright)
|
|
272
|
+
|
|
273
|
+
if pintersectFactor < 1.0:
|
|
274
|
+
contained = False
|
|
275
|
+
else:
|
|
276
|
+
contained = True
|
|
277
|
+
|
|
278
|
+
return contained, pintersect, pintersectFactor, [pindexn, d1, d2, d3]
|
|
279
|
+
|
|
280
|
+
def save(self, file: Path):
|
|
281
|
+
toSave = {
|
|
282
|
+
"points": self.__points,
|
|
283
|
+
"metaData": self.__metaData
|
|
284
|
+
}
|
|
285
|
+
with open(file, "bw") as f:
|
|
286
|
+
pickle.dump(toSave, f)
|
|
287
|
+
|
|
288
|
+
def open(self, file: Path):
|
|
289
|
+
with open(file, "br") as f:
|
|
290
|
+
toRead = pickle.load(f)
|
|
291
|
+
self.__points = toRead["points"]
|
|
292
|
+
self.__metaData = toRead["metaData"]
|
|
293
|
+
|
|
294
|
+
def model(self) -> PointCloud2dMdl:
|
|
295
|
+
points: List[Point2dMdl] = [Point2dMdl(x=i, y=i, null=True) for i in range(len(self.__points))]
|
|
296
|
+
for i, p in enumerate(points):
|
|
297
|
+
p.xy = (self.__points[i].x, self.__points[i].y)
|
|
298
|
+
return PointCloud2dMdl(points=points)
|