GSOF-3dWireFrame 1.0.0__py3-none-any.whl
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.
- GSOF_3dWireFrame/Example_F16.py +141 -0
- GSOF_3dWireFrame/Example_dynamicWorld.py +99 -0
- GSOF_3dWireFrame/Example_motionWorld.py +75 -0
- GSOF_3dWireFrame/Example_staticWorld.py +112 -0
- GSOF_3dWireFrame/F16_Class.py +163 -0
- GSOF_3dWireFrame/Lib3D/Assembly.py +72 -0
- GSOF_3dWireFrame/Lib3D/Lib3D.py +145 -0
- GSOF_3dWireFrame/Lib3D/Object_WireFrame.py +66 -0
- GSOF_3dWireFrame/Lib3D/Object_base.py +122 -0
- GSOF_3dWireFrame/Lib3D/Objects.py +91 -0
- GSOF_3dWireFrame/Lib3D/Utils.py +9 -0
- GSOF_3dWireFrame/Lib3D/WireFrame_display.py +37 -0
- GSOF_3dWireFrame/Lib3D/__init__.py +7 -0
- GSOF_3dWireFrame/Lib3D/stlToObj.py +40 -0
- GSOF_3dWireFrame/MathLib/MathLib.py +460 -0
- GSOF_3dWireFrame/MathLib/__init__.py +7 -0
- GSOF_3dWireFrame/attitudeViewer.py +5 -0
- GSOF_3dWireFrame/modules/AttitudeViewer_class.py +115 -0
- GSOF_3dWireFrame/modules/Controls.py +60 -0
- GSOF_3dWireFrame/modules/__init__.py +7 -0
- GSOF_3dWireFrame/objects/Axis.json +40 -0
- GSOF_3dWireFrame/objects/F16.stl +0 -0
- GSOF_3dWireFrame/objects/LandingGear.json +28 -0
- GSOF_3dWireFrame/objects/Plume.json +34 -0
- GSOF_3dWireFrame/objects/Spark.json +15 -0
- GSOF_3dWireFrame/objects/cube.json +31 -0
- GSOF_3dWireFrame/objects/cube_flex.json +31 -0
- GSOF_3dWireFrame/objects/frame.json +16 -0
- GSOF_3dWireFrame/objects/house.json +50 -0
- GSOF_3dWireFrame/objects/net.json +3 -0
- GSOF_3dWireFrame/objects/plane.stl +0 -0
- GSOF_3dWireFrame/objects/pyramid.json +22 -0
- GSOF_3dWireFrame/stlToObject.py +32 -0
- gsof_3dwireframe-1.0.0.dist-info/METADATA +48 -0
- gsof_3dwireframe-1.0.0.dist-info/RECORD +39 -0
- gsof_3dwireframe-1.0.0.dist-info/WHEEL +5 -0
- gsof_3dwireframe-1.0.0.dist-info/licenses/AUTHORS +1 -0
- gsof_3dwireframe-1.0.0.dist-info/licenses/LICENSE +7 -0
- gsof_3dwireframe-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
"""
|
|
3
|
+
* Example_F16.py
|
|
4
|
+
* Created on: 31 May 2026
|
|
5
|
+
* Improved for: 31 May 2026
|
|
6
|
+
* Author: Guy Soffer
|
|
7
|
+
* Copyright (C) 2026 Guy Soffer
|
|
8
|
+
"""
|
|
9
|
+
import pygame, math
|
|
10
|
+
from Lib3D.Object_WireFrame import Object_wireFrame
|
|
11
|
+
from Lib3D.Assembly import Assembly
|
|
12
|
+
#from Lib3D.Object_base import Object_base
|
|
13
|
+
from Lib3D import Objects
|
|
14
|
+
from Lib3D import WireFrame_display as DISP
|
|
15
|
+
from F16_Class import F16_View, Commands, State
|
|
16
|
+
|
|
17
|
+
SCREEN_WIDTH = 800
|
|
18
|
+
SCREEN_HEIGHT = 600
|
|
19
|
+
WHITE = (255,255,255)
|
|
20
|
+
BLUE = (220,220,255)
|
|
21
|
+
BLACK = (0,0,0)
|
|
22
|
+
RED = (255,0,0)
|
|
23
|
+
GREEN = (0,255,0)
|
|
24
|
+
PI = math.pi
|
|
25
|
+
|
|
26
|
+
def drawWireFrame(screen, obj, color=None) -> None:
|
|
27
|
+
for line in obj.getLines():
|
|
28
|
+
x0, y0, z0 = line.p0
|
|
29
|
+
x1, y1, z1 = line.p1
|
|
30
|
+
lcolor = color
|
|
31
|
+
if lcolor == None:
|
|
32
|
+
lcolor = line.color
|
|
33
|
+
pygame.draw.line( screen, lcolor, (x0, y0), (x1, y1) ) #< Line from P0 to P1
|
|
34
|
+
|
|
35
|
+
def clearScreen(screen, color=(255,255,255)) -> None:
|
|
36
|
+
screen.fill(color)
|
|
37
|
+
|
|
38
|
+
def newScreen(title="New", resX=SCREEN_WIDTH, resY=SCREEN_HEIGHT, color=WHITE):
|
|
39
|
+
screenSize = (resX, resY)
|
|
40
|
+
screen = pygame.display.set_mode( screenSize )
|
|
41
|
+
clearScreen(screen, color)
|
|
42
|
+
pygame.display.set_caption(title)
|
|
43
|
+
return screen
|
|
44
|
+
|
|
45
|
+
if __name__ == "__main__":
|
|
46
|
+
import copy
|
|
47
|
+
ground = Object_wireFrame(
|
|
48
|
+
obj=Objects.net(25,25), color=(0,100,0))\
|
|
49
|
+
.scale(0.2)\
|
|
50
|
+
.rotate(x=math.pi/2, y=0, z=0)\
|
|
51
|
+
.translate(-250, -200, -250)\
|
|
52
|
+
.setOrigin()
|
|
53
|
+
|
|
54
|
+
f16 = F16_View()\
|
|
55
|
+
.scale(1.0)\
|
|
56
|
+
.translate(100, 100, -100).setOrigin()
|
|
57
|
+
f16.plane.color = BLACK
|
|
58
|
+
|
|
59
|
+
rotateBy = (0*3.14/4, 1*3.14/8, 0)
|
|
60
|
+
f16_1 = copy.deepcopy(f16)
|
|
61
|
+
f16_1.rotate(*rotateBy, centerAt=(100,100,-100))\
|
|
62
|
+
.setOrigin()
|
|
63
|
+
f16_1.plane.color = RED
|
|
64
|
+
|
|
65
|
+
rotateBy = (0*3.14/4, -1*3.14/8, 0)
|
|
66
|
+
f16_2 = copy.deepcopy(f16)
|
|
67
|
+
f16_2.translate(-100, -100, 100)\
|
|
68
|
+
.rotate(*rotateBy)\
|
|
69
|
+
.translate(100, 100, -100)\
|
|
70
|
+
.translate(-100, 0, 100)\
|
|
71
|
+
.setOrigin()
|
|
72
|
+
f16_2.plane.color = GREEN
|
|
73
|
+
|
|
74
|
+
world = Assembly(objects=(ground, f16, f16_1, f16_2))
|
|
75
|
+
|
|
76
|
+
pygame.init()
|
|
77
|
+
clock = pygame.time.Clock()
|
|
78
|
+
screen = newScreen("3D Wire Frame Shapes", SCREEN_WIDTH, SCREEN_HEIGHT, WHITE)
|
|
79
|
+
wireframe = DISP.WireFrame(screen, pygame.draw.line, f=50, scale=20.0)
|
|
80
|
+
|
|
81
|
+
fps = 30
|
|
82
|
+
dt = 1/fps
|
|
83
|
+
t = 0.0
|
|
84
|
+
camAngX_r = 0.0
|
|
85
|
+
camAngY_r = 0.0
|
|
86
|
+
camAngZ_r = 0.0
|
|
87
|
+
commands = Commands()
|
|
88
|
+
mPosZ = 0.0
|
|
89
|
+
state = State(thrust_lbf=6000)
|
|
90
|
+
useMouse = True
|
|
91
|
+
|
|
92
|
+
run = True
|
|
93
|
+
while run:
|
|
94
|
+
for event in pygame.event.get():
|
|
95
|
+
if event.type == pygame.QUIT:
|
|
96
|
+
run = False
|
|
97
|
+
### Wait for next step time
|
|
98
|
+
keys = pygame.key.get_pressed()
|
|
99
|
+
state.thrust_lbf += (keys[pygame.K_q] -keys[pygame.K_a])*200
|
|
100
|
+
commands.gearsDown_b += keys[pygame.K_b] -keys[pygame.K_g]
|
|
101
|
+
commands.gearsDown_b = min(1, max(0,commands.gearsDown_b))
|
|
102
|
+
state.wowNose_b = bool(keys[pygame.K_2])
|
|
103
|
+
state.wowLeft_b = bool(keys[pygame.K_1])
|
|
104
|
+
state.wowRight_b = bool(keys[pygame.K_3])
|
|
105
|
+
print(state.wowLeft_b, state.wowNose_b, state.wowRight_b)
|
|
106
|
+
|
|
107
|
+
if useMouse:
|
|
108
|
+
(mPosX, mPosY) = pygame.mouse.get_pos()
|
|
109
|
+
mPosZ += (keys[pygame.K_z] -keys[pygame.K_x])*2
|
|
110
|
+
x = mPosX/SCREEN_WIDTH -0.5
|
|
111
|
+
y = mPosY/SCREEN_HEIGHT -0.5
|
|
112
|
+
z = mPosZ/360
|
|
113
|
+
else:
|
|
114
|
+
x += 0.5*dt
|
|
115
|
+
y += 1*dt
|
|
116
|
+
z = 0
|
|
117
|
+
commands.rudder_d = z*30
|
|
118
|
+
commands.leftAliron_d = -25*x
|
|
119
|
+
commands.rightAliron_d = 25*x
|
|
120
|
+
commands.leftElevator = 0.5*commands.leftAliron_d +25*y
|
|
121
|
+
commands.rightElevator = 0.5*commands.rightAliron_d +25*y
|
|
122
|
+
camAngX_r = y*PI
|
|
123
|
+
camAngY_r = z*PI
|
|
124
|
+
camAngZ_r = x*PI
|
|
125
|
+
|
|
126
|
+
world.reset()
|
|
127
|
+
f16.setControls(t, commands, state)
|
|
128
|
+
world.transform(rotate=(camAngX_r,camAngY_r,camAngZ_r), translate=(0,0,-1000))
|
|
129
|
+
#world.transform(rotate=(3.14/2, 0.0*3.14, 0), translate=(0,0,-1000))
|
|
130
|
+
|
|
131
|
+
### Draw 3D world
|
|
132
|
+
clearScreen(screen, BLUE)
|
|
133
|
+
wireframe.draw(world)
|
|
134
|
+
|
|
135
|
+
t += dt
|
|
136
|
+
clock.tick(30)
|
|
137
|
+
|
|
138
|
+
### Display output
|
|
139
|
+
pygame.display.flip()
|
|
140
|
+
|
|
141
|
+
pygame.quit()
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import pygame, math
|
|
2
|
+
from Lib3D.Object_WireFrame import Object_wireFrame as Object
|
|
3
|
+
from Lib3D.Assembly import Assembly
|
|
4
|
+
from Lib3D import WireFrame_display as DISP
|
|
5
|
+
|
|
6
|
+
SCREEN_WIDTH = 800
|
|
7
|
+
SCREEN_HEIGHT = 600
|
|
8
|
+
WHITE = (255,255,255)
|
|
9
|
+
YELLOW = (240,240,150)
|
|
10
|
+
BLACK = (0,0,0)
|
|
11
|
+
PI = math.pi
|
|
12
|
+
|
|
13
|
+
def clearScreen(screen, color=(255,255,255)) -> None:
|
|
14
|
+
screen.fill(color)
|
|
15
|
+
|
|
16
|
+
def newScreen(title="New", resX=SCREEN_WIDTH, resY=SCREEN_HEIGHT, color=WHITE):
|
|
17
|
+
screenSize = (resX, resY)
|
|
18
|
+
screen = pygame.display.set_mode( screenSize )
|
|
19
|
+
clearScreen(screen, color)
|
|
20
|
+
pygame.display.set_caption(title)
|
|
21
|
+
return screen
|
|
22
|
+
|
|
23
|
+
if __name__ == "__main__":
|
|
24
|
+
import json
|
|
25
|
+
d = 4
|
|
26
|
+
solidHouse = Object(filename="./objects/house.json", color=(0,180,180), name="House").scale(10.0).setOrigin()
|
|
27
|
+
window = Object(filename="./objects/frame.json", color=(200,0,0), name="Window").setCenter(scale=0.8)
|
|
28
|
+
flexHouse = Assembly(objects = (
|
|
29
|
+
Object(filename="./objects/cube.json", color=(190,190,190), name="Body").translate(0,0,0 +d).setOrigin(),
|
|
30
|
+
Object(filename="./objects/pyramid.json", color=(90,90,90), name="Roof").translate(0,-2.5,0 +d).setOrigin(),
|
|
31
|
+
Assembly(objects=(window,), name="Window-Assy").translate(0,0,-1 +d).setOrigin(),
|
|
32
|
+
),
|
|
33
|
+
connections=[
|
|
34
|
+
[[0,4],[1,0]], #< Point-4 of object-0 to point-0 of object-1
|
|
35
|
+
[[0,5],[1,1]], #< Point-5 of object-0 to point-1 of object-1
|
|
36
|
+
[[0,6],[1,2]], #< Point-6 of object-0 to point-2 of object-1
|
|
37
|
+
[[0,7],[1,3]] #< Point-7 of object-0 to point-3 of object-1
|
|
38
|
+
],
|
|
39
|
+
name="Flex-House"
|
|
40
|
+
).scale(10).rotate(PI,0,0).setOrigin()
|
|
41
|
+
|
|
42
|
+
world = Assembly(objects = (
|
|
43
|
+
solidHouse.translate(0,0,0).setOrigin(),
|
|
44
|
+
flexHouse.translate(0,-0,0).setOrigin(),
|
|
45
|
+
))
|
|
46
|
+
pygame.init()
|
|
47
|
+
clock = pygame.time.Clock()
|
|
48
|
+
screen = newScreen("3D Wire Frame Shapes", SCREEN_WIDTH, SCREEN_HEIGHT, WHITE)
|
|
49
|
+
wireframe = DISP.WireFrame(screen, pygame.draw.line, f=50, scale=80)
|
|
50
|
+
fps = 30
|
|
51
|
+
dt = 1/fps
|
|
52
|
+
t = 0.0
|
|
53
|
+
camAngX_r = 0.0
|
|
54
|
+
camAngY_r = 0.0
|
|
55
|
+
roofAng_r = 0.0
|
|
56
|
+
houseAng_r = 0.0
|
|
57
|
+
useMouse = True
|
|
58
|
+
|
|
59
|
+
run = True
|
|
60
|
+
while run:
|
|
61
|
+
### Get inputs
|
|
62
|
+
for event in pygame.event.get():
|
|
63
|
+
if event.type == pygame.QUIT:
|
|
64
|
+
run = False
|
|
65
|
+
|
|
66
|
+
### Calculate next step state
|
|
67
|
+
camAngX_r += 0.5*dt
|
|
68
|
+
camAngY_r += 1*dt
|
|
69
|
+
roofAng_r = (PI/4/2)*math.sin(2*PI*1*t)
|
|
70
|
+
houseAng_r = (PI/4)*math.sin(2*PI*1.5*t)
|
|
71
|
+
t += dt
|
|
72
|
+
|
|
73
|
+
### Update 3D world
|
|
74
|
+
world.reset()
|
|
75
|
+
solidHouse.rotate(x=0,y=roofAng_r,z=houseAng_r)
|
|
76
|
+
roof = flexHouse.objects[1]
|
|
77
|
+
roof.rotate(x=0,y=roofAng_r,z=0)
|
|
78
|
+
window.rotate(x=0,y=0,z=10*t)
|
|
79
|
+
world.rotate(x=camAngX_r,y=camAngY_r,z=0)
|
|
80
|
+
world.translate(x=0,y=0,z=-600)
|
|
81
|
+
|
|
82
|
+
### Draw 3D world
|
|
83
|
+
clearScreen(screen, YELLOW)
|
|
84
|
+
wireframe.draw(world)
|
|
85
|
+
|
|
86
|
+
### Wait for next step time
|
|
87
|
+
if useMouse:
|
|
88
|
+
(mPosX, mPosY) = pygame.mouse.get_pos()
|
|
89
|
+
camAngY_r = 0.01*(mPosX -SCREEN_WIDTH/2)
|
|
90
|
+
camAngX_r = 0.01*(mPosY -SCREEN_HEIGHT/2)
|
|
91
|
+
else:
|
|
92
|
+
camAngX_r += 0.5*dt
|
|
93
|
+
camAngY_r += 1*dt
|
|
94
|
+
clock.tick(30)
|
|
95
|
+
|
|
96
|
+
### Display output
|
|
97
|
+
pygame.display.flip()
|
|
98
|
+
|
|
99
|
+
pygame.quit()
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import pygame
|
|
2
|
+
from Lib3D.Object_WireFrame import Object_wireFrame as Object
|
|
3
|
+
from Lib3D.Assembly import Assembly
|
|
4
|
+
from Lib3D import Objects
|
|
5
|
+
from Lib3D import WireFrame_display as DISP
|
|
6
|
+
from modules import Controls
|
|
7
|
+
|
|
8
|
+
SCREEN_WIDTH = 800
|
|
9
|
+
SCREEN_HEIGHT = 600
|
|
10
|
+
WHITE = (255,255,255)
|
|
11
|
+
BLACK = (0,0,0)
|
|
12
|
+
|
|
13
|
+
def clearScreen(screen, color=(255,255,255)) -> None:
|
|
14
|
+
screen.fill(color)
|
|
15
|
+
|
|
16
|
+
def newScreen(title="New", resX=SCREEN_WIDTH, resY=SCREEN_HEIGHT, color=WHITE):
|
|
17
|
+
screenSize = (resX, resY)
|
|
18
|
+
screen = pygame.display.set_mode( screenSize )
|
|
19
|
+
clearScreen(screen, color)
|
|
20
|
+
pygame.display.set_caption(title)
|
|
21
|
+
return screen
|
|
22
|
+
|
|
23
|
+
if __name__ == "__main__":
|
|
24
|
+
house = Object(filename="./objects/house.json", color=(0,180,180))
|
|
25
|
+
world = Assembly(objects = (
|
|
26
|
+
house,
|
|
27
|
+
))
|
|
28
|
+
|
|
29
|
+
pygame.init()
|
|
30
|
+
clock = pygame.time.Clock()
|
|
31
|
+
screen = newScreen("3D Wire Frame Shapes", SCREEN_WIDTH, SCREEN_HEIGHT, WHITE)
|
|
32
|
+
wireframe = DISP.WireFrame(screen, pygame.draw.line, f=50, scale=10)
|
|
33
|
+
viewer = Controls.Viewer( pos=(0,0,-400),
|
|
34
|
+
center=(int(screen.get_width()/2), int(screen.get_height()/2)),
|
|
35
|
+
moveLeftKey = pygame.K_a,
|
|
36
|
+
moveRightKey = pygame.K_d,
|
|
37
|
+
moveUpKey = pygame.K_UP,
|
|
38
|
+
moveDownKey = pygame.K_DOWN,
|
|
39
|
+
moveFwdKey = pygame.K_w,
|
|
40
|
+
moveBackKey = pygame.K_x,
|
|
41
|
+
tiltLeft = pygame.K_COMMA,
|
|
42
|
+
tiltRight = pygame.K_PERIOD
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
fps = 30
|
|
46
|
+
dt = 1/fps
|
|
47
|
+
t = 0.0
|
|
48
|
+
run = True
|
|
49
|
+
|
|
50
|
+
while run:
|
|
51
|
+
for event in pygame.event.get():
|
|
52
|
+
if event.type == pygame.QUIT:
|
|
53
|
+
run = False
|
|
54
|
+
|
|
55
|
+
keys = pygame.key.get_pressed()
|
|
56
|
+
(mPosX, mPosY) = pygame.mouse.get_pos()
|
|
57
|
+
wireframe.f += -(keys[pygame.K_1] - keys[pygame.K_2])*10
|
|
58
|
+
viewer.update(keys, mPosX, mPosY, speed=2)
|
|
59
|
+
|
|
60
|
+
clearScreen(screen, WHITE)
|
|
61
|
+
world.reset()
|
|
62
|
+
world.translate(*viewer.getPosition())
|
|
63
|
+
world.rotate(*viewer.getAttitude())
|
|
64
|
+
|
|
65
|
+
### Draw 3D world
|
|
66
|
+
clearScreen(screen, WHITE)
|
|
67
|
+
wireframe.draw(world)
|
|
68
|
+
|
|
69
|
+
### Wait for next step time
|
|
70
|
+
clock.tick(30)
|
|
71
|
+
|
|
72
|
+
### Display output
|
|
73
|
+
pygame.display.flip()
|
|
74
|
+
|
|
75
|
+
pygame.quit()
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import pygame, math
|
|
2
|
+
from Lib3D.Object_WireFrame import Object_wireFrame as Object
|
|
3
|
+
from Lib3D.Object_base import Object_base
|
|
4
|
+
from Lib3D.Assembly import Assembly
|
|
5
|
+
from Lib3D import WireFrame_display as DISP
|
|
6
|
+
from Lib3D import Objects
|
|
7
|
+
|
|
8
|
+
SCREEN_WIDTH = 800
|
|
9
|
+
SCREEN_HEIGHT = 600
|
|
10
|
+
WHITE = (255,255,255)
|
|
11
|
+
BLACK = (0,0,0)
|
|
12
|
+
PI = math.pi
|
|
13
|
+
|
|
14
|
+
def drawWireFrame(screen, obj, color=None) -> None:
|
|
15
|
+
for line in obj.getLines():
|
|
16
|
+
x0, y0, z0 = line.p0
|
|
17
|
+
x1, y1, z1 = line.p1
|
|
18
|
+
lcolor = color
|
|
19
|
+
if lcolor == None:
|
|
20
|
+
lcolor = line.color
|
|
21
|
+
pygame.draw.line( screen, lcolor, (x0, y0), (x1, y1) ) #< Line from P0 to P1
|
|
22
|
+
|
|
23
|
+
def clearScreen(screen, color=(255,255,255)) -> None:
|
|
24
|
+
screen.fill(color)
|
|
25
|
+
|
|
26
|
+
def newScreen(title="New", resX=SCREEN_WIDTH, resY=SCREEN_HEIGHT, color=WHITE):
|
|
27
|
+
screenSize = (resX, resY)
|
|
28
|
+
screen = pygame.display.set_mode( screenSize )
|
|
29
|
+
clearScreen(screen, color)
|
|
30
|
+
pygame.display.set_caption(title)
|
|
31
|
+
return screen
|
|
32
|
+
|
|
33
|
+
if __name__ == "__main__":
|
|
34
|
+
### 1. Build the ground
|
|
35
|
+
net = Object(obj=Objects.net(25,20), color=(0,100,0), name="NET")\
|
|
36
|
+
.setCenter(pos=(0,0,0), rotate=(PI/2, 0, 0), scale=0.2 )
|
|
37
|
+
axis1 = Object(filename="./objects/axis.json", color=(10,10,10), name="WorldAxis" )\
|
|
38
|
+
.scale(100.0)\
|
|
39
|
+
.setOrigin()
|
|
40
|
+
ground = Assembly(objects=(net, axis1), name="Ground").translate(-200, 0, 0).setOrigin()
|
|
41
|
+
|
|
42
|
+
### 2. Build the sun
|
|
43
|
+
sun = Object(obj=Objects.sphere(500, 15, color=(225,220,50)), name="SUN")
|
|
44
|
+
sun.setCenter(scale=0.15, rotate=(PI/2,0,0))
|
|
45
|
+
sun.translate(0, 250, 300) #< More up (Y) and forward (Z) to the center of the net
|
|
46
|
+
sun.setOrigin()
|
|
47
|
+
|
|
48
|
+
### 3. Build the plane
|
|
49
|
+
axis2 = Object(
|
|
50
|
+
filename="./objects/axis.json", color=(10,10,10 ))\
|
|
51
|
+
.scale(100.0)\
|
|
52
|
+
.setOrigin()
|
|
53
|
+
f16 = Object(filename="./objects/F16.stl", color=(0,0,255), name="F16")\
|
|
54
|
+
.setCenter(scale=1.0, rotate=(0,-PI/2,0), method="arithCenter")
|
|
55
|
+
plane = Assembly(objects=[f16, axis2], name="Plane")\
|
|
56
|
+
.rotate(0.5*-3.14/2,0,0)\
|
|
57
|
+
.translate(0,250,0)\
|
|
58
|
+
.setOrigin()
|
|
59
|
+
|
|
60
|
+
### 4. Build the world
|
|
61
|
+
world = Assembly(objects = (
|
|
62
|
+
ground,
|
|
63
|
+
plane,
|
|
64
|
+
sun,
|
|
65
|
+
), name="World")
|
|
66
|
+
|
|
67
|
+
pygame.init()
|
|
68
|
+
clock = pygame.time.Clock()
|
|
69
|
+
screen = newScreen("3D Wire Frame Shapes", SCREEN_WIDTH, SCREEN_HEIGHT, WHITE)
|
|
70
|
+
wireframe = DISP.WireFrame(screen, pygame.draw.line, f=50, scale=10.0)
|
|
71
|
+
|
|
72
|
+
fps = 30
|
|
73
|
+
dt = 1/fps
|
|
74
|
+
t = 0.0
|
|
75
|
+
camAngX_r = 0.0
|
|
76
|
+
camAngY_r = 0.0
|
|
77
|
+
f16Ang_r = 0.0
|
|
78
|
+
useMouse = True
|
|
79
|
+
|
|
80
|
+
run = True
|
|
81
|
+
while run:
|
|
82
|
+
for event in pygame.event.get():
|
|
83
|
+
if event.type == pygame.QUIT:
|
|
84
|
+
run = False
|
|
85
|
+
|
|
86
|
+
world.reset()
|
|
87
|
+
#world.rotate(x=1.8,y=3.14,z=0.3)
|
|
88
|
+
world.rotate(x=camAngX_r,y=camAngY_r,z=0)
|
|
89
|
+
#world.rotate(x=camAngX_r,y=camAngY_r,z=0)
|
|
90
|
+
f16.rotate(0, f16Ang_r, 0)
|
|
91
|
+
world.translate(x=0,y=0,z=-1000)
|
|
92
|
+
|
|
93
|
+
### Draw 3D world
|
|
94
|
+
clearScreen(screen, WHITE)
|
|
95
|
+
wireframe.draw(world)
|
|
96
|
+
|
|
97
|
+
### Wait for next step time
|
|
98
|
+
if useMouse:
|
|
99
|
+
(mPosX, mPosY) = pygame.mouse.get_pos()
|
|
100
|
+
camAngY_r = 0.01*(mPosX -SCREEN_WIDTH/2)
|
|
101
|
+
camAngX_r = 0.01*(mPosY -SCREEN_HEIGHT/2)
|
|
102
|
+
else:
|
|
103
|
+
camAngX_r += 0.5*dt
|
|
104
|
+
camAngY_r += 1*dt
|
|
105
|
+
f16Ang_r += 4*dt
|
|
106
|
+
t += dt
|
|
107
|
+
clock.tick(30)
|
|
108
|
+
|
|
109
|
+
### Display output
|
|
110
|
+
pygame.display.flip()
|
|
111
|
+
|
|
112
|
+
pygame.quit()
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
"""
|
|
3
|
+
* F16_Class.py
|
|
4
|
+
* Created on: 6 Jan 2025
|
|
5
|
+
* Improved for: 25 May 2026
|
|
6
|
+
* Author: Guy Soffer
|
|
7
|
+
* Copyright (C) 2026 Guy Soffer
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from math import pi
|
|
11
|
+
try:
|
|
12
|
+
from GSOF_3dWireFrame.Lib3D.Object_WireFrame import Object_wireFrame as Object
|
|
13
|
+
from GSOF_3dWireFrame.Lib3D.Object_base import Object_base
|
|
14
|
+
from GSOF_3dWireFrame.Lib3D.Assembly import Assembly
|
|
15
|
+
from GSOF_3dWireFrame.Lib3D import Objects
|
|
16
|
+
_3D_active = True
|
|
17
|
+
except:
|
|
18
|
+
_3D_active = False
|
|
19
|
+
print("GSOF_Wireframe3D module isn't installed")
|
|
20
|
+
|
|
21
|
+
degToRad = pi/180
|
|
22
|
+
#YELLOW = (255,255,140)
|
|
23
|
+
YELLOW = (125,125,0)
|
|
24
|
+
BLACK = (0,0,0)
|
|
25
|
+
RED = (255,0,0)
|
|
26
|
+
GREEN = (0,170,0)
|
|
27
|
+
BLUE = (0,0,255)
|
|
28
|
+
GRAY = (50,50,50)
|
|
29
|
+
|
|
30
|
+
class State():
|
|
31
|
+
def __init__(self,
|
|
32
|
+
azimuth_d=0,
|
|
33
|
+
pitch_d=0,
|
|
34
|
+
roll_d=0,
|
|
35
|
+
thrust_lbf=0,
|
|
36
|
+
wowNose_b=False,
|
|
37
|
+
wowLeft_b=False,
|
|
38
|
+
wowRight_b=False):
|
|
39
|
+
self.azimuth_d = azimuth_d
|
|
40
|
+
self.pitch_d = pitch_d
|
|
41
|
+
self.roll_d = roll_d
|
|
42
|
+
self.thrust_lbf = thrust_lbf
|
|
43
|
+
self.wowNose_b = wowNose_b
|
|
44
|
+
self.wowLeft_b = wowLeft_b
|
|
45
|
+
self.wowRight_b = wowRight_b
|
|
46
|
+
|
|
47
|
+
class Commands():
|
|
48
|
+
def __init__(self,
|
|
49
|
+
leftAliron_d=0, rightAliron_d=0,
|
|
50
|
+
leftElevator_d=0, rightElevator_d=0,
|
|
51
|
+
rudder_d=0, speedbrake_d=0,
|
|
52
|
+
throttle=0,
|
|
53
|
+
gearsDown_b=True):
|
|
54
|
+
self.leftAliron_d = leftAliron_d
|
|
55
|
+
self.rightAliron_d = rightAliron_d
|
|
56
|
+
self.leftElevator_d = leftElevator_d
|
|
57
|
+
self.rightElevator_d = rightElevator_d
|
|
58
|
+
self.rudder_d = rudder_d
|
|
59
|
+
self.speedbrake_d = speedbrake_d
|
|
60
|
+
self.gearsDown_b = gearsDown_b
|
|
61
|
+
|
|
62
|
+
class F16_View(Assembly):
|
|
63
|
+
"""Constructs the gauges screen"""
|
|
64
|
+
def __init__(self, folder='./'):
|
|
65
|
+
self.time = 0.0
|
|
66
|
+
axis = Object(
|
|
67
|
+
filename="%s/objects/axis.json"%folder, color=GREEN)\
|
|
68
|
+
.scale(50.0)\
|
|
69
|
+
.translate(0, 0, 150)\
|
|
70
|
+
.setOrigin()
|
|
71
|
+
|
|
72
|
+
self.plane = Object(
|
|
73
|
+
filename="%s/objects/f16.stl"%folder, color=BLUE, name="F16")\
|
|
74
|
+
.setCenter(scale=1.0, method="arithCenter")
|
|
75
|
+
|
|
76
|
+
self.plume = objects=Object(
|
|
77
|
+
filename="%s/objects/Plume.json"%folder, color=RED)\
|
|
78
|
+
.setCenter(scale=30, rotate=(180*degToRad, 0, 0))
|
|
79
|
+
plume = Assembly(objects=(self.plume,))
|
|
80
|
+
plume.translate(0, 0, -90).setOrigin()
|
|
81
|
+
|
|
82
|
+
self.nw = Object(
|
|
83
|
+
filename="%s/objects/LandingGear.json"%folder, color=BLACK, name="NW")
|
|
84
|
+
self.nwow = Object(
|
|
85
|
+
filename="%s/objects/Spark.json"%folder, color=YELLOW, name="NWOW")
|
|
86
|
+
nw = Assembly(objects=(self.nw, self.nwow), name="NW-Assy")\
|
|
87
|
+
.translate(0, -2, 0).scale(8).translate(0, -30, 80).setOrigin()
|
|
88
|
+
|
|
89
|
+
self.lw = Object(
|
|
90
|
+
filename="%s/objects/LandingGear.json"%folder, color=BLACK, name="LW")
|
|
91
|
+
self.lwow = Object(
|
|
92
|
+
filename="%s/objects/Spark.json"%folder, color=YELLOW)
|
|
93
|
+
lw = Assembly(objects=(self.lw, self.lwow), name="LW-Assy")\
|
|
94
|
+
.translate(0, -2, 0).rotate(x=0, y=0, z=degToRad*15)\
|
|
95
|
+
.scale(8).translate(18, -30, 0).setOrigin()
|
|
96
|
+
|
|
97
|
+
self.rw = Object(
|
|
98
|
+
filename="%s/objects/LandingGear.json"%folder, color=BLACK, name="RW")
|
|
99
|
+
self.rwow = Object(
|
|
100
|
+
filename="%s/objects/Spark.json"%folder, color=YELLOW)
|
|
101
|
+
rw = Assembly(objects=(self.rw, self.rwow), name="RW-Assy")\
|
|
102
|
+
.translate(0, -2, 0).rotate(x=0, y=0, z=-degToRad*15)\
|
|
103
|
+
.scale(8).translate(-18, -30, 0).setOrigin()
|
|
104
|
+
|
|
105
|
+
self.gears = Assembly(objects=(nw,
|
|
106
|
+
rw,
|
|
107
|
+
lw
|
|
108
|
+
))
|
|
109
|
+
super().__init__(objects=(axis, self.plane, plume, self.gears))
|
|
110
|
+
|
|
111
|
+
def setControls(self, time, commands=None, state=None):
|
|
112
|
+
"""Update all elements"""
|
|
113
|
+
self.time += 0.1
|
|
114
|
+
|
|
115
|
+
if state != None:
|
|
116
|
+
self.setWOW(state.wowNose_b, state.wowLeft_b, state.wowRight_b)
|
|
117
|
+
self.setExahustPlume(state.thrust_lbf)
|
|
118
|
+
heading = state.azimuth_d*degToRad
|
|
119
|
+
pitch = -state.pitch_d*degToRad
|
|
120
|
+
roll = state.roll_d*degToRad
|
|
121
|
+
self.setAttitude(heading, pitch, roll)
|
|
122
|
+
|
|
123
|
+
if commands != None:
|
|
124
|
+
self.setFCS(commands.leftAliron_d,
|
|
125
|
+
commands.rightAliron_d,
|
|
126
|
+
commands.leftElevator_d,
|
|
127
|
+
commands.rightElevator_d,
|
|
128
|
+
commands.rudder_d,
|
|
129
|
+
commands.speedbrake_d)
|
|
130
|
+
self.setGearsDown(commands.gearsDown_b)
|
|
131
|
+
|
|
132
|
+
def setAttitude(self, heading, pitch, roll) -> None:
|
|
133
|
+
self.rotate( y=-heading, x=-pitch, z=-roll )
|
|
134
|
+
|
|
135
|
+
def setExahustPlume(self, thrust_lbf) -> None:
|
|
136
|
+
thrustMAX = 6360
|
|
137
|
+
plume = thrust_lbf/thrustMAX
|
|
138
|
+
if plume > 1:
|
|
139
|
+
plume = 1
|
|
140
|
+
elif plume < 0:
|
|
141
|
+
plume = 0
|
|
142
|
+
plumeColor = (255, 255*(1-plume), 255*(1-plume))
|
|
143
|
+
self.plume.rotate(x=0, y=0, z=self.time*plume)
|
|
144
|
+
self.plume.scale(plume).color = plumeColor
|
|
145
|
+
|
|
146
|
+
def setFCS(self, lAliron_deg, rAliron_deg, lElevator, rElevator, rudder_deg, sb_deg) -> None:
|
|
147
|
+
return
|
|
148
|
+
|
|
149
|
+
def setGearsDown(self, downCmd) -> None:
|
|
150
|
+
if downCmd == False:
|
|
151
|
+
self.gears.scale(0.0)
|
|
152
|
+
else:
|
|
153
|
+
self.gears.scale(1.0)
|
|
154
|
+
|
|
155
|
+
def setWOW(self, nose, left, right) -> None:
|
|
156
|
+
time = int(self.time*10)
|
|
157
|
+
blink = bool(time&0b010)
|
|
158
|
+
self.nwow.rotate(x=0, y=0, z=4*self.time)\
|
|
159
|
+
.scale(int(nose and blink))
|
|
160
|
+
self.lwow.rotate(x=0, y=0, z=4*self.time)\
|
|
161
|
+
.scale(int(left and blink))
|
|
162
|
+
self.rwow.rotate(x=0, y=0, z=4*self.time)\
|
|
163
|
+
.scale(int(right and blink))
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
from GSOF_3dWireFrame.Lib3D import Lib3D as L
|
|
2
|
+
from GSOF_3dWireFrame.Lib3D.Object_base import Object_base
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Assembly(Object_base):
|
|
6
|
+
ASSY_NUM = 0
|
|
7
|
+
"""A new frame of reference with collection of other assemblies or objects"""
|
|
8
|
+
def __init__(self,
|
|
9
|
+
objects=[], #< Objects in assembly
|
|
10
|
+
connections=[], #< Connections between objects (for flexable objects)
|
|
11
|
+
name=None
|
|
12
|
+
):
|
|
13
|
+
self.objects = objects
|
|
14
|
+
self.connections = connections
|
|
15
|
+
if name == None:
|
|
16
|
+
name = "A" +str(Assembly.ASSY_NUM)
|
|
17
|
+
Assembly.ASSY_NUM += 1
|
|
18
|
+
self.name = name
|
|
19
|
+
super().__init__()
|
|
20
|
+
|
|
21
|
+
def reset(self, all=True):
|
|
22
|
+
"""Reset current state and all objects (optional)"""
|
|
23
|
+
super().reset()
|
|
24
|
+
if all:
|
|
25
|
+
for obj in self.objects:
|
|
26
|
+
obj.reset(all)
|
|
27
|
+
return self
|
|
28
|
+
|
|
29
|
+
def findCenter(self, method) -> list:
|
|
30
|
+
"""Return the center point of the assembly"""
|
|
31
|
+
points = self.getLines()
|
|
32
|
+
return super()._findCenter(points, method)
|
|
33
|
+
|
|
34
|
+
def getObjects(self) -> list:
|
|
35
|
+
"""Return a list of all objects"""
|
|
36
|
+
objects = []
|
|
37
|
+
for obj in self.objects:
|
|
38
|
+
objects += obj.getObjects()
|
|
39
|
+
return objects
|
|
40
|
+
|
|
41
|
+
def getLines(self):
|
|
42
|
+
"""Return lines of all objects. Will update transformations if needed"""
|
|
43
|
+
lines = []
|
|
44
|
+
if not self.isUpdated():
|
|
45
|
+
self.update()
|
|
46
|
+
|
|
47
|
+
### Lines from all objects
|
|
48
|
+
for obj in self.objects:
|
|
49
|
+
lines += obj.getLines()
|
|
50
|
+
|
|
51
|
+
### Lines between objects
|
|
52
|
+
#print("\n", self.name +":") if len(self.connections) > 0 else None
|
|
53
|
+
for between in self.connections:
|
|
54
|
+
[obj0, p0], [obj1, p1] = between
|
|
55
|
+
fromPnt = self.objects[obj0].newPoints[p0]
|
|
56
|
+
toPnt = self.objects[obj1].newPoints[p1]
|
|
57
|
+
lines += [L.Line(fromPnt, toPnt)]
|
|
58
|
+
## if False:
|
|
59
|
+
## print("CON: ", obj0, p0, obj1, p1)
|
|
60
|
+
## print(self.objects[obj0].name, self.objects[obj1].name)
|
|
61
|
+
## print(fromPnt, toPnt)
|
|
62
|
+
return lines
|
|
63
|
+
|
|
64
|
+
def update(self):
|
|
65
|
+
"""Update the coordinates of all assembly and object"""
|
|
66
|
+
#print("Update: %s"%self.name)
|
|
67
|
+
for obj in self.objects:
|
|
68
|
+
#print("%s.%s"%(self.name,obj.name))
|
|
69
|
+
if not self.isUpdated():
|
|
70
|
+
obj.transform(transMatrix=self.state)
|
|
71
|
+
obj.update()
|
|
72
|
+
self.stateTouched = False
|