Skip to content

Commit ddc53a0

Browse files
chore(spinbox): fix return type
1 parent d8177de commit ddc53a0

11 files changed

+1471
-1
lines changed

libs/pyTermTk/TermTk/TTkWidgets/spinbox.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ def mouseDragEvent(self, evt:TTkMouseEvent) -> bool:
168168
d = evt.y-evt.x
169169
if self._draggable:
170170
self.setValue(self._valueDelta + self._mouseDelta - d)
171-
return True
171+
return True
172172

173173
def setEnabled(self, enabled=True):
174174
self._lineEdit.setEnabled(enabled)
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
from PIL import Image, ImageDraw
2+
import numpy as np
3+
4+
def find_coeffs(source_coords, target_coords):
5+
"""
6+
Calculate coefficients for perspective transformation.
7+
"""
8+
matrix = []
9+
for s, t in zip(source_coords, target_coords):
10+
matrix.extend([
11+
[s[0], s[1], 1, 0, 0, 0, -t[0] * s[0], -t[0] * s[1]],
12+
[0, 0, 0, s[0], s[1], 1, -t[1] * s[0], -t[1] * s[1]]
13+
])
14+
15+
A = np.matrix(matrix, dtype=np.float64)
16+
B = np.array(target_coords).reshape(8)
17+
18+
try:
19+
res = np.linalg.solve(A, B)
20+
return tuple(np.array(res).flatten())
21+
except np.linalg.LinAlgError:
22+
print("Error: Could not solve transformation matrix.")
23+
return None
24+
25+
def main():
26+
# Load the input image
27+
try:
28+
image = Image.open("img4.png").convert("RGBA")
29+
except FileNotFoundError:
30+
print("Error: img1.png not found")
31+
return
32+
33+
# Create an empty output image (transparent background)
34+
output_size = (800, 600)
35+
output_image = Image.new("RGBA", output_size, (0, 0, 0, 0))
36+
37+
# Define the target quadrilateral (very small area in the output image)
38+
target_quad = [(50, 50), (100, 60), (120, 100), (45, 90)]
39+
40+
# Define the source coordinates (entire input image)
41+
width, height = image.size
42+
source_quad = [(0, 0), (width, 0), (width, height), (0, height)]
43+
44+
# Calculate the transformation coefficients
45+
coeffs = find_coeffs(source_quad, target_quad)
46+
if not coeffs:
47+
return
48+
49+
# Calculate the bounding box of the target quadrilateral
50+
min_x = min(p[0] for p in target_quad)
51+
min_y = min(p[1] for p in target_quad)
52+
max_x = max(p[0] for p in target_quad)
53+
max_y = max(p[1] for p in target_quad)
54+
55+
# Create a polygon mask to ensure transparency outside the target area
56+
mask = Image.new("L", output_size, 0)
57+
draw = ImageDraw.Draw(mask)
58+
draw.polygon(target_quad, fill=255)
59+
60+
# Apply the transformation to the entire input image
61+
transformed = image.transform(
62+
output_size,
63+
Image.PERSPECTIVE,
64+
coeffs,
65+
Image.BILINEAR
66+
)
67+
68+
# Apply the mask to keep only the transformed area within the target quadrilateral
69+
transformed.putalpha(mask)
70+
71+
# Paste the transformed image onto the output image
72+
output_image.paste(transformed, (0, 0), transformed)
73+
74+
# Save the result
75+
output_image.save("outImage.png")
76+
print("Image transformed and saved as outImage.png")
77+
78+
if __name__ == "__main__":
79+
main()
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from PIL import Image
2+
3+
img = Image.open('img5.png')
4+
5+
w,h = img.size
6+
out = img.transform((800,600), Image.Transform.QUAD, [100, 100, 500, 600, 400, 700, 50])
7+
8+
out.show()

