craphics 0.0.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.
- craphics-0.0.1/LICENSE +21 -0
- craphics-0.0.1/PKG-INFO +340 -0
- craphics-0.0.1/README.md +321 -0
- craphics-0.0.1/craphics/__init__.py +1 -0
- craphics-0.0.1/craphics/craphics.py +202 -0
- craphics-0.0.1/craphics.egg-info/PKG-INFO +340 -0
- craphics-0.0.1/craphics.egg-info/SOURCES.txt +9 -0
- craphics-0.0.1/craphics.egg-info/dependency_links.txt +1 -0
- craphics-0.0.1/craphics.egg-info/top_level.txt +1 -0
- craphics-0.0.1/pyproject.toml +28 -0
- craphics-0.0.1/setup.cfg +4 -0
craphics-0.0.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Moinak Debnath
|
|
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.
|
craphics-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: craphics
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: A small Python Library for CPU - rendered graphics
|
|
5
|
+
Author: Moinak Debnath
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/findstring/craphics
|
|
8
|
+
Project-URL: Repository, https://github.com/findstring/craphics
|
|
9
|
+
Project-URL: Issues, https://github.com/findstring/craphics/issues
|
|
10
|
+
Keywords: graphics,cpu,rendering,tkinter,math
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Topic :: Multimedia :: Graphics
|
|
14
|
+
Classifier: Topic :: Multimedia :: Graphics :: 3D Modeling
|
|
15
|
+
Requires-Python: >=3.9
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
|
|
20
|
+
# Craphics
|
|
21
|
+
|
|
22
|
+
A small and lightweight Python 3D graphics module built using Python's standard libraries **`tkinter`**, **`math`**, and **`time`**.
|
|
23
|
+
|
|
24
|
+
Craphics provides basic 3D rendering with perspective projection, camera movement, mouse-controlled rotation, triangle rendering, and `.obj` model loading.
|
|
25
|
+
|
|
26
|
+
> **Version:** `v0.0.1`
|
|
27
|
+
|
|
28
|
+
## Features
|
|
29
|
+
|
|
30
|
+
* 🖥️ Tkinter-based rendering
|
|
31
|
+
* 📐 Basic 3D perspective projection
|
|
32
|
+
* 🎥 Movable camera
|
|
33
|
+
* 🖱️ Mouse-controlled camera rotation
|
|
34
|
+
* 🔺 Triangle-based object rendering
|
|
35
|
+
* 📦 Wavefront `.obj` file loading
|
|
36
|
+
* 👁️ Near and far rendering planes
|
|
37
|
+
* 🎨 Custom object fill and outline colors
|
|
38
|
+
* 📊 Optional rendering logs
|
|
39
|
+
* ⚙️ Configurable FPS and mouse sensitivity
|
|
40
|
+
* 📚 Uses only Python standard libraries
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
Install Craphics using `pip`:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pip install craphics
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Requirements
|
|
51
|
+
|
|
52
|
+
Craphics is built using Python's standard library:
|
|
53
|
+
|
|
54
|
+
* `tkinter`
|
|
55
|
+
* `math`
|
|
56
|
+
* `time`
|
|
57
|
+
|
|
58
|
+
No additional Python dependencies are required.
|
|
59
|
+
|
|
60
|
+
> **Note:** Your Python installation must have Tkinter available.
|
|
61
|
+
|
|
62
|
+
## Quick Start
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
import tkinter as tk
|
|
66
|
+
from craphics import CRAPHICS
|
|
67
|
+
|
|
68
|
+
window = tk.Tk()
|
|
69
|
+
window.title("Craphics Demo")
|
|
70
|
+
window.geometry("800x600")
|
|
71
|
+
|
|
72
|
+
graphics = CRAPHICS(
|
|
73
|
+
window=window,
|
|
74
|
+
BG="black",
|
|
75
|
+
FOCAL_LENGTH=500,
|
|
76
|
+
CAMERA_DISTANCE=5,
|
|
77
|
+
RENDER_DISTANCE=1000,
|
|
78
|
+
SENSITIVITY=0.005,
|
|
79
|
+
FPS=60,
|
|
80
|
+
LOG=True
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
window.mainloop()
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Controls
|
|
87
|
+
|
|
88
|
+
| Key / Input | Action |
|
|
89
|
+
| ------------ | -------------------- |
|
|
90
|
+
| `W` | Move camera forward |
|
|
91
|
+
| `S` | Move camera backward |
|
|
92
|
+
| `A` | Move camera left |
|
|
93
|
+
| `D` | Move camera right |
|
|
94
|
+
| `Space` | Move camera upward |
|
|
95
|
+
| `Left Shift` | Move camera downward |
|
|
96
|
+
| Mouse | Rotate camera |
|
|
97
|
+
|
|
98
|
+
The mouse cursor is hidden while using the Craphics canvas.
|
|
99
|
+
|
|
100
|
+
## API
|
|
101
|
+
|
|
102
|
+
### `CRAPHICS()`
|
|
103
|
+
|
|
104
|
+
Creates a Craphics renderer inside the supplied Tkinter window.
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
CRAPHICS(
|
|
108
|
+
window,
|
|
109
|
+
BG,
|
|
110
|
+
FOCAL_LENGTH,
|
|
111
|
+
CAMERA_DISTANCE,
|
|
112
|
+
RENDER_DISTANCE,
|
|
113
|
+
SENSITIVITY,
|
|
114
|
+
FPS,
|
|
115
|
+
LOG
|
|
116
|
+
)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Arguments
|
|
120
|
+
|
|
121
|
+
| Argument | Description |
|
|
122
|
+
| ----------------- | ----------------------------------------------------------------- |
|
|
123
|
+
| `window` | Tkinter parent window in which the rendering canvas is created |
|
|
124
|
+
| `BG` | Background color of the rendering canvas |
|
|
125
|
+
| `FOCAL_LENGTH` | Focal length used for perspective projection. Must not be `0` |
|
|
126
|
+
| `CAMERA_DISTANCE` | Initial distance of the camera along the Z axis. Must not be `0` |
|
|
127
|
+
| `RENDER_DISTANCE` | Maximum distance from the camera at which objects can be rendered |
|
|
128
|
+
| `SENSITIVITY` | Mouse rotation sensitivity |
|
|
129
|
+
| `FPS` | Target rendering frames per second. Must be greater than `0` |
|
|
130
|
+
| `LOG` | If `True`, prints rendering information to the console |
|
|
131
|
+
|
|
132
|
+
## Adding Objects
|
|
133
|
+
|
|
134
|
+
### `add_object()`
|
|
135
|
+
|
|
136
|
+
Adds a triangle-based 3D object to the scene.
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
graphics.add_object(
|
|
140
|
+
name,
|
|
141
|
+
fill,
|
|
142
|
+
outline,
|
|
143
|
+
polygons
|
|
144
|
+
)
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Arguments
|
|
148
|
+
|
|
149
|
+
| Argument | Description |
|
|
150
|
+
| ---------- | ----------------------------------------- |
|
|
151
|
+
| `name` | Unique name used to identify the object |
|
|
152
|
+
| `fill` | Fill color of the object's triangles |
|
|
153
|
+
| `outline` | Outline color of the object's triangles |
|
|
154
|
+
| `polygons` | List of triangles that make up the object |
|
|
155
|
+
|
|
156
|
+
Each triangle consists of three 3D vertices:
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
polygons = [
|
|
160
|
+
[
|
|
161
|
+
[x1, y1, z1],
|
|
162
|
+
[x2, y2, z2],
|
|
163
|
+
[x3, y3, z3]
|
|
164
|
+
]
|
|
165
|
+
]
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
For example:
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
triangle = [
|
|
172
|
+
[
|
|
173
|
+
[-1, -1, 0],
|
|
174
|
+
[1, -1, 0],
|
|
175
|
+
[0, 1, 0]
|
|
176
|
+
]
|
|
177
|
+
]
|
|
178
|
+
|
|
179
|
+
graphics.add_object(
|
|
180
|
+
"Triangle",
|
|
181
|
+
"red",
|
|
182
|
+
"white",
|
|
183
|
+
triangle
|
|
184
|
+
)
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Removing Objects
|
|
188
|
+
|
|
189
|
+
### `remove_object()`
|
|
190
|
+
|
|
191
|
+
Removes an object from the scene using its name.
|
|
192
|
+
|
|
193
|
+
```python
|
|
194
|
+
graphics.remove_object("Triangle")
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Loading `.obj` Models
|
|
198
|
+
|
|
199
|
+
### `add_objfile()`
|
|
200
|
+
|
|
201
|
+
Craphics can load geometry from a Wavefront `.obj` file.
|
|
202
|
+
|
|
203
|
+
```python
|
|
204
|
+
graphics.add_objfile(
|
|
205
|
+
name,
|
|
206
|
+
fill,
|
|
207
|
+
outline,
|
|
208
|
+
file
|
|
209
|
+
)
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Arguments
|
|
213
|
+
|
|
214
|
+
| Argument | Description |
|
|
215
|
+
| --------- | -------------------------------- |
|
|
216
|
+
| `name` | Name used to identify the object |
|
|
217
|
+
| `fill` | Fill color of the model |
|
|
218
|
+
| `outline` | Outline color of the model |
|
|
219
|
+
| `file` | Path to the `.obj` file |
|
|
220
|
+
|
|
221
|
+
Example:
|
|
222
|
+
|
|
223
|
+
```python
|
|
224
|
+
graphics.add_objfile(
|
|
225
|
+
"Cube",
|
|
226
|
+
"blue",
|
|
227
|
+
"white",
|
|
228
|
+
"cube.obj"
|
|
229
|
+
)
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### `.obj` Support
|
|
233
|
+
|
|
234
|
+
The current `.obj` loader supports:
|
|
235
|
+
|
|
236
|
+
* Vertex definitions (`v`)
|
|
237
|
+
* Triangle faces (`f`)
|
|
238
|
+
* Vertex/texture/normal face formats such as `f 1/1/1 2/2/2 3/3/3`
|
|
239
|
+
* Comments (`#`)
|
|
240
|
+
* Vertex normals (`vn`) are ignored
|
|
241
|
+
|
|
242
|
+
The current implementation expects faces to contain **three vertices**, meaning models should use triangular faces.
|
|
243
|
+
|
|
244
|
+
## Rendering
|
|
245
|
+
|
|
246
|
+
Craphics uses a basic perspective projection:
|
|
247
|
+
|
|
248
|
+
```text
|
|
249
|
+
screen_x = FOCAL_LENGTH × x / z
|
|
250
|
+
screen_y = FOCAL_LENGTH × y / z
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Objects are transformed according to the camera's:
|
|
254
|
+
|
|
255
|
+
* Position
|
|
256
|
+
* Yaw
|
|
257
|
+
* Pitch
|
|
258
|
+
|
|
259
|
+
Triangles outside the near and far clipping planes are skipped.
|
|
260
|
+
|
|
261
|
+
The renderer also performs basic back-face culling, so triangles facing away from the camera are not rendered.
|
|
262
|
+
|
|
263
|
+
## Camera
|
|
264
|
+
|
|
265
|
+
The camera starts at:
|
|
266
|
+
|
|
267
|
+
```python
|
|
268
|
+
CAMERA_X = 0
|
|
269
|
+
CAMERA_Y = 0
|
|
270
|
+
CAMERA_Z = CAMERA_DISTANCE
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Camera movement is controlled using the keyboard, while yaw and pitch are controlled by mouse movement.
|
|
274
|
+
|
|
275
|
+
### Camera Parameters
|
|
276
|
+
|
|
277
|
+
| Parameter | Description |
|
|
278
|
+
| ----------------- | -------------------------------------------------- |
|
|
279
|
+
| `CAMERA_DISTANCE` | Initial Z position of the camera |
|
|
280
|
+
| `SENSITIVITY` | Amount of camera rotation caused by mouse movement |
|
|
281
|
+
| `RENDER_DISTANCE` | Maximum rendering range |
|
|
282
|
+
|
|
283
|
+
## Rendering Logs
|
|
284
|
+
|
|
285
|
+
Set `LOG=True` to display rendering information:
|
|
286
|
+
|
|
287
|
+
```python
|
|
288
|
+
graphics = CRAPHICS(
|
|
289
|
+
window,
|
|
290
|
+
"black",
|
|
291
|
+
500,
|
|
292
|
+
5,
|
|
293
|
+
1000,
|
|
294
|
+
0.005,
|
|
295
|
+
60,
|
|
296
|
+
True
|
|
297
|
+
)
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
The console will display information such as:
|
|
301
|
+
|
|
302
|
+
```text
|
|
303
|
+
[CRAPHICS] Added Object : Cube
|
|
304
|
+
[CRAPHICS] Rendered 1 objects | Render time: 2.31 ms | Render FPS: 432.9 | Target FPS: 60
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
Set `LOG=False` to disable these messages.
|
|
308
|
+
|
|
309
|
+
## Limitations
|
|
310
|
+
|
|
311
|
+
Craphics is currently intended to be a **simple and experimental 3D renderer**, rather than a full 3D engine.
|
|
312
|
+
|
|
313
|
+
Current limitations include:
|
|
314
|
+
|
|
315
|
+
* Triangle-based rendering
|
|
316
|
+
* `.obj` faces should be triangles
|
|
317
|
+
* No textures
|
|
318
|
+
* No lighting system
|
|
319
|
+
* No materials
|
|
320
|
+
* No shadows
|
|
321
|
+
* No perspective-correct texture mapping
|
|
322
|
+
* Basic back-face culling
|
|
323
|
+
* CPU-based rendering through Tkinter
|
|
324
|
+
* Performance decreases with complex models
|
|
325
|
+
|
|
326
|
+
## Version
|
|
327
|
+
|
|
328
|
+
**Craphics v0.0.1**
|
|
329
|
+
|
|
330
|
+
This is an early version of the project and the API may change in future releases.
|
|
331
|
+
|
|
332
|
+
## License
|
|
333
|
+
|
|
334
|
+
**MIT License © 2026**
|
|
335
|
+
|
|
336
|
+
See the `LICENSE` file for the full license text.
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
Made with Python 🐍 and Tkinter.
|
craphics-0.0.1/README.md
ADDED
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
# Craphics
|
|
2
|
+
|
|
3
|
+
A small and lightweight Python 3D graphics module built using Python's standard libraries **`tkinter`**, **`math`**, and **`time`**.
|
|
4
|
+
|
|
5
|
+
Craphics provides basic 3D rendering with perspective projection, camera movement, mouse-controlled rotation, triangle rendering, and `.obj` model loading.
|
|
6
|
+
|
|
7
|
+
> **Version:** `v0.0.1`
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
* 🖥️ Tkinter-based rendering
|
|
12
|
+
* 📐 Basic 3D perspective projection
|
|
13
|
+
* 🎥 Movable camera
|
|
14
|
+
* 🖱️ Mouse-controlled camera rotation
|
|
15
|
+
* 🔺 Triangle-based object rendering
|
|
16
|
+
* 📦 Wavefront `.obj` file loading
|
|
17
|
+
* 👁️ Near and far rendering planes
|
|
18
|
+
* 🎨 Custom object fill and outline colors
|
|
19
|
+
* 📊 Optional rendering logs
|
|
20
|
+
* ⚙️ Configurable FPS and mouse sensitivity
|
|
21
|
+
* 📚 Uses only Python standard libraries
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
Install Craphics using `pip`:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install craphics
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Requirements
|
|
32
|
+
|
|
33
|
+
Craphics is built using Python's standard library:
|
|
34
|
+
|
|
35
|
+
* `tkinter`
|
|
36
|
+
* `math`
|
|
37
|
+
* `time`
|
|
38
|
+
|
|
39
|
+
No additional Python dependencies are required.
|
|
40
|
+
|
|
41
|
+
> **Note:** Your Python installation must have Tkinter available.
|
|
42
|
+
|
|
43
|
+
## Quick Start
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
import tkinter as tk
|
|
47
|
+
from craphics import CRAPHICS
|
|
48
|
+
|
|
49
|
+
window = tk.Tk()
|
|
50
|
+
window.title("Craphics Demo")
|
|
51
|
+
window.geometry("800x600")
|
|
52
|
+
|
|
53
|
+
graphics = CRAPHICS(
|
|
54
|
+
window=window,
|
|
55
|
+
BG="black",
|
|
56
|
+
FOCAL_LENGTH=500,
|
|
57
|
+
CAMERA_DISTANCE=5,
|
|
58
|
+
RENDER_DISTANCE=1000,
|
|
59
|
+
SENSITIVITY=0.005,
|
|
60
|
+
FPS=60,
|
|
61
|
+
LOG=True
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
window.mainloop()
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Controls
|
|
68
|
+
|
|
69
|
+
| Key / Input | Action |
|
|
70
|
+
| ------------ | -------------------- |
|
|
71
|
+
| `W` | Move camera forward |
|
|
72
|
+
| `S` | Move camera backward |
|
|
73
|
+
| `A` | Move camera left |
|
|
74
|
+
| `D` | Move camera right |
|
|
75
|
+
| `Space` | Move camera upward |
|
|
76
|
+
| `Left Shift` | Move camera downward |
|
|
77
|
+
| Mouse | Rotate camera |
|
|
78
|
+
|
|
79
|
+
The mouse cursor is hidden while using the Craphics canvas.
|
|
80
|
+
|
|
81
|
+
## API
|
|
82
|
+
|
|
83
|
+
### `CRAPHICS()`
|
|
84
|
+
|
|
85
|
+
Creates a Craphics renderer inside the supplied Tkinter window.
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
CRAPHICS(
|
|
89
|
+
window,
|
|
90
|
+
BG,
|
|
91
|
+
FOCAL_LENGTH,
|
|
92
|
+
CAMERA_DISTANCE,
|
|
93
|
+
RENDER_DISTANCE,
|
|
94
|
+
SENSITIVITY,
|
|
95
|
+
FPS,
|
|
96
|
+
LOG
|
|
97
|
+
)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Arguments
|
|
101
|
+
|
|
102
|
+
| Argument | Description |
|
|
103
|
+
| ----------------- | ----------------------------------------------------------------- |
|
|
104
|
+
| `window` | Tkinter parent window in which the rendering canvas is created |
|
|
105
|
+
| `BG` | Background color of the rendering canvas |
|
|
106
|
+
| `FOCAL_LENGTH` | Focal length used for perspective projection. Must not be `0` |
|
|
107
|
+
| `CAMERA_DISTANCE` | Initial distance of the camera along the Z axis. Must not be `0` |
|
|
108
|
+
| `RENDER_DISTANCE` | Maximum distance from the camera at which objects can be rendered |
|
|
109
|
+
| `SENSITIVITY` | Mouse rotation sensitivity |
|
|
110
|
+
| `FPS` | Target rendering frames per second. Must be greater than `0` |
|
|
111
|
+
| `LOG` | If `True`, prints rendering information to the console |
|
|
112
|
+
|
|
113
|
+
## Adding Objects
|
|
114
|
+
|
|
115
|
+
### `add_object()`
|
|
116
|
+
|
|
117
|
+
Adds a triangle-based 3D object to the scene.
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
graphics.add_object(
|
|
121
|
+
name,
|
|
122
|
+
fill,
|
|
123
|
+
outline,
|
|
124
|
+
polygons
|
|
125
|
+
)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Arguments
|
|
129
|
+
|
|
130
|
+
| Argument | Description |
|
|
131
|
+
| ---------- | ----------------------------------------- |
|
|
132
|
+
| `name` | Unique name used to identify the object |
|
|
133
|
+
| `fill` | Fill color of the object's triangles |
|
|
134
|
+
| `outline` | Outline color of the object's triangles |
|
|
135
|
+
| `polygons` | List of triangles that make up the object |
|
|
136
|
+
|
|
137
|
+
Each triangle consists of three 3D vertices:
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
polygons = [
|
|
141
|
+
[
|
|
142
|
+
[x1, y1, z1],
|
|
143
|
+
[x2, y2, z2],
|
|
144
|
+
[x3, y3, z3]
|
|
145
|
+
]
|
|
146
|
+
]
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
For example:
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
triangle = [
|
|
153
|
+
[
|
|
154
|
+
[-1, -1, 0],
|
|
155
|
+
[1, -1, 0],
|
|
156
|
+
[0, 1, 0]
|
|
157
|
+
]
|
|
158
|
+
]
|
|
159
|
+
|
|
160
|
+
graphics.add_object(
|
|
161
|
+
"Triangle",
|
|
162
|
+
"red",
|
|
163
|
+
"white",
|
|
164
|
+
triangle
|
|
165
|
+
)
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Removing Objects
|
|
169
|
+
|
|
170
|
+
### `remove_object()`
|
|
171
|
+
|
|
172
|
+
Removes an object from the scene using its name.
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
graphics.remove_object("Triangle")
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Loading `.obj` Models
|
|
179
|
+
|
|
180
|
+
### `add_objfile()`
|
|
181
|
+
|
|
182
|
+
Craphics can load geometry from a Wavefront `.obj` file.
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
graphics.add_objfile(
|
|
186
|
+
name,
|
|
187
|
+
fill,
|
|
188
|
+
outline,
|
|
189
|
+
file
|
|
190
|
+
)
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Arguments
|
|
194
|
+
|
|
195
|
+
| Argument | Description |
|
|
196
|
+
| --------- | -------------------------------- |
|
|
197
|
+
| `name` | Name used to identify the object |
|
|
198
|
+
| `fill` | Fill color of the model |
|
|
199
|
+
| `outline` | Outline color of the model |
|
|
200
|
+
| `file` | Path to the `.obj` file |
|
|
201
|
+
|
|
202
|
+
Example:
|
|
203
|
+
|
|
204
|
+
```python
|
|
205
|
+
graphics.add_objfile(
|
|
206
|
+
"Cube",
|
|
207
|
+
"blue",
|
|
208
|
+
"white",
|
|
209
|
+
"cube.obj"
|
|
210
|
+
)
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### `.obj` Support
|
|
214
|
+
|
|
215
|
+
The current `.obj` loader supports:
|
|
216
|
+
|
|
217
|
+
* Vertex definitions (`v`)
|
|
218
|
+
* Triangle faces (`f`)
|
|
219
|
+
* Vertex/texture/normal face formats such as `f 1/1/1 2/2/2 3/3/3`
|
|
220
|
+
* Comments (`#`)
|
|
221
|
+
* Vertex normals (`vn`) are ignored
|
|
222
|
+
|
|
223
|
+
The current implementation expects faces to contain **three vertices**, meaning models should use triangular faces.
|
|
224
|
+
|
|
225
|
+
## Rendering
|
|
226
|
+
|
|
227
|
+
Craphics uses a basic perspective projection:
|
|
228
|
+
|
|
229
|
+
```text
|
|
230
|
+
screen_x = FOCAL_LENGTH × x / z
|
|
231
|
+
screen_y = FOCAL_LENGTH × y / z
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Objects are transformed according to the camera's:
|
|
235
|
+
|
|
236
|
+
* Position
|
|
237
|
+
* Yaw
|
|
238
|
+
* Pitch
|
|
239
|
+
|
|
240
|
+
Triangles outside the near and far clipping planes are skipped.
|
|
241
|
+
|
|
242
|
+
The renderer also performs basic back-face culling, so triangles facing away from the camera are not rendered.
|
|
243
|
+
|
|
244
|
+
## Camera
|
|
245
|
+
|
|
246
|
+
The camera starts at:
|
|
247
|
+
|
|
248
|
+
```python
|
|
249
|
+
CAMERA_X = 0
|
|
250
|
+
CAMERA_Y = 0
|
|
251
|
+
CAMERA_Z = CAMERA_DISTANCE
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Camera movement is controlled using the keyboard, while yaw and pitch are controlled by mouse movement.
|
|
255
|
+
|
|
256
|
+
### Camera Parameters
|
|
257
|
+
|
|
258
|
+
| Parameter | Description |
|
|
259
|
+
| ----------------- | -------------------------------------------------- |
|
|
260
|
+
| `CAMERA_DISTANCE` | Initial Z position of the camera |
|
|
261
|
+
| `SENSITIVITY` | Amount of camera rotation caused by mouse movement |
|
|
262
|
+
| `RENDER_DISTANCE` | Maximum rendering range |
|
|
263
|
+
|
|
264
|
+
## Rendering Logs
|
|
265
|
+
|
|
266
|
+
Set `LOG=True` to display rendering information:
|
|
267
|
+
|
|
268
|
+
```python
|
|
269
|
+
graphics = CRAPHICS(
|
|
270
|
+
window,
|
|
271
|
+
"black",
|
|
272
|
+
500,
|
|
273
|
+
5,
|
|
274
|
+
1000,
|
|
275
|
+
0.005,
|
|
276
|
+
60,
|
|
277
|
+
True
|
|
278
|
+
)
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
The console will display information such as:
|
|
282
|
+
|
|
283
|
+
```text
|
|
284
|
+
[CRAPHICS] Added Object : Cube
|
|
285
|
+
[CRAPHICS] Rendered 1 objects | Render time: 2.31 ms | Render FPS: 432.9 | Target FPS: 60
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Set `LOG=False` to disable these messages.
|
|
289
|
+
|
|
290
|
+
## Limitations
|
|
291
|
+
|
|
292
|
+
Craphics is currently intended to be a **simple and experimental 3D renderer**, rather than a full 3D engine.
|
|
293
|
+
|
|
294
|
+
Current limitations include:
|
|
295
|
+
|
|
296
|
+
* Triangle-based rendering
|
|
297
|
+
* `.obj` faces should be triangles
|
|
298
|
+
* No textures
|
|
299
|
+
* No lighting system
|
|
300
|
+
* No materials
|
|
301
|
+
* No shadows
|
|
302
|
+
* No perspective-correct texture mapping
|
|
303
|
+
* Basic back-face culling
|
|
304
|
+
* CPU-based rendering through Tkinter
|
|
305
|
+
* Performance decreases with complex models
|
|
306
|
+
|
|
307
|
+
## Version
|
|
308
|
+
|
|
309
|
+
**Craphics v0.0.1**
|
|
310
|
+
|
|
311
|
+
This is an early version of the project and the API may change in future releases.
|
|
312
|
+
|
|
313
|
+
## License
|
|
314
|
+
|
|
315
|
+
**MIT License © 2026**
|
|
316
|
+
|
|
317
|
+
See the `LICENSE` file for the full license text.
|
|
318
|
+
|
|
319
|
+
---
|
|
320
|
+
|
|
321
|
+
Made with Python 🐍 and Tkinter.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .craphics import *
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import tkinter as tk
|
|
2
|
+
import math
|
|
3
|
+
import time
|
|
4
|
+
|
|
5
|
+
CRAPHICS_VERSION = "0.0.1"
|
|
6
|
+
|
|
7
|
+
class CRAPHICS:
|
|
8
|
+
def __init__(self, window, BG, FOCAL_LENGTH, CAMERA_DISTANCE, RENDER_DISTANCE, SENSITIVITY, FPS, LOG):
|
|
9
|
+
self.LOG = LOG
|
|
10
|
+
self.FPS = FPS
|
|
11
|
+
self.last_mouse_x = None
|
|
12
|
+
self.last_mouse_y = None
|
|
13
|
+
self.SENSITIVITY = SENSITIVITY
|
|
14
|
+
self.YAW = 0
|
|
15
|
+
self.PITCH = 0
|
|
16
|
+
self.NEAR_PLANE = 0.1
|
|
17
|
+
self.FAR_PLANE = self.NEAR_PLANE + RENDER_DISTANCE
|
|
18
|
+
self.RENDER_DISTANCE = RENDER_DISTANCE
|
|
19
|
+
self.CAMERA_X = 0
|
|
20
|
+
self.CAMERA_Y = 0
|
|
21
|
+
self.FOCAL_LENGTH = FOCAL_LENGTH
|
|
22
|
+
self.CAMERA_Z = CAMERA_DISTANCE
|
|
23
|
+
self.objects = {}
|
|
24
|
+
self.canvas = tk.Canvas(window, bg = BG)
|
|
25
|
+
self.canvas.pack(expand = True, fill = "both")
|
|
26
|
+
self.canvas.focus_set()
|
|
27
|
+
self.canvas.bind("<KeyPress-w>", lambda event: self._move_forward(0.1))
|
|
28
|
+
self.canvas.bind("<KeyPress-s>", lambda event: self._move_forward(-0.1))
|
|
29
|
+
self.canvas.bind("<KeyPress-a>", lambda event: self._move_right(-0.1))
|
|
30
|
+
self.canvas.bind("<KeyPress-d>", lambda event: self._move_right(0.1))
|
|
31
|
+
self.canvas.bind("<KeyPress-space>", lambda event: self._change_y(0.1))
|
|
32
|
+
self.canvas.bind("<KeyPress-Shift_L>", lambda event: self._change_y(-0.1))
|
|
33
|
+
self.canvas.bind("<Motion>", self._mouse_move)
|
|
34
|
+
self.canvas.config(cursor="none")
|
|
35
|
+
self._render()
|
|
36
|
+
|
|
37
|
+
def _mouse_move(self, event):
|
|
38
|
+
if self.last_mouse_x is None:
|
|
39
|
+
self.last_mouse_x = event.x
|
|
40
|
+
self.last_mouse_y = event.y
|
|
41
|
+
return
|
|
42
|
+
|
|
43
|
+
dx = event.x - self.last_mouse_x
|
|
44
|
+
dy = event.y - self.last_mouse_y
|
|
45
|
+
|
|
46
|
+
self.YAW += dx * self.SENSITIVITY
|
|
47
|
+
self.PITCH -= dy * self.SENSITIVITY
|
|
48
|
+
|
|
49
|
+
self.last_mouse_x = event.x
|
|
50
|
+
self.last_mouse_y = event.y
|
|
51
|
+
|
|
52
|
+
def _change_y(self, amount):
|
|
53
|
+
self.CAMERA_Y += amount
|
|
54
|
+
|
|
55
|
+
def _move_forward(self, amount):
|
|
56
|
+
self.CAMERA_X += math.sin(self.YAW) * amount
|
|
57
|
+
self.CAMERA_Z += math.cos(self.YAW) * amount
|
|
58
|
+
|
|
59
|
+
def _move_right(self, amount):
|
|
60
|
+
self.CAMERA_X += math.cos(self.YAW) * amount
|
|
61
|
+
self.CAMERA_Z -= math.sin(self.YAW) * amount
|
|
62
|
+
|
|
63
|
+
def add_object(self, name, fill, outline, polygons):
|
|
64
|
+
self.objects[name] = (name, fill, outline, polygons)
|
|
65
|
+
if self.LOG:
|
|
66
|
+
print("[CRAPHICS] Added Object :", name)
|
|
67
|
+
|
|
68
|
+
def remove_object(self, name):
|
|
69
|
+
if name in self.objects:
|
|
70
|
+
del self.objects[name]
|
|
71
|
+
if self.LOG:
|
|
72
|
+
print("[CRAPHICS] Removed Object :", name)
|
|
73
|
+
|
|
74
|
+
def _get_projection(self,x,y,z):
|
|
75
|
+
x -= self.CAMERA_X
|
|
76
|
+
y -= self.CAMERA_Y
|
|
77
|
+
z -= self.CAMERA_Z
|
|
78
|
+
|
|
79
|
+
x_new = x * math.cos(self.YAW) - z * math.sin(self.YAW)
|
|
80
|
+
z_new = x * math.sin(self.YAW) + z * math.cos(self.YAW)
|
|
81
|
+
|
|
82
|
+
y_rot = y * math.cos(self.PITCH) - z_new * math.sin(self.PITCH)
|
|
83
|
+
z_rot = y * math.sin(self.PITCH) + z_new * math.cos(self.PITCH)
|
|
84
|
+
|
|
85
|
+
y_new = y_rot
|
|
86
|
+
z_new = z_rot
|
|
87
|
+
|
|
88
|
+
if z_new <= self.NEAR_PLANE or z_new >= self.FAR_PLANE:
|
|
89
|
+
return None
|
|
90
|
+
|
|
91
|
+
x_new = float(self.FOCAL_LENGTH * x_new / z_new)
|
|
92
|
+
y_new = float(self.FOCAL_LENGTH * y_new / z_new)
|
|
93
|
+
return x_new, y_new
|
|
94
|
+
|
|
95
|
+
def _convert_coordinates(self,x,y):
|
|
96
|
+
x = x + self.canvas.winfo_width() / 2
|
|
97
|
+
y = -y + self.canvas.winfo_height() / 2
|
|
98
|
+
return x,y
|
|
99
|
+
|
|
100
|
+
def add_objfile(self, name, fill, outline, file):
|
|
101
|
+
with open(file, "r") as f:
|
|
102
|
+
content = f.readlines()
|
|
103
|
+
vertices = []
|
|
104
|
+
polygons = []
|
|
105
|
+
|
|
106
|
+
for line in content:
|
|
107
|
+
if line[0] == "#" or line[0] == "vn":
|
|
108
|
+
continue
|
|
109
|
+
elif line[0] == "v":
|
|
110
|
+
line = line.split()
|
|
111
|
+
vertices.append([float(line[1]),float(line[2]),float(line[3])])
|
|
112
|
+
elif line[0] == "f":
|
|
113
|
+
line.split()
|
|
114
|
+
point1 = int(line.split()[1].split("/")[0])
|
|
115
|
+
point2 = int(line.split()[2].split("/")[0])
|
|
116
|
+
point3 = int(line.split()[3].split("/")[0])
|
|
117
|
+
polygons.append([vertices[point1 - 1],vertices[point2 - 1],vertices[point3 - 1]])
|
|
118
|
+
|
|
119
|
+
self.add_object(name, fill, outline, polygons)
|
|
120
|
+
|
|
121
|
+
def _get_camera_z(self, x, y, z):
|
|
122
|
+
x -= self.CAMERA_X
|
|
123
|
+
y -= self.CAMERA_Y
|
|
124
|
+
z -= self.CAMERA_Z
|
|
125
|
+
|
|
126
|
+
x_new = x * math.cos(self.YAW) - z * math.sin(self.YAW)
|
|
127
|
+
z_new = x * math.sin(self.YAW) + z * math.cos(self.YAW)
|
|
128
|
+
|
|
129
|
+
y_rot = y * math.cos(self.PITCH) - z_new * math.sin(self.PITCH)
|
|
130
|
+
z_rot = y * math.sin(self.PITCH) + z_new * math.cos(self.PITCH)
|
|
131
|
+
|
|
132
|
+
return z_rot
|
|
133
|
+
|
|
134
|
+
def _render(self):
|
|
135
|
+
start = time.perf_counter()
|
|
136
|
+
|
|
137
|
+
self.canvas.update()
|
|
138
|
+
self.canvas.delete("all")
|
|
139
|
+
count = 0
|
|
140
|
+
for object in self.objects.values():
|
|
141
|
+
name = object[0]
|
|
142
|
+
fill = object[1]
|
|
143
|
+
outline = object[2]
|
|
144
|
+
polygons = object[3]
|
|
145
|
+
rendered = 0
|
|
146
|
+
|
|
147
|
+
polygons = sorted(polygons, key=lambda polygon: sum(self._get_camera_z(*vertex) for vertex in polygon) / len(polygon),reverse=True)
|
|
148
|
+
|
|
149
|
+
for polygon in polygons:
|
|
150
|
+
x1,y1,z1 = polygon[0]
|
|
151
|
+
x2,y2,z2 = polygon[1]
|
|
152
|
+
x3,y3,z3 = polygon[2]
|
|
153
|
+
|
|
154
|
+
ax = x2 - x1
|
|
155
|
+
ay = y2 - y1
|
|
156
|
+
az = z2 - z1
|
|
157
|
+
|
|
158
|
+
bx = x3 - x1
|
|
159
|
+
by = y3 - y1
|
|
160
|
+
bz = z3 - z1
|
|
161
|
+
|
|
162
|
+
nx = ay * bz - az * by
|
|
163
|
+
ny = az * bx - ax * bz
|
|
164
|
+
nz = ax * by - ay * bx
|
|
165
|
+
|
|
166
|
+
vx = self.CAMERA_X - x1
|
|
167
|
+
vy = self.CAMERA_Y - y1
|
|
168
|
+
vz = self.CAMERA_Z - z1
|
|
169
|
+
|
|
170
|
+
dot = nx * vx + ny * vy + nz * vz
|
|
171
|
+
|
|
172
|
+
if dot <= 0:
|
|
173
|
+
continue
|
|
174
|
+
|
|
175
|
+
value1 = self._get_projection(x1,y1,z1)
|
|
176
|
+
value2 = self._get_projection(x2,y2,z2)
|
|
177
|
+
value3 = self._get_projection(x3,y3,z3)
|
|
178
|
+
|
|
179
|
+
if not value1 or not value2 or not value3:
|
|
180
|
+
continue
|
|
181
|
+
|
|
182
|
+
x1,y1 = self._convert_coordinates(*value1)
|
|
183
|
+
x2,y2 = self._convert_coordinates(*value2)
|
|
184
|
+
x3,y3 = self._convert_coordinates(*value3)
|
|
185
|
+
|
|
186
|
+
rendered += 1
|
|
187
|
+
self.canvas.create_polygon(x1,y1,x2,y2,x3,y3, fill = fill, outline = outline)
|
|
188
|
+
|
|
189
|
+
if rendered > 0 :
|
|
190
|
+
count += 1
|
|
191
|
+
|
|
192
|
+
render_time = time.perf_counter() - start
|
|
193
|
+
|
|
194
|
+
if self.LOG:
|
|
195
|
+
print(
|
|
196
|
+
f"[CRAPHICS] Rendered {count} objects "
|
|
197
|
+
f"| Render time: {render_time * 1000:.2f} ms "
|
|
198
|
+
f"| Render FPS: {1 / render_time:.1f} "
|
|
199
|
+
f"| Target FPS: {self.FPS}"
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
self.canvas.after(1000 // self.FPS, self._render)
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: craphics
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: A small Python Library for CPU - rendered graphics
|
|
5
|
+
Author: Moinak Debnath
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/findstring/craphics
|
|
8
|
+
Project-URL: Repository, https://github.com/findstring/craphics
|
|
9
|
+
Project-URL: Issues, https://github.com/findstring/craphics/issues
|
|
10
|
+
Keywords: graphics,cpu,rendering,tkinter,math
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Topic :: Multimedia :: Graphics
|
|
14
|
+
Classifier: Topic :: Multimedia :: Graphics :: 3D Modeling
|
|
15
|
+
Requires-Python: >=3.9
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
|
|
20
|
+
# Craphics
|
|
21
|
+
|
|
22
|
+
A small and lightweight Python 3D graphics module built using Python's standard libraries **`tkinter`**, **`math`**, and **`time`**.
|
|
23
|
+
|
|
24
|
+
Craphics provides basic 3D rendering with perspective projection, camera movement, mouse-controlled rotation, triangle rendering, and `.obj` model loading.
|
|
25
|
+
|
|
26
|
+
> **Version:** `v0.0.1`
|
|
27
|
+
|
|
28
|
+
## Features
|
|
29
|
+
|
|
30
|
+
* 🖥️ Tkinter-based rendering
|
|
31
|
+
* 📐 Basic 3D perspective projection
|
|
32
|
+
* 🎥 Movable camera
|
|
33
|
+
* 🖱️ Mouse-controlled camera rotation
|
|
34
|
+
* 🔺 Triangle-based object rendering
|
|
35
|
+
* 📦 Wavefront `.obj` file loading
|
|
36
|
+
* 👁️ Near and far rendering planes
|
|
37
|
+
* 🎨 Custom object fill and outline colors
|
|
38
|
+
* 📊 Optional rendering logs
|
|
39
|
+
* ⚙️ Configurable FPS and mouse sensitivity
|
|
40
|
+
* 📚 Uses only Python standard libraries
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
Install Craphics using `pip`:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pip install craphics
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Requirements
|
|
51
|
+
|
|
52
|
+
Craphics is built using Python's standard library:
|
|
53
|
+
|
|
54
|
+
* `tkinter`
|
|
55
|
+
* `math`
|
|
56
|
+
* `time`
|
|
57
|
+
|
|
58
|
+
No additional Python dependencies are required.
|
|
59
|
+
|
|
60
|
+
> **Note:** Your Python installation must have Tkinter available.
|
|
61
|
+
|
|
62
|
+
## Quick Start
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
import tkinter as tk
|
|
66
|
+
from craphics import CRAPHICS
|
|
67
|
+
|
|
68
|
+
window = tk.Tk()
|
|
69
|
+
window.title("Craphics Demo")
|
|
70
|
+
window.geometry("800x600")
|
|
71
|
+
|
|
72
|
+
graphics = CRAPHICS(
|
|
73
|
+
window=window,
|
|
74
|
+
BG="black",
|
|
75
|
+
FOCAL_LENGTH=500,
|
|
76
|
+
CAMERA_DISTANCE=5,
|
|
77
|
+
RENDER_DISTANCE=1000,
|
|
78
|
+
SENSITIVITY=0.005,
|
|
79
|
+
FPS=60,
|
|
80
|
+
LOG=True
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
window.mainloop()
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Controls
|
|
87
|
+
|
|
88
|
+
| Key / Input | Action |
|
|
89
|
+
| ------------ | -------------------- |
|
|
90
|
+
| `W` | Move camera forward |
|
|
91
|
+
| `S` | Move camera backward |
|
|
92
|
+
| `A` | Move camera left |
|
|
93
|
+
| `D` | Move camera right |
|
|
94
|
+
| `Space` | Move camera upward |
|
|
95
|
+
| `Left Shift` | Move camera downward |
|
|
96
|
+
| Mouse | Rotate camera |
|
|
97
|
+
|
|
98
|
+
The mouse cursor is hidden while using the Craphics canvas.
|
|
99
|
+
|
|
100
|
+
## API
|
|
101
|
+
|
|
102
|
+
### `CRAPHICS()`
|
|
103
|
+
|
|
104
|
+
Creates a Craphics renderer inside the supplied Tkinter window.
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
CRAPHICS(
|
|
108
|
+
window,
|
|
109
|
+
BG,
|
|
110
|
+
FOCAL_LENGTH,
|
|
111
|
+
CAMERA_DISTANCE,
|
|
112
|
+
RENDER_DISTANCE,
|
|
113
|
+
SENSITIVITY,
|
|
114
|
+
FPS,
|
|
115
|
+
LOG
|
|
116
|
+
)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Arguments
|
|
120
|
+
|
|
121
|
+
| Argument | Description |
|
|
122
|
+
| ----------------- | ----------------------------------------------------------------- |
|
|
123
|
+
| `window` | Tkinter parent window in which the rendering canvas is created |
|
|
124
|
+
| `BG` | Background color of the rendering canvas |
|
|
125
|
+
| `FOCAL_LENGTH` | Focal length used for perspective projection. Must not be `0` |
|
|
126
|
+
| `CAMERA_DISTANCE` | Initial distance of the camera along the Z axis. Must not be `0` |
|
|
127
|
+
| `RENDER_DISTANCE` | Maximum distance from the camera at which objects can be rendered |
|
|
128
|
+
| `SENSITIVITY` | Mouse rotation sensitivity |
|
|
129
|
+
| `FPS` | Target rendering frames per second. Must be greater than `0` |
|
|
130
|
+
| `LOG` | If `True`, prints rendering information to the console |
|
|
131
|
+
|
|
132
|
+
## Adding Objects
|
|
133
|
+
|
|
134
|
+
### `add_object()`
|
|
135
|
+
|
|
136
|
+
Adds a triangle-based 3D object to the scene.
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
graphics.add_object(
|
|
140
|
+
name,
|
|
141
|
+
fill,
|
|
142
|
+
outline,
|
|
143
|
+
polygons
|
|
144
|
+
)
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Arguments
|
|
148
|
+
|
|
149
|
+
| Argument | Description |
|
|
150
|
+
| ---------- | ----------------------------------------- |
|
|
151
|
+
| `name` | Unique name used to identify the object |
|
|
152
|
+
| `fill` | Fill color of the object's triangles |
|
|
153
|
+
| `outline` | Outline color of the object's triangles |
|
|
154
|
+
| `polygons` | List of triangles that make up the object |
|
|
155
|
+
|
|
156
|
+
Each triangle consists of three 3D vertices:
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
polygons = [
|
|
160
|
+
[
|
|
161
|
+
[x1, y1, z1],
|
|
162
|
+
[x2, y2, z2],
|
|
163
|
+
[x3, y3, z3]
|
|
164
|
+
]
|
|
165
|
+
]
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
For example:
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
triangle = [
|
|
172
|
+
[
|
|
173
|
+
[-1, -1, 0],
|
|
174
|
+
[1, -1, 0],
|
|
175
|
+
[0, 1, 0]
|
|
176
|
+
]
|
|
177
|
+
]
|
|
178
|
+
|
|
179
|
+
graphics.add_object(
|
|
180
|
+
"Triangle",
|
|
181
|
+
"red",
|
|
182
|
+
"white",
|
|
183
|
+
triangle
|
|
184
|
+
)
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Removing Objects
|
|
188
|
+
|
|
189
|
+
### `remove_object()`
|
|
190
|
+
|
|
191
|
+
Removes an object from the scene using its name.
|
|
192
|
+
|
|
193
|
+
```python
|
|
194
|
+
graphics.remove_object("Triangle")
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Loading `.obj` Models
|
|
198
|
+
|
|
199
|
+
### `add_objfile()`
|
|
200
|
+
|
|
201
|
+
Craphics can load geometry from a Wavefront `.obj` file.
|
|
202
|
+
|
|
203
|
+
```python
|
|
204
|
+
graphics.add_objfile(
|
|
205
|
+
name,
|
|
206
|
+
fill,
|
|
207
|
+
outline,
|
|
208
|
+
file
|
|
209
|
+
)
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Arguments
|
|
213
|
+
|
|
214
|
+
| Argument | Description |
|
|
215
|
+
| --------- | -------------------------------- |
|
|
216
|
+
| `name` | Name used to identify the object |
|
|
217
|
+
| `fill` | Fill color of the model |
|
|
218
|
+
| `outline` | Outline color of the model |
|
|
219
|
+
| `file` | Path to the `.obj` file |
|
|
220
|
+
|
|
221
|
+
Example:
|
|
222
|
+
|
|
223
|
+
```python
|
|
224
|
+
graphics.add_objfile(
|
|
225
|
+
"Cube",
|
|
226
|
+
"blue",
|
|
227
|
+
"white",
|
|
228
|
+
"cube.obj"
|
|
229
|
+
)
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### `.obj` Support
|
|
233
|
+
|
|
234
|
+
The current `.obj` loader supports:
|
|
235
|
+
|
|
236
|
+
* Vertex definitions (`v`)
|
|
237
|
+
* Triangle faces (`f`)
|
|
238
|
+
* Vertex/texture/normal face formats such as `f 1/1/1 2/2/2 3/3/3`
|
|
239
|
+
* Comments (`#`)
|
|
240
|
+
* Vertex normals (`vn`) are ignored
|
|
241
|
+
|
|
242
|
+
The current implementation expects faces to contain **three vertices**, meaning models should use triangular faces.
|
|
243
|
+
|
|
244
|
+
## Rendering
|
|
245
|
+
|
|
246
|
+
Craphics uses a basic perspective projection:
|
|
247
|
+
|
|
248
|
+
```text
|
|
249
|
+
screen_x = FOCAL_LENGTH × x / z
|
|
250
|
+
screen_y = FOCAL_LENGTH × y / z
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Objects are transformed according to the camera's:
|
|
254
|
+
|
|
255
|
+
* Position
|
|
256
|
+
* Yaw
|
|
257
|
+
* Pitch
|
|
258
|
+
|
|
259
|
+
Triangles outside the near and far clipping planes are skipped.
|
|
260
|
+
|
|
261
|
+
The renderer also performs basic back-face culling, so triangles facing away from the camera are not rendered.
|
|
262
|
+
|
|
263
|
+
## Camera
|
|
264
|
+
|
|
265
|
+
The camera starts at:
|
|
266
|
+
|
|
267
|
+
```python
|
|
268
|
+
CAMERA_X = 0
|
|
269
|
+
CAMERA_Y = 0
|
|
270
|
+
CAMERA_Z = CAMERA_DISTANCE
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Camera movement is controlled using the keyboard, while yaw and pitch are controlled by mouse movement.
|
|
274
|
+
|
|
275
|
+
### Camera Parameters
|
|
276
|
+
|
|
277
|
+
| Parameter | Description |
|
|
278
|
+
| ----------------- | -------------------------------------------------- |
|
|
279
|
+
| `CAMERA_DISTANCE` | Initial Z position of the camera |
|
|
280
|
+
| `SENSITIVITY` | Amount of camera rotation caused by mouse movement |
|
|
281
|
+
| `RENDER_DISTANCE` | Maximum rendering range |
|
|
282
|
+
|
|
283
|
+
## Rendering Logs
|
|
284
|
+
|
|
285
|
+
Set `LOG=True` to display rendering information:
|
|
286
|
+
|
|
287
|
+
```python
|
|
288
|
+
graphics = CRAPHICS(
|
|
289
|
+
window,
|
|
290
|
+
"black",
|
|
291
|
+
500,
|
|
292
|
+
5,
|
|
293
|
+
1000,
|
|
294
|
+
0.005,
|
|
295
|
+
60,
|
|
296
|
+
True
|
|
297
|
+
)
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
The console will display information such as:
|
|
301
|
+
|
|
302
|
+
```text
|
|
303
|
+
[CRAPHICS] Added Object : Cube
|
|
304
|
+
[CRAPHICS] Rendered 1 objects | Render time: 2.31 ms | Render FPS: 432.9 | Target FPS: 60
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
Set `LOG=False` to disable these messages.
|
|
308
|
+
|
|
309
|
+
## Limitations
|
|
310
|
+
|
|
311
|
+
Craphics is currently intended to be a **simple and experimental 3D renderer**, rather than a full 3D engine.
|
|
312
|
+
|
|
313
|
+
Current limitations include:
|
|
314
|
+
|
|
315
|
+
* Triangle-based rendering
|
|
316
|
+
* `.obj` faces should be triangles
|
|
317
|
+
* No textures
|
|
318
|
+
* No lighting system
|
|
319
|
+
* No materials
|
|
320
|
+
* No shadows
|
|
321
|
+
* No perspective-correct texture mapping
|
|
322
|
+
* Basic back-face culling
|
|
323
|
+
* CPU-based rendering through Tkinter
|
|
324
|
+
* Performance decreases with complex models
|
|
325
|
+
|
|
326
|
+
## Version
|
|
327
|
+
|
|
328
|
+
**Craphics v0.0.1**
|
|
329
|
+
|
|
330
|
+
This is an early version of the project and the API may change in future releases.
|
|
331
|
+
|
|
332
|
+
## License
|
|
333
|
+
|
|
334
|
+
**MIT License © 2026**
|
|
335
|
+
|
|
336
|
+
See the `LICENSE` file for the full license text.
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
Made with Python 🐍 and Tkinter.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
craphics
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "craphics"
|
|
7
|
+
version = "0.0.1"
|
|
8
|
+
description = "A small Python Library for CPU - rendered graphics"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
license-files = ["LICENSE"]
|
|
12
|
+
requires-python = ">=3.9"
|
|
13
|
+
authors = [
|
|
14
|
+
{ name = "Moinak Debnath" }
|
|
15
|
+
]
|
|
16
|
+
keywords = ["graphics", "cpu", "rendering", "tkinter", "math"]
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Topic :: Multimedia :: Graphics",
|
|
21
|
+
"Topic :: Multimedia :: Graphics :: 3D Modeling",
|
|
22
|
+
]
|
|
23
|
+
dependencies = []
|
|
24
|
+
|
|
25
|
+
[project.urls]
|
|
26
|
+
Homepage = "https://github.com/findstring/craphics"
|
|
27
|
+
Repository = "https://github.com/findstring/craphics"
|
|
28
|
+
Issues = "https://github.com/findstring/craphics/issues"
|
craphics-0.0.1/setup.cfg
ADDED