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.
Files changed (53) hide show
  1. pycivil-0.1.0/PKG-INFO +152 -0
  2. pycivil-0.1.0/README.md +121 -0
  3. pycivil-0.1.0/pycivil/EXAExceptions.py +8 -0
  4. pycivil-0.1.0/pycivil/EXAGeometry/__init__.py +0 -0
  5. pycivil-0.1.0/pycivil/EXAGeometry/clouds.py +298 -0
  6. pycivil-0.1.0/pycivil/EXAGeometry/geometry.py +742 -0
  7. pycivil-0.1.0/pycivil/EXAGeometry/shapes.py +489 -0
  8. pycivil-0.1.0/pycivil/EXAGeotechnical/base.py +437 -0
  9. pycivil-0.1.0/pycivil/EXAMicroServices/__init__.py +0 -0
  10. pycivil-0.1.0/pycivil/EXAMicroServices/dbutils.py +159 -0
  11. pycivil-0.1.0/pycivil/EXAMicroServices/fast-aster.py +149 -0
  12. pycivil-0.1.0/pycivil/EXAMicroServices/fast.py +708 -0
  13. pycivil-0.1.0/pycivil/EXAMicroServices/models.py +397 -0
  14. pycivil-0.1.0/pycivil/EXAMicroServices/server-settings.json +4 -0
  15. pycivil-0.1.0/pycivil/EXAMicroServices/settings.py +31 -0
  16. pycivil-0.1.0/pycivil/EXAMicroServices/srvRcSecCheck.py +1177 -0
  17. pycivil-0.1.0/pycivil/EXAParametric/box.py +518 -0
  18. pycivil-0.1.0/pycivil/EXAParametric/tube.py +665 -0
  19. pycivil-0.1.0/pycivil/EXAStructural/__init__.py +0 -0
  20. pycivil-0.1.0/pycivil/EXAStructural/checkable.py +107 -0
  21. pycivil-0.1.0/pycivil/EXAStructural/codeEC211.py +128 -0
  22. pycivil-0.1.0/pycivil/EXAStructural/codeEC212.py +640 -0
  23. pycivil-0.1.0/pycivil/EXAStructural/codeEC2Rules.py +272 -0
  24. pycivil-0.1.0/pycivil/EXAStructural/codeNTC2018.py +1883 -0
  25. pycivil-0.1.0/pycivil/EXAStructural/codeNTC2018Rules.py +239 -0
  26. pycivil-0.1.0/pycivil/EXAStructural/codes.py +226 -0
  27. pycivil-0.1.0/pycivil/EXAStructural/loads.py +129 -0
  28. pycivil-0.1.0/pycivil/EXAStructural/materials.py +1019 -0
  29. pycivil-0.1.0/pycivil/EXAStructural/modeler.py +541 -0
  30. pycivil-0.1.0/pycivil/EXAStructural/plot.py +1039 -0
  31. pycivil-0.1.0/pycivil/EXAStructural/sections.py +1514 -0
  32. pycivil-0.1.0/pycivil/EXAStructural/templateRCGen.py +1500 -0
  33. pycivil-0.1.0/pycivil/EXAStructural/templateRCRect.py +1625 -0
  34. pycivil-0.1.0/pycivil/EXAStructural/templateRCRectFire.py +846 -0
  35. pycivil-0.1.0/pycivil/EXAStructuralCheckable/RcsRectangular.py +954 -0
  36. pycivil-0.1.0/pycivil/EXAStructuralCheckable/__init__.py +0 -0
  37. pycivil-0.1.0/pycivil/EXAStructuralModel/fe.py +1316 -0
  38. pycivil-0.1.0/pycivil/EXAUtils/__init__.py +0 -0
  39. pycivil-0.1.0/pycivil/EXAUtils/binaries-runner-settings.json +6 -0
  40. pycivil-0.1.0/pycivil/EXAUtils/codeAster.py +1082 -0
  41. pycivil-0.1.0/pycivil/EXAUtils/dbtools.py +270 -0
  42. pycivil-0.1.0/pycivil/EXAUtils/gmsh.py +131 -0
  43. pycivil-0.1.0/pycivil/EXAUtils/latexCheatSheets.py +197 -0
  44. pycivil-0.1.0/pycivil/EXAUtils/latexReportMakers.py +433 -0
  45. pycivil-0.1.0/pycivil/EXAUtils/logging.py +47 -0
  46. pycivil-0.1.0/pycivil/EXAUtils/math.py +148 -0
  47. pycivil-0.1.0/pycivil/EXAUtils/report.py +312 -0
  48. pycivil-0.1.0/pycivil/EXAUtils/solver.py +119 -0
  49. pycivil-0.1.0/pycivil/EXAUtils/tables.py +126 -0
  50. pycivil-0.1.0/pycivil/EXAUtils/validator.py +61 -0
  51. pycivil-0.1.0/pycivil/EXAUtils/vtk.py +438 -0
  52. pycivil-0.1.0/pycivil/__init__.py +1 -0
  53. 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
+
@@ -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
+ ```
@@ -0,0 +1,8 @@
1
+ from typing import Any
2
+
3
+
4
+ class EXAExceptions(Exception):
5
+ def __init__(self, error: str, message: str, number: Any = None) -> None:
6
+ self.error = error
7
+ self.message = message
8
+ self.number = number
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)