tools/image/example.projection.2.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import math
2+
3+
def project_3d_to_2d(square_3d, observer, look_at, fov=90, aspect_ratio=1, near=0.1, far=1000):
4+
"""
5+
Project a 3D square onto a 2D plane.
6+
7+
Args:
8+
square_3d: List of 4 points representing the square in 3D space [(x, y, z), ...].
9+
observer: The observer's position in 3D space (x, y, z).
10+
look_at: The point the observer is looking at in 3D space (x, y, z).
11+
fov: Field of view in degrees.
12+
aspect_ratio: Aspect ratio of the 2D plane (width/height).
13+
near: Near clipping plane.
14+
far: Far clipping plane.
15+
16+
Returns:
17+
List of 2D points [(x, y), ...].
18+
"""
19+
# Step 1: Create the view matrix
20+
def normalize(v):
21+
return v / np.linalg.norm(v)
22+
23+
forward = normalize(np.array(look_at) - np.array(observer))
24+
right = normalize(np.cross(forward, [0, 1, 0]))
25+
up = np.cross(right, forward)
26+
27+
view_matrix = np.array([
28+
[right[0], right[1], right[2], -np.dot(right, observer)],
29+
[up[0], up[1], up[2], -np.dot(up, observer)],
30+
[-forward[0], -forward[1], -forward[2], np.dot(forward, observer)],
31+
[0, 0, 0, 1]
32+
])
33+
34+
# Step 2: Create the projection matrix
35+
fov_rad = np.radians(fov)
36+
f = 1 / np.tan(fov_rad / 2)
37+
projection_matrix = np.array([
38+
[f / aspect_ratio, 0, 0, 0],
39+
[0, f, 0, 0],
40+
[0, 0, (far + near) / (near - far), (2 * far * near) / (near - far)],
41+
[0, 0, -1, 0]
42+
])
43+
44+
# Step 3: Transform the 3D points to 2D
45+
square_2d = []
46+
for point in square_3d:
47+
# Convert to homogeneous coordinates
48+
point_3d = np.array([point[0], point[1], point[2], 1])
49+
# Apply view and projection transformations
50+
transformed_point = projection_matrix @ view_matrix @ point_3d
51+
# Perform perspective divide
52+
x = transformed_point[0] / transformed_point[3]
53+
y = transformed_point[1] / transformed_point[3]
54+
square_2d.append((x, y))
55+
56+
return square_2d
57+
58+
59+
# Example usage
60+
square_3d = [
61+
(1, 1, 5), # Top-right
62+
(-1, 1, 5), # Top-left
63+
(-1, -1, 5), # Bottom-left
64+
(1, -1, 5) # Bottom-right
65+
]
66+
observer = (0, 0, 0) # Observer's position
67+
look_at = (0, 0, 1) # Observer is looking along the positive Z-axis
68+
69+
square_2d = project_3d_to_2d(square_3d, observer, look_at)
70+
print("2D Coordinates of the square:", square_2d)

