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.
Files changed (39) hide show
  1. GSOF_3dWireFrame/Example_F16.py +141 -0
  2. GSOF_3dWireFrame/Example_dynamicWorld.py +99 -0
  3. GSOF_3dWireFrame/Example_motionWorld.py +75 -0
  4. GSOF_3dWireFrame/Example_staticWorld.py +112 -0
  5. GSOF_3dWireFrame/F16_Class.py +163 -0
  6. GSOF_3dWireFrame/Lib3D/Assembly.py +72 -0
  7. GSOF_3dWireFrame/Lib3D/Lib3D.py +145 -0
  8. GSOF_3dWireFrame/Lib3D/Object_WireFrame.py +66 -0
  9. GSOF_3dWireFrame/Lib3D/Object_base.py +122 -0
  10. GSOF_3dWireFrame/Lib3D/Objects.py +91 -0
  11. GSOF_3dWireFrame/Lib3D/Utils.py +9 -0
  12. GSOF_3dWireFrame/Lib3D/WireFrame_display.py +37 -0
  13. GSOF_3dWireFrame/Lib3D/__init__.py +7 -0
  14. GSOF_3dWireFrame/Lib3D/stlToObj.py +40 -0
  15. GSOF_3dWireFrame/MathLib/MathLib.py +460 -0
  16. GSOF_3dWireFrame/MathLib/__init__.py +7 -0
  17. GSOF_3dWireFrame/attitudeViewer.py +5 -0
  18. GSOF_3dWireFrame/modules/AttitudeViewer_class.py +115 -0
  19. GSOF_3dWireFrame/modules/Controls.py +60 -0
  20. GSOF_3dWireFrame/modules/__init__.py +7 -0
  21. GSOF_3dWireFrame/objects/Axis.json +40 -0
  22. GSOF_3dWireFrame/objects/F16.stl +0 -0
  23. GSOF_3dWireFrame/objects/LandingGear.json +28 -0
  24. GSOF_3dWireFrame/objects/Plume.json +34 -0
  25. GSOF_3dWireFrame/objects/Spark.json +15 -0
  26. GSOF_3dWireFrame/objects/cube.json +31 -0
  27. GSOF_3dWireFrame/objects/cube_flex.json +31 -0
  28. GSOF_3dWireFrame/objects/frame.json +16 -0
  29. GSOF_3dWireFrame/objects/house.json +50 -0
  30. GSOF_3dWireFrame/objects/net.json +3 -0
  31. GSOF_3dWireFrame/objects/plane.stl +0 -0
  32. GSOF_3dWireFrame/objects/pyramid.json +22 -0
  33. GSOF_3dWireFrame/stlToObject.py +32 -0
  34. gsof_3dwireframe-1.0.0.dist-info/METADATA +48 -0
  35. gsof_3dwireframe-1.0.0.dist-info/RECORD +39 -0
  36. gsof_3dwireframe-1.0.0.dist-info/WHEEL +5 -0
  37. gsof_3dwireframe-1.0.0.dist-info/licenses/AUTHORS +1 -0
  38. gsof_3dwireframe-1.0.0.dist-info/licenses/LICENSE +7 -0
  39. gsof_3dwireframe-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,145 @@