tools/image/example.projection.3.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import math
2+
3+
def project_3d_to_2d(observer, look_at, fov_h, fov_v, screen_width, screen_height, point_3d):
4+
"""
5+
Project a 3D point onto a 2D screen.
6+
7+
Args:
8+
observer: The observer's position in 3D space (x, y, z).
9+
look_at: The point the observer is looking at in 3D space (x, y, z).
10+
fov_h: Horizontal field of view in radians.
11+
fov_v: Vertical field of view in radians.
12+
screen_width: Width of the 2D screen.
13+
screen_height: Height of the 2D screen.
14+
point_3d: The 3D point to project (x, y, z).
15+
16+
Returns:
17+
The 2D coordinates of the projected point (x, y) on the screen.
18+
"""
19+
# Step 1: Calculate the forward, right, and up vectors
20+
def normalize(v):
21+
length = math.sqrt(sum(coord ** 2 for coord in v))
22+
return tuple(coord / length for coord in v)
23+
24+
forward = normalize((look_at[0] - observer[0], look_at[1] - observer[1], look_at[2] - observer[2]))
25+
right = normalize((
26+
forward[1] * 0 - forward[2] * 1,
27+
forward[2] * 0 - forward[0] * 0,
28+
forward[0] * 1 - forward[1] * 0
29+
))
30+
up = (
31+
right[1] * forward[2] - right[2] * forward[1],
32+
right[2] * forward[0] - right[0] * forward[2],
33+
right[0] * forward[1] - right[1] * forward[0]
34+
)
35+
36+
# Step 2: Transform the 3D point into the observer's coordinate system
37+
relative_point = (
38+
point_3d[0] - observer[0],
39+
point_3d[1] - observer[1],
40+
point_3d[2] - observer[2]
41+
)
42+
x_in_view = sum(relative_point[i] * right[i] for i in range(3))
43+
y_in_view = sum(relative_point[i] * up[i] for i in range(3))
44+
z_in_view = sum(relative_point[i] * forward[i] for i in range(3))
45+
46+
# Step 3: Perform perspective projection
47+
if z_in_view <= 0:
48+
raise ValueError("The point is behind the observer and cannot be projected.")
49+
50+
aspect_ratio = screen_width / screen_height
51+
tan_fov_h = math.tan(fov_h / 2)
52+
tan_fov_v = math.tan(fov_v / 2)
53+
54+
ndc_x = x_in_view / (z_in_view * tan_fov_h * aspect_ratio)
55+
ndc_y = y_in_view / (z_in_view * tan_fov_v)
56+
57+
# Step 4: Map normalized device coordinates (NDC) to screen coordinates
58+
screen_x = (ndc_x + 1) / 2 * screen_width
59+
screen_y = (1 - ndc_y) / 2 * screen_height
60+
61+
return screen_x, screen_y
62+
63+
64+
# Example usage
65+
observer = (0, 0, 0) # Observer's position
66+
look_at = (0, 0, 1) # Observer is looking along the positive Z-axis
67+
fov_h = math.radians(90) # Horizontal FOV in radians
68+
fov_v = math.radians(60) # Vertical FOV in radians
69+
screen_width = 800 # Screen width
70+
screen_height = 600 # Screen height
71+
point_3d = (1, 1, 5) # The 3D point to project
72+
73+
projected_2d_point = project_3d_to_2d(observer, look_at, fov_h, fov_v, screen_width, screen_height, point_3d)
74+
print("Projected 2D Point:", projected_2d_point)

tools/image/example.projection.4.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import numpy as np
2+
3+
def project_point(camera_position, look_at, point_3d):
4+
"""
5+
Project a 3D point into a normalized projection matrix.
6+
7+
Args:
8+
camera_position: The 3D position of the camera (x, y, z).
9+
look_at: The 3D position the camera is looking at (x, y, z).
10+
point_3d: The 3D point to project (x, y, z).
11+
12+
Returns:
13+
The 2D coordinates of the projected point in normalized space.
14+
"""
15+
# Step 1: Calculate the forward, right, and up vectors
16+
def normalize(v):
17+
return v / np.linalg.norm(v)
18+
19+
forward = normalize(np.array(look_at) - np.array(camera_position))
20+
right = normalize(np.cross(forward, [0, 1, 0]))
21+
up = np.cross(right, forward)
22+
23+
# Step 2: Create the view matrix
24+
view_matrix = np.array([
25+
[right[0], right[1], right[2], -np.dot(right, camera_position)],
26+
[up[0], up[1], up[2], -np.dot(up, camera_position)],
27+
[-forward[0], -forward[1], -forward[2], np.dot(forward, camera_position)],
28+
[0, 0, 0, 1]
29+
])
30+
31+
# Step 3: Create the projection matrix
32+
near = 1.0 # Near plane normalized to 1
33+
width = 1.0 # Width of the near plane
34+
height = 1.0 # Height of the near plane
35+
aspect_ratio = width / height
36+
37+
projection_matrix = np.array([
38+
[1 / aspect_ratio, 0, 0, 0],
39+
[0, 1, 0, 0],
40+
[0, 0, -1, -2 * near],
41+
[0, 0, -1, 0]
42+
])
43+
44+
# Step 4: Transform the 3D point into clip space
45+
point_3d_homogeneous = np.array([point_3d[0], point_3d[1], point_3d[2], 1])
46+
view_space_point = view_matrix @ point_3d_homogeneous
47+
clip_space_point = projection_matrix @ view_space_point
48+
49+
# Step 5: Perform perspective divide to get normalized device coordinates (NDC)
50+
if clip_space_point[3] == 0:
51+
raise ValueError("Invalid projection: w component is zero.")
52+
ndc_x = clip_space_point[0] / clip_space_point[3]
53+
ndc_y = clip_space_point[1] / clip_space_point[3]
54+
55+
return ndc_x, ndc_y
56+
57+
58+
# Example usage
59+
camera_position = (0, 0, 0) # Camera position
60+
look_at = (0, 0, -1) # Camera is looking along the negative Z-axis
61+
point_3d = (0.5, 0.5, -2) # A 3D point to project
62+
63+
projected_point = project_point(camera_position, look_at, point_3d)
64+
print("Projected 2D Point in NDC:", projected_point)

tools/image/example.projection.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import numpy as np
2+
3+
def project_3d_to_2d(square_3d, observer, look_at, fov=90, aspect_ratio=1, near=0.1, far=1000):
4+
"""
5+
Project a 3D square onto a 2D plane.
6+
7+
Args:
8+
square_3d: List of 4 points representing the square in 3D space [(x, y, z), ...].
9+
observer: The observer's position in 3D space (x, y, z).
10+
look_at: The point the observer is looking at in 3D space (x, y, z).
11+
fov: Field of view in degrees.
12+
aspect_ratio: Aspect ratio of the 2D plane (width/height).
13+
near: Near clipping plane.
14+
far: Far clipping plane.
15+
16+
Returns:
17+
List of 2D points [(x, y), ...].
18+
"""
19+
# Step 1: Create the view matrix
20+
def normalize(v):
21+
return v / np.linalg.norm(v)
22+
23+
forward = normalize(np.array(look_at) - np.array(observer))
24+
right = normalize(np.cross(forward, [0, 1, 0]))
25+
up = np.cross(right, forward)
26+
27+
view_matrix = np.array([
28+
[right[0], right[1], right[2], -np.dot(right, observer)],
29+
[up[0], up[1], up[2], -np.dot(up, observer)],
30+
[-forward[0], -forward[1], -forward[2], np.dot(forward, observer)],
31+
[0, 0, 0, 1]
32+
])
33+
34+
# Step 2: Create the projection matrix
35+
fov_rad = np.radians(fov)
36+
f = 1 / np.tan(fov_rad / 2)
37+
projection_matrix = np.array([
38+
[f / aspect_ratio, 0, 0, 0],
39+
[0, f, 0, 0],
40+
[0, 0, (far + near) / (near - far), (2 * far * near) / (near - far)],
41+
[0, 0, -1, 0]
42+
])
43+
44+
# Step 3: Transform the 3D points to 2D
45+
square_2d = []
46+
for point in square_3d:
47+
# Convert to homogeneous coordinates
48+
point_3d = np.array([point[0], point[1], point[2], 1])
49+
# Apply view and projection transformations
50+
transformed_point = projection_matrix @ view_matrix @ point_3d
51+
# Perform perspective divide
52+
x = transformed_point[0] / transformed_point[3]
53+
y = transformed_point[1] / transformed_point[3]
54+
square_2d.append((x, y))
55+
56+
return square_2d
57+
58+
59+
# Example usage
60+
square_3d = [
61+
(1, 1, 5), # Top-right
62+
(-1, 1, 5), # Top-left
63+
(-1, -1, 5), # Bottom-left
64+
(1, -1, 5) # Bottom-right
65+
]
66+
observer = (0, 0, 0) # Observer's position
67+
look_at = (0, 0, 1) # Observer is looking along the positive Z-axis
68+
69+
square_2d = project_3d_to_2d(square_3d, observer, look_at)
70+
print("2D Coordinates of the square:", square_2d)

0 commit comments

Comments
 (0)