1
+ from GSOF_3dWireFrame.MathLib import MathLib as ML
2
+ import json
3
+ from GSOF_3dWireFrame.Lib3D import stlToObj
4
+
5
+ def loadStl(filename, faceCount=500):
6
+ return stlToObj.stlToObj(filename, faceCount=faceCount)
7
+
8
+ def loadJson(filename):
9
+ obj = None
10
+ with open(filename) as f:
11
+ obj = json.load(f)
12
+ return obj
13
+
14
+ def dataToDict(points, lines, scale=1.0, color=(0,0,0)):
15
+ return({"scale":scale,
16
+ "color":color,
17
+ "points_xyz": points,
18
+ "connections": lines})
19
+
20
+ def findArithmeticCenter(points):
21
+ """ Return the arithmetic center from the list of points """
22
+ mean = [0]*(len(points[0]))
23
+
24
+ for axis in points:
25
+ for i in range(len(axis)):
26
+ mean[i]+=axis[i]
27
+ for i in range(len(mean)):
28
+ mean[i] = mean[i]/len(points)
29
+ return mean
30
+
31
+ def findMinMaxCenter(points):
32
+ """ Return the center point between the minimum and maximum
33
+ values in the list of points """
34
+ Max = 999999
35
+ Min = -Max
36
+ N = len(points[0])
37
+ minPoint = [Max]*N
38
+ maxPoint = [Min]*N
39
+ for point in points:
40
+ for i, v in enumerate(point):
41
+ if v < minPoint[i]:
42
+ minPoint[i] = v
43
+ if v > maxPoint[i]:
44
+ maxPoint[i] = v
45
+
46
+ meanPoint = [0,0,0]
47
+ for i in range(N):
48
+ meanPoint[i] = (minPoint[i]+maxPoint[i])/2
49
+ return meanPoint
50
+
51
+ def scalePoint(point, scale, dim) -> list:
52
+ new = [0]*dim
53
+ for i in range(0, dim):
54
+ new[i] = point[i]*scale[i]
55
+ return new
56
+
57
+ def scale(points, _scale) -> list:
58
+ """Scale coordinates"""
59
+ dim = len(points[0])
60
+ if not isinstance(_scale, (tuple,list)):
61
+ _scale = (_scale,)*dim
62
+ newPoints = [None]*len(points)
63
+ for i, point in enumerate(points):
64
+ newPoints[i] = scalePoint(point, _scale, dim)
65
+ return newPoints
66
+
67
+ def getRotationMatrix(x=0, y=0, z=0) -> list:
68
+ """Return the proper rotation matrix for our coordinate system"""
69
+ return ML.DCM_YXZ(y, x, z) #< the proper rotation order for our coordinate system
70
+
71
+ def getTransformMatrix(scale=(1,1,1), rotate=(0,0,0), translate=(0,0,0)) -> list:
72
+ """Return the transformation matrix"""
73
+ M = getRotationMatrix(*rotate)
74
+ scaleM = ML.I(3)
75
+ scaleM[0][0] = scale[0]
76
+ scaleM[1][1] = scale[1]
77
+ scaleM[2][2] = scale[2]
78
+ M = ML.MxM(M, scaleM)
79
+ M[0] += [translate[0]] #< Add translation
80
+ M[1] += [translate[1]]
81
+ M[2] += [translate[2]]
82
+ return M +[[0,0,0,1]] #< Add last row and return
83
+
84
+ def updateTransformationMatrix(oldT, newT) -> list:
85
+ return ML.MxM(newT, oldT)
86
+
87
+ def rotate(points, x=0, y=0, z=0, dcm=None) -> list:
88
+ newPoints = [None]*len(points)
89
+ if dcm == None:
90
+ dcm = getRotationMatrix(x, y, z)
91
+
92
+ for i, point in enumerate(points):
93
+ newPoints[i] = ML.MxV(dcm, point)
94
+ return newPoints
95
+
96
+ def translate(points, x=0, y=0, z=0) -> list:
97
+ newPoints = [None]*len(points)
98
+ T = [x, y, z]
99
+ for i, point in enumerate(points):
100
+ newPoints[i] = ML.addV(point, T)
101
+ return newPoints
102
+
103
+ def transform(points, _scale=(1,1,1), _rotate=(0,0,0), _translate=(0,0,0), M=None) -> list:
104
+ if M == None:
105
+ M = getTransformMatrix(_scale, _rotate, _translate)
106
+ return _update(points, M)
107
+ ## points = scale(points, _scale) #< Inefficient method
108
+ ## points = rotate(points, *_rotate)
109
+ ## return translate(points, *_translate)
110
+
111
+ def _update(points, transformation) -> list:
112
+ newPoints = [None]*len(points)
113
+ for i, point in enumerate(points):
114
+ if len(point) < 4:
115
+ point += [1] #< Add translation dimension
116
+ newPoints[i] = (ML.MxV(transformation, point))[0:3]
117
+ return newPoints
118
+
119
+ def calcLines(points, connections, color=(0,0,0)) -> list:
120
+ lines = [None]*len(connections)
121
+ for i, connect in enumerate(connections):
122
+ fromPnt, toPnt = connect
123
+ p0 = points[fromPnt]
124
+ p1 = points[toPnt]
125
+ lines[i] = Line(p0, p1, color)
126
+ return lines
127
+
128
+ class Line():
129
+ def __init__(self, p0=None, p1=None, color=(0,0,0)):
130
+ self.points = [p0, p1]
131
+ self.color = color
132
+ ### THE METHODS BELOW ARE NOT USED
133
+ ## def scale(self, scale):
134
+ ## self.points = scale(self.points, scale)
135
+ ##
136
+ ## def translate(self, x=0, y=0, z=0):
137
+ ## self.points = translate(self.points, x,y,z)
138
+ ##
139
+ ## def rotate(self, x=0, y=0, z=0, dcm=None):
140
+ ## self.points = rotate(self.points, x,y,z,dcm)
141
+
142
+ class Triangle(Line):
143
+ def __init__(self, p0=None, p1=None, p2=None, color=(0,0,0)):
144
+ super().__init__(p0, p1, color)
145
+ self.points += [p2]
@@ -0,0 +1,66 @@
1
+ from GSOF_3dWireFrame.Lib3D import Lib3D as L
2
+ from GSOF_3dWireFrame.Lib3D.Object_base import *
3
+
4
+ class Object_wireFrame(Object_base):
5
+ OBJ_NUM = 0
6
+ def __init__(self, obj=None, filename=None, color=None, name=None):
7
+ if name == None:
8
+ name = "O" +str(Object_wireFrame.OBJ_NUM)
9
+ Object_wireFrame.OBJ_NUM += 1
10
+ self.name = name
11
+ super().__init__()
12
+ if filename != None:
13
+ ext = filename.split(".")[-1]
14
+ if ext == "json":
15
+ obj = L.loadJson(filename)
16
+ elif ext == "stl":
17
+ obj = L.loadStl(filename)
18
+
19
+ if color == None:
20
+ if "color" in obj:
21
+ color = obj["color"] #< Color from file
22
+ else:
23
+ color = (0,0,0) #< Default color black
24
+ self.color = color #< Color to draw all lines
25
+ self.points = obj["points_xyz"] #< xyz-coordinates of all corner-points of object
26
+ self.connections = obj["connections"] #< Lines between corner points of object
27
+
28
+ def update(self) -> None:
29
+ self.newPoints = L.transform(points=self.points, M=self.state)
30
+ self.stateTouched = True
31
+
32
+ def getLines(self) -> list:
33
+ if not self.isUpdated():
34
+ self.update()
35
+ return L.calcLines(self.newPoints, self.connections, self.color)
36
+
37
+ def setCenter(self,
38
+ pos=(0,0,0),
39
+ rotate=(0,0,0),
40
+ scale=(1,1,1),
41
+ method=None
42
+ ):
43
+ """Move all original points to the new location than rotate and scale"""
44
+ ### Find new center and translate all points
45
+ center = super()._findCenter(self.points, method)
46
+ translate = (-(pos[0]+center[0]), -(pos[1]+center[1]), -(pos[2]+center[2]))
47
+ if True:
48
+ self.points = L.translate(self.points, *translate)
49
+ self.points = L.rotate(self.points, *rotate)
50
+ self.points = L.scale(self.points, scale)
51
+ else:
52
+ transMatrix = L.getTransformMatrix(scale=(1,1,1),
53
+ rotate=(0,0,0),
54
+ translate=translate)
55
+ self.points = L.transform(points=self.points, M=transMatrix)
56
+
57
+ ### Rotate all points
58
+ if not isinstance(scale, (list, tuple)):
59
+ scale = (scale,)*3
60
+ transMatrix = L.getTransformMatrix(scale,
61
+ rotate,
62
+ translate=(0,0,0))
63
+ self.points = L.transform(points=self.points, M=transMatrix)
64
+
65
+ self.stateTouched = True
66
+ return self
@@ -0,0 +1,122 @@
1
+ import copy
2
+ from GSOF_3dWireFrame.MathLib import MathLib as ML
3
+ from GSOF_3dWireFrame.Lib3D import Lib3D as L
4
+
5
+ class Object_base():
6
+ def __init__(self):
7
+ self.stateOrigin = ML.I(4) #< 4x4 matrix to store the original state of scale, rotation and translations
8
+ self.reset() #< 4x4 matrix to store the correct state
9
+
10
+ def reset(self, all=True):
11
+ """Reset current state to original"""
12
+ self.state = self.getOrigin() #< 4x4 matrix to store the correct state
13
+ self.stateTouched = True
14
+ return self
15
+
16
+ def setOrigin(self, newState=None):
17
+ """Set current state as the new original state"""
18
+ if newState == None:
19
+ newState = self.state
20
+ self.stateOrigin = copy.deepcopy(newState)
21
+ return self
22
+
23
+ def getOrigin(self) -> list:
24
+ """Get the original state"""
25
+ return copy.deepcopy(self.stateOrigin)
26
+
27
+ def _findCenter(self,
28
+ points: list|tuple,
29
+ method: str="arithCenter"
30
+ ) -> list:
31
+ """Return the center point of all points"""
32
+ if method == "arithCenter":
33
+ return L.findArithmeticCenter(points)
34
+
35
+ elif method == "minMaxCenter":
36
+ return L.findMinMaxCenter(points)
37
+
38
+ elif method == None:
39
+ return (0,0,0)
40
+
41
+ else:
42
+ return None
43
+
44
+ def scale(self, scale: list|tuple|float):
45
+ """Apply scaling to current state"""
46
+ """Apply scaling to current state"""
47
+ if not isinstance(scale, (list, tuple)):
48
+ scale = (scale,)*3
49
+ scaleM = ML.I(4)
50
+ scaleM[0][0] = scale[0]
51
+ scaleM[1][1] = scale[1]
52
+ scaleM[2][2] = scale[2]
53
+ self.state = ML.MxM(self.state, scaleM)
54
+ self.stateTouched = True
55
+ return self
56
+
57
+ def copyDcmIntoState(self, dcm: list) -> None:
58
+ #self.state = ML.copyIntoMatrix(self.state, dcm, rs=0, cs=0)
59
+ for ri, row in enumerate(dcm):
60
+ for ci, val in enumerate(row):
61
+ self.state[ri][ci] = val
62
+
63
+ def rotate(self, x: float, y: float, z: float, dcm: list=None, centerAt: list=None):
64
+ """Apply rotation to current state"""
65
+ if dcm == None:
66
+ dcm = L.getRotationMatrix(x, y, z)
67
+ if centerAt == None:
68
+ #self.copyDcmIntoState(ML.MxM(self.state[0:3], dcm))
69
+ dcm[0] += [0]
70
+ dcm[1] += [0]
71
+ dcm[2] += [0]
72
+ else:
73
+ tx, ty, tz = centerAt[0],centerAt[1],centerAt[2]
74
+ r = dcm
75
+ dcm[0] += [tx*(1-r[0][0]) -r[0][1]*ty -r[0][2]*tz]
76
+ dcm[1] += [ty*(1-r[1][1]) -r[1][0]*tx -r[1][2]*tz]
77
+ dcm[2] += [tz*(1-r[2][2]) -r[2][0]*tx -r[2][1]*ty]
78
+ dcm += [[0,0,0,1]]
79
+ self.state = ML.MxM(dcm, self.state)
80
+ self.stateTouched = True
81
+ return self
82
+
83
+ def translate(self, x, y, z):
84
+ """Apply translation to current state"""
85
+ transM = ML.I(4)
86
+ transM[0][3] += x
87
+ transM[1][3] += y
88
+ transM[2][3] += z
89
+ self.state = ML.MxM(transM, self.state)
90
+ self.stateTouched = True
91
+ return self
92
+
93
+ def transform(self,
94
+ scale: list=(1,1,1),
95
+ rotate: list=(0,0,0),
96
+ translate: list=(0,0,0),
97
+ transMatrix: list=None
98
+ ):
99
+ """Apply transformation to current state"""
100
+ if transMatrix == None:
101
+ self\
102
+ .scale(scale)\
103
+ .rotate(*rotate)\
104
+ .translate(*translate)
105
+ else:
106
+ self.state = L.updateTransformationMatrix(self.state, transMatrix)
107
+ self.stateTouched = True
108
+ return self
109
+
110
+ def isUpdated(self) -> bool:
111
+ return not bool(self.stateTouched)
112
+
113
+ def update(self) -> None:
114
+ self.stateTouched = False
115
+
116
+ def getLines(self) -> list:
117
+ """Return all lines of object"""
118
+ return []
119
+
120
+ def getObjects(self) -> list: #< For compatibility with Assembly class
121
+ """Return a list of all objects"""
122
+ return []
@@ -0,0 +1,91 @@
1
+ import math
2
+ from GSOF_3dWireFrame.Lib3D import Lib3D as L
3
+
4
+ LINE_COLOR = (0,0,0)
5
+
6
+ def net(N=1000, M=1000, dN=100, dM=100, scale=1.0, color=LINE_COLOR) -> dict:
7
+ points = []
8
+ lines = []
9
+ for x in range(N):
10
+ points.append([0, x*dN, 0])
11
+ points.append([M*dM, x*dN, 0])
12
+
13
+ lines.append([(x*2), (x*2)+1])
14
+
15
+ for y in range(M):
16
+ points.append([y*dN, 0, 0])
17
+ points.append([y*dM, N*dN, 0])
18
+
19
+ lines.append([N*2+(y*2), N*2+(y*2)+1])
20
+
21
+ jsonObject = L.dataToDict(points, lines, scale, color)
22
+ return jsonObject
23
+
24
+ def sphere(radius, resolution=10, scale=1.0, color=LINE_COLOR):
25
+ points = []
26
+ lines = []
27
+
28
+ # Create points
29
+ for i in range(resolution):
30
+ theta = i * math.pi / (resolution - 1)
31
+ for j in range(resolution * 2):
32
+ phi = j * 2 * math.pi / (resolution * 2)
33
+ x = radius * math.sin(theta) * math.cos(phi)
34
+ y = radius * math.sin(theta) * math.sin(phi)
35
+ z = radius * math.cos(theta)
36
+ points.append([x, y, z])
37
+
38
+ # Create lines
39
+ for i in range(resolution - 1):
40
+ for j in range(resolution * 2):
41
+ p1 = i * resolution * 2 + j
42
+ p2 = i * resolution * 2 + (j + 1) % (resolution * 2)
43
+ p3 = ((i + 1) * resolution * 2 + j) % (len(points))
44
+ p4 = ((i + 1) * resolution * 2 + (j + 1) % (resolution * 2)) % (len(points))
45
+ lines.extend(((p1, p2), (p1, p3), (p2, p4)))
46
+
47
+ # Connect the last row
48
+ for j in range(resolution * 2):
49
+ p1 = (resolution - 1) * resolution * 2 + j
50
+ p2 = (resolution - 1) * resolution * 2 + (j + 1) % (resolution * 2)
51
+ lines.append((p1, p2))
52
+
53
+ return L.dataToDict(points, lines, scale, color)
54
+
55
+ def rectangle(X, Y, Z, scale=1.0, color=LINE_COLOR):
56
+ X = 0.5*X
57
+ Y = 0.5*Y
58
+ X = 0.5*Z
59
+
60
+ points = [
61
+ [-X, Y,-Z], #<0
62
+ [ X, Y,-Z], #<1
63
+ [ X, Y, Z], #<2
64
+ [-X, Y, Z], #<3
65
+
66
+ [-X,-Y,-Z], #<4
67
+ [ X,-Y,-Z], #<5
68
+ [ X,-Y, Z], #<6
69
+ [-X,-Y, Z], #<7
70
+ ]
71
+
72
+ lines = [[0,1],
73
+ [1,2],
74
+ [2,3],
75
+ [3,0],
76
+
77
+ [4,5],
78
+ [5,6],
79
+ [6,7],
80
+ [7,4],
81
+
82
+ [0,4],
83
+ [1,5],
84
+ [2,6],
85
+ [3,7],
86
+ ]
87
+
88
+ return L.dataToDict(points, lines, scale, color)
89
+
90
+ if __name__ == "__main__":
91
+ print(len(sphere(1000, 25)))
@@ -0,0 +1,9 @@
1
+ class Colors:
2
+ WHITE = (255,255,255)
3
+ BLACK = (0,0,0)
4
+ RED = (255,0,0)
5
+ GREEN = (0,170,0)
6
+ BLUE = (0,0,255)
7
+ DARK_BLUE = (220,220,255)
8
+ YELLOW = (125,125,0)
9
+ GRAY = (50,50,50)
@@ -0,0 +1,37 @@
1
+ class WireFrame():
2
+ def __init__(self, screen, line, f=50, scale=1.0):
3
+ self.screen = screen
4
+ self.line = line
5
+ self.f = f
6
+ self.scale = scale
7
+ self.centerX, self.centerY = (int(screen.get_width()/2), int(screen.get_height()/2))
8
+
9
+ def draw(self, obj, color=None) -> None:
10
+ """ """
11
+ for line in obj.getLines():
12
+ p0 = self.camera(line.points[0])
13
+ p1 = self.camera(line.points[1])
14
+ if (p0[2] > self.f) or (p1[2] > self.f): #Skip lines in the back of the viewer
15
+ lcolor = color
16
+ if lcolor == None:
17
+ lcolor = line.color
18
+ self.drawLine( lcolor, p0, p1 ) #< Line from P0 to P1
19
+
20
+ def camera(self, point) -> list:
21
+ """ Perspective projection <https://en.wikipedia.org/wiki/3D_projection> """
22
+ x, y, z = point
23
+ z = -z
24
+ if (self.f != None):
25
+ if (z > self.f):
26
+ s = (self.f/z)*self.scale
27
+ else:
28
+ s = 100000
29
+ x *= s
30
+ y *= s
31
+ return (x,y,z)
32
+
33
+ def drawLine(self, color, p0, p1):
34
+ """ """
35
+ p0 = (self.centerX +p0[0], self.centerY -p0[1])
36
+ p1 = (self.centerX +p1[0], self.centerY -p1[1])
37
+ self.line( self.screen, color, p0, p1 ) #< Line from P0 to P1
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env python
2
+
3
+ # this is a wrapper module for different platform implementations
4
+ #
5
+ # (C) 2019 Guy Soffer <gsoffer@yahoo.com>
6
+
7
+ VERSION = '101'
@@ -0,0 +1,40 @@
1
+ from GSOF_3dWireFrame.Lib3D import Lib3D as L
2
+ try:
3
+ import pymeshlab as ml
4
+ except:
5
+ print("could not find pymeshlab. DO NOT USE STL FILES!!\nTo install, run ```pip install pymeshlab```")
6
+
7
+ def stlToObj(filename, faceCount=-1, color=(0,0,0)):
8
+ # Load the STL file
9
+ ms = ml.MeshSet()
10
+ ms.load_new_mesh(filename)
11
+
12
+ # Simplify the mesh
13
+ if faceCount > 0:
14
+ ms.apply_filter('meshing_decimation_quadric_edge_collapse',
15
+ targetfacenum=faceCount)
16
+
17
+ # Get the simplified mesh
18
+ simplified_mesh = ms.current_mesh()
19
+ faceMatrix = simplified_mesh.face_matrix()
20
+ vertexMatrix = simplified_mesh.vertex_matrix()
21
+
22
+ # Initialize lists for points and lines
23
+ points = []
24
+ lines = []
25
+
26
+ for i in range(simplified_mesh.face_number()):
27
+ # Get the vertices of the triangle
28
+ vertexes = faceMatrix[i]
29
+
30
+ # Add the vertices (points) to the list
31
+ for vertex in vertexes:
32
+ points.append(vertexMatrix[vertex].tolist())
33
+
34
+ # Add the edges (lines) to the list
35
+ lines.append([i*3, i*3+1])
36
+ lines.append([i*3+1, i*3+2])
37
+ lines.append([i*3+2, i*3])
38
+
39
+ # Now you have a list of points and lines
40
+ return L.dataToDict(points, lines, scale=100.0, color=color)