664 lines
28 KiB
Python
664 lines
28 KiB
Python
import os, sys, bpy, math, time, platform, cairosvg, ppf.datamatrix, shutil, requests, json, redis, oss2, heapq
|
||
import matplotlib.pyplot as plt
|
||
from PIL import Image
|
||
import numpy as np
|
||
from addon_utils import enable
|
||
enable('io_import_images_as_planes')
|
||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||
import config
|
||
|
||
def gen_data_matrix(print_id, qr_path, size = 300):
|
||
svg = ppf.datamatrix.DataMatrix(f'p{print_id}').svg()
|
||
cairosvg.svg2png(bytestring=svg, write_to=qr_path, output_width=size, output_height=size, background_color='white')
|
||
|
||
def active_object(obj):
|
||
bpy.context.view_layer.objects.active = obj
|
||
obj.select_set(True)
|
||
|
||
def down_obj_fromoss(pid, print_type=1, order_id=None):
|
||
# print_type:// 打印状态 1:正常打印 2:重打 3:加打,4: 样品
|
||
print('开始下载obj文件...' , pid)
|
||
|
||
if not order_id is None:
|
||
path = os.path.join(workdir, f'{pid}_{order_id}')
|
||
else:
|
||
path = os.path.join(workdir, pid)
|
||
if not os.path.exists(path): os.makedirs(path)
|
||
|
||
# 根据前缀获取文件列表
|
||
prefix = f'objs/print/{pid}/'
|
||
filelist = oss2.ObjectIteratorV2(config.oss_bucket, prefix=prefix)
|
||
find = False
|
||
for file in filelist:
|
||
filename = file.key.split('/')[-1]
|
||
if filename == '': continue
|
||
if filename.endswith(f'{pid}.obj'):
|
||
find = True
|
||
localfile = os.path.join(path, filename)
|
||
res = config.oss_bucket.get_object_to_file(file.key, localfile)
|
||
print(f'下载文件:{file.key},状态:{res.status}')
|
||
|
||
if not find:
|
||
for file in os.listdir(path):
|
||
if file.endswith('.obj'):
|
||
print('找到其他obj文件,采用这个文件来生成需要的尺寸', file)
|
||
shutil.copy(os.path.join(path, file), os.path.join(path, f'{pid}.obj'))
|
||
find = True
|
||
break
|
||
if not find:
|
||
print('找不到obj文件,异常退出')
|
||
sys.exit(1)
|
||
|
||
def find_obj(pid, order_id):
|
||
find = False
|
||
if not os.path.exists(os.path.join(workdir, f'{pid}_{order_id}', f'{pid}.mtl')):
|
||
print('没有找到obj模型文件,开始下载')
|
||
down_obj_fromoss(pid, order_id=order_id)
|
||
if os.path.exists(os.path.join(workdir, f'{pid}_{order_id}', f'{pid}.jpg')):
|
||
shutil.move(os.path.join(workdir, f'{pid}_{order_id}', f'{pid}.jpg'), os.path.join(workdir, f'{pid}_{order_id}', f'{pid}Tex1.jpg'))
|
||
with open(os.path.join(workdir, f'{pid}_{order_id}', f'{pid}.mtl'), 'r') as f:
|
||
lines = f.readlines()
|
||
lines = [line.replace(f'map_Kd {pid}.jpg', f'map_Kd {pid}Tex1.jpg') for line in lines]
|
||
with open(os.path.join(workdir, f'{pid}_{order_id}', f'{pid}.mtl'), 'w') as f:
|
||
f.writelines(lines)
|
||
|
||
filelist = os.listdir(os.path.join(workdir, f'{pid}_{order_id}'))
|
||
for filename in filelist:
|
||
if '9cm' in filename:
|
||
find = True
|
||
return filename
|
||
for filename in filelist:
|
||
if f'{pid}.obj' in filename:
|
||
find = True
|
||
return filename
|
||
for filename in filelist:
|
||
if '.obj' in filename:
|
||
find = True
|
||
return filename
|
||
print('没有找到obj模型文件')
|
||
return ''
|
||
|
||
def find_pid_objname(pid):
|
||
for obj in bpy.data.objects:
|
||
if obj.name.startswith(str(pid)):
|
||
return obj.name
|
||
|
||
def get_obj_max_foot():
|
||
filename = find_obj(pid, order_id)
|
||
|
||
filename = os.path.join(workdir, f'{pid}_{order_id}', filename)
|
||
bpy.ops.wm.read_homefile()
|
||
bpy.context.preferences.view.language = 'en_US'
|
||
bpy.ops.object.delete(use_global=False, confirm=False)
|
||
bpy.ops.import_scene.obj(filepath=filename)
|
||
bpy.context.scene.unit_settings.scale_length = 0.001
|
||
bpy.context.scene.unit_settings.length_unit = 'CENTIMETERS'
|
||
bpy.context.scene.unit_settings.mass_unit = 'GRAMS'
|
||
|
||
obj = bpy.context.selected_objects[0]
|
||
bpy.context.view_layer.objects.active = obj
|
||
obj.select_set(True)
|
||
|
||
pid_objname = find_pid_objname(pid)
|
||
|
||
scale = 90 / obj.dimensions.y
|
||
obj.scale = (scale, scale, scale)
|
||
|
||
bpy.ops.object.align(align_mode='OPT_1', relative_to='OPT_1', align_axis={'Z'})
|
||
|
||
bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_MASS', center='MEDIAN')
|
||
obj.location[0] = 0
|
||
obj.location[1] = 0
|
||
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
|
||
|
||
if pid in ('76461', '98871', '112139'):
|
||
bpy.ops.mesh.primitive_plane_add(size=200, enter_editmode=False, align='WORLD', location=(0, 0, 0.6), scale=(1, 1, 1))
|
||
else:
|
||
bpy.ops.mesh.primitive_plane_add(size=200, enter_editmode=False, align='WORLD', location=(0, 0, 0.2), scale=(1, 1, 1))
|
||
# bpy.ops.wm.save_as_mainfile(filepath=os.path.join(workdir, f'{pid}_{order_id}', f'{pid}_{order_id}.blend'))
|
||
bpy.ops.object.modifier_add(type='BOOLEAN')
|
||
bpy.context.object.modifiers["Boolean"].object = bpy.data.objects[pid_objname]
|
||
bpy.context.object.modifiers["Boolean"].operation = 'INTERSECT'
|
||
bpy.context.object.modifiers["Boolean"].solver = 'FAST'
|
||
bpy.ops.object.modifier_apply(modifier="Boolean")
|
||
|
||
bpy.ops.mesh.separate(type='LOOSE')
|
||
|
||
max_area = 0
|
||
for obj in bpy.data.objects:
|
||
if obj.type == 'MESH' and obj.name.startswith('Plane'):
|
||
if len(obj.data.polygons) == 0: continue
|
||
area = obj.data.polygons[0].area
|
||
if area > max_area:
|
||
max_area = area
|
||
obj.name = 'foot'
|
||
print(f'最大脚底板面积: {max_area} cm²')
|
||
if max_area < 5:
|
||
print('最大脚底板面积太小,脚底模型可能有破损,进行再次处理')
|
||
numsTemp = 0
|
||
#最多执行三次 重新处理
|
||
while numsTemp < 3:
|
||
#每次削的比例要加上去
|
||
tempArea = 0.2+numsTemp*0.2
|
||
max_area = check_and_deal_foot_area(tempArea)
|
||
if max_area >= 5:
|
||
break
|
||
numsTemp += 1
|
||
|
||
if max_area < 5:
|
||
print('最大脚底板面积处理多次还没有得到理想的面积,退出')
|
||
sys.exit(1)
|
||
# bpy.ops.wm.save_as_mainfile(filepath=os.path.join(workdir, f'{pid}_{order_id}', f'{pid}_{order_id}.blend'))
|
||
active_object(bpy.data.objects['foot'])
|
||
foot_points = get_plane_points(bpy.data.objects['foot'])
|
||
# plot(get_plane_points(bpy.data.objects['foot']), 'blue')
|
||
bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_MASS', center='MEDIAN')
|
||
|
||
# print(f"location: {bpy.data.objects['foot'].location}")
|
||
bpy.ops.import_image.to_plane(files=[{"name":"qr.png"}], directory=f"{workdir}{pid}_{order_id}", relative=False)
|
||
# bpy.ops.mesh.primitive_plane_add(size=1, enter_editmode=False, align='WORLD', location=(0, 0, 0), scale=(1, 1, 1))
|
||
|
||
# print(f"new_location: {bpy.data.objects['foot'].location}")
|
||
|
||
active_object(bpy.data.objects['qr'])
|
||
bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_MASS', center='MEDIAN')
|
||
bpy.data.objects['qr'].rotation_euler[0] = 0
|
||
bpy.data.objects['qr'].location = bpy.data.objects['foot'].location
|
||
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
|
||
# bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_MASS', center='MEDIAN')
|
||
# print(f"qr_location: {bpy.data.objects['qr'].location}")
|
||
# print(f'qr_points: {get_plane_points(bpy.data.objects["qr"])}')
|
||
# plot(get_plane_points(bpy.data.objects['qr']), 'red')
|
||
return foot_points
|
||
|
||
#检测到脚底板面积小于5cm²,重新调几次程序再次处理,如果还不行就退出
|
||
def check_and_deal_foot_area(tempArea):
|
||
bpy.ops.mesh.primitive_plane_add(size=200, enter_editmode=False, align='WORLD', location=(0, 0, tempArea), scale=(1, 1, 1))
|
||
bpy.ops.object.modifier_add(type='BOOLEAN')
|
||
bpy.context.object.modifiers["Boolean"].object = bpy.data.objects[pid_objname]
|
||
bpy.context.object.modifiers["Boolean"].operation = 'INTERSECT'
|
||
bpy.context.object.modifiers["Boolean"].solver = 'FAST'
|
||
bpy.ops.object.modifier_apply(modifier="Boolean")
|
||
|
||
bpy.ops.mesh.separate(type='LOOSE')
|
||
|
||
max_area = 0
|
||
for obj in bpy.data.objects:
|
||
if obj.type == 'MESH' and obj.name.startswith('Plane'):
|
||
if len(obj.data.polygons) == 0: continue
|
||
area = obj.data.polygons[0].area
|
||
if area > max_area:
|
||
max_area = area
|
||
obj.name = 'foot'
|
||
print("再次处理脚底板得到的面积:" + str(max_area) + "cm²")
|
||
return max_area
|
||
|
||
|
||
def euclidean_distance(p1, p2):
|
||
return ((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2) ** 0.5
|
||
|
||
def nearest_neighbor_sort(points):
|
||
print('nearest neighbor sort')
|
||
n = len(points)
|
||
visited = set()
|
||
sorted_points = []
|
||
i = 1
|
||
# Start from the first point
|
||
current_point = points[0]
|
||
|
||
#while len(visited) < n:
|
||
while i < n:
|
||
i += 1
|
||
sorted_points.append(current_point)
|
||
visited.add(current_point)
|
||
|
||
# Create a priority queue to store distances and points
|
||
distance_queue = []
|
||
for point in points:
|
||
if point not in visited:
|
||
distance = euclidean_distance(current_point, point)
|
||
heapq.heappush(distance_queue, (distance, point))
|
||
|
||
# Find the nearest unvisited point
|
||
while distance_queue:
|
||
distance, next_point = heapq.heappop(distance_queue)
|
||
if next_point not in visited:
|
||
current_point = next_point
|
||
break
|
||
|
||
|
||
return sorted_points
|
||
|
||
def get_max_qr(foot_points):
|
||
|
||
def dis_flag(square, foot_points):
|
||
for point in foot_points:
|
||
dis0 = get_distance_from_point_to_line(point, square[0], square[1])
|
||
dis1 = get_distance_from_point_to_line(point, square[1], square[2])
|
||
dis2 = get_distance_from_point_to_line(point, square[2], square[3])
|
||
dis3 = get_distance_from_point_to_line(point, square[3], square[0])
|
||
min_dis = min([dis0, dis1, dis2, dis3])
|
||
return min_dis > 0.5
|
||
|
||
def get_distance_from_point_to_line(point, line_point1, line_point2):
|
||
# 对于两点坐标为同一点时,返回点与点的距离
|
||
if line_point1 == line_point2:
|
||
point_array = np.array(point)
|
||
point1_array = np.array(line_point1)
|
||
return np.linalg.norm(point_array - point1_array)
|
||
# 计算直线的三个参数
|
||
A = line_point2[1] - line_point1[1]
|
||
B = line_point1[0] - line_point2[0]
|
||
C = (line_point1[1] - line_point2[1]) * line_point1[0] + \
|
||
(line_point2[0] - line_point1[0]) * line_point1[1]
|
||
# 根据点到直线的距离公式计算距离
|
||
distance = np.abs(A * point[0] + B * point[1] + C) / (np.sqrt(A ** 2 + B ** 2))
|
||
return distance
|
||
|
||
# 判断方形是否在轮廓内
|
||
def square_in_polygon_default(square, polygon):
|
||
for point in square:
|
||
if not point_in_polygon(point, polygon):
|
||
return False
|
||
return True
|
||
|
||
# 自定义二维码初始坐标
|
||
def get_default_qr_points(foot_points):
|
||
max_x = max([x[0] for x in foot_points])
|
||
min_x = min([x[0] for x in foot_points])
|
||
max_y = max([x[1] for x in foot_points])
|
||
min_y = min([x[1] for x in foot_points])
|
||
|
||
center_x, center_y = (max_x + min_x) / 2, (max_y + min_y) / 2
|
||
flag_default = point_in_polygon((center_x, center_y), foot_points)
|
||
if not flag_default:
|
||
index_move = 0
|
||
while not flag_default and index_move < 5:
|
||
center_x = (center_x + min_x) / 2
|
||
index_move += 1
|
||
flag_default = point_in_polygon((center_x, center_y), foot_points)
|
||
if not flag_default:
|
||
while not flag_default:
|
||
center_y = (center_y + min_y) / 2
|
||
flag_default = point_in_polygon((center_x, center_y), foot_points)
|
||
|
||
length = min((center_x - min_x) / 2, (center_y - min_y) / 2) / 2
|
||
# 在不规则平面中心位置初始化一个方形
|
||
qr_points = [(center_x - length, center_y + length), (center_x + length, center_y + length), (center_x + length, center_y - length), (center_x - length, center_y - length)]
|
||
qr_points = scale_qr_new(foot_points, qr_points, length, (center_x, center_y), scale=1.05)
|
||
return qr_points
|
||
|
||
def scale_qr_new(foot_points, qr_points, length, center, scale=1.1):
|
||
default_flag = flag = square_in_polygon(qr_points, foot_points)
|
||
center_x, center_y = center[0], center[1]
|
||
if flag:
|
||
while default_flag == flag:
|
||
length *= scale
|
||
# 对每个点进行放大操作并更新坐标
|
||
qr_points = [((x - center_x) * scale + center_x, (y - center_y) * scale + center_y) for x, y in qr_points]
|
||
flag = square_in_polygon_default(qr_points, foot_points) and square_in_polygon(qr_points, foot_points)
|
||
else:
|
||
while default_flag == flag:
|
||
length /= scale
|
||
# 对每个点进行缩小操作并更新坐标
|
||
qr_points = [((x - center_x) / scale + center_x, (y - center_y) / scale + center_y) for x, y in qr_points]
|
||
flag = square_in_polygon_default(qr_points, foot_points) and square_in_polygon(qr_points, foot_points)
|
||
return qr_points
|
||
|
||
# 获取旋转后方形 根据方形原坐标旋转
|
||
def cal_rota_points(qr_points, center, angle):
|
||
center_x, center_y = center[0], center[1]
|
||
if angle > 0:
|
||
qr_points_after_rotate = []
|
||
for point in qr_points:
|
||
new_x = (point[0] - center_x) * math.cos(angle) - (point[1] - center_y) * math.sin(angle) + center_x
|
||
new_y = (point[0] - center_x) * math.sin(angle) + (point[1] - center_y) * math.cos(angle) + center_y
|
||
qr_points_after_rotate.append((new_x, new_y))
|
||
return qr_points_after_rotate
|
||
else:
|
||
return qr_points
|
||
|
||
# 取中点
|
||
def cal_middle_point(p1, p2):
|
||
x1, y1 = p1
|
||
x2, y2 = p2
|
||
# 中点
|
||
a1 = (x1 + x2) / 2
|
||
b1 = (y1 + y2) / 2
|
||
return a1, b1
|
||
|
||
def make_points(qr_points):
|
||
new_points = []
|
||
index = [0, 1, 2, 3, 0]
|
||
for i in range(4):
|
||
a, b = cal_middle_point(qr_points[index[i]], qr_points[index[i + 1]])
|
||
new_points.append((a, b))
|
||
new_points.append((cal_middle_point(qr_points[index[i]], (a, b))))
|
||
new_points.append((cal_middle_point(qr_points[index[i + 1]], (a, b))))
|
||
return new_points
|
||
|
||
#qr_points = get_default_qr_points(foot_points)
|
||
|
||
min_qr_length = 0.5
|
||
|
||
minx = min([p[0] for p in foot_points]) + min_qr_length
|
||
maxx = max([p[0] for p in foot_points]) - min_qr_length
|
||
miny = min([p[1] for p in foot_points]) + min_qr_length
|
||
maxy = max([p[1] for p in foot_points]) - min_qr_length
|
||
|
||
def rotate_qr_v3(foot_points, qr_points, scale, angle=1):
|
||
best_length = length = cal_square_length(qr_points)
|
||
best_angle, default_angle = 0, 0
|
||
center_x, center_y = calculate_center(qr_points)
|
||
best_qr_points = qr_points
|
||
# 循环1 求最佳angle 不断增大angle角度
|
||
while default_angle <= 90:
|
||
qr_points_after_rotate = cal_rota_points(qr_points, (center_x, center_y), default_angle)
|
||
# 在当前angle下增加边长
|
||
while square_in_polygon(qr_points_after_rotate, foot_points) and dis_flag(qr_points_after_rotate, foot_points):
|
||
flag = True
|
||
best_qr_points = qr_points_after_rotate
|
||
best_angle = default_angle
|
||
best_length = length
|
||
# 对每个点进行放大(或缩小)操作并更新坐标
|
||
qr_points = [((x - center_x) * scale + center_x, (y - center_y) * scale + center_y) for x, y in qr_points]
|
||
length *= scale
|
||
qr_points_after_rotate = cal_rota_points(qr_points, (center_x, center_y), default_angle)
|
||
# 限制最大边长
|
||
if best_length > 5:
|
||
return best_qr_points, best_angle, best_length
|
||
|
||
default_angle += angle
|
||
return best_qr_points, best_angle, best_length
|
||
|
||
if maxx - minx < maxy - miny:
|
||
step = (maxx - minx) / 15
|
||
else:
|
||
step = (maxy - miny) / 15
|
||
|
||
x, y = minx, miny
|
||
locations = []
|
||
|
||
while x <= maxx:
|
||
while y <= maxy:
|
||
locations.append((x, y))
|
||
y += step
|
||
x += step
|
||
y = miny
|
||
|
||
# print(f'locations: {locations}')
|
||
locations = [point for point in locations if all(cal_distance(point, f) >= min_qr_length for f in foot_points)]
|
||
location = locations[0]
|
||
qr_points = [(location[0] - 0.5, location[1] - 0.5), (location[0] + 0.5, location[1] - 0.5), (location[0] + 0.5, location[1] + 0.5), (location[0] - 0.5, location[1] + 0.5)]
|
||
plot(foot_points)
|
||
plot(qr_points, 'yellow')
|
||
plt.savefig(f'{workdir}{pid}_{order_id}/fig.png')
|
||
|
||
best_qr, max_qr_length, best_location, best_rotation = None, 0, None, 0
|
||
for location in locations:
|
||
plt.plot(location[0], location[1], 'ro')
|
||
qr_points = move_square(qr_points, location)
|
||
if not square_in_polygon(qr_points, foot_points) or not square_in_polygon_default(qr_points, foot_points):
|
||
continue
|
||
else:
|
||
# qr_points = scale_qr(foot_points, qr_points, 1.1)
|
||
# qrs.append(qr_points)
|
||
rotate_qr, rotate_angle, qr_length = rotate_qr_v3(foot_points, qr_points, 1.1, 1)
|
||
if qr_length > max_qr_length:
|
||
max_qr_length = qr_length
|
||
best_location = location
|
||
best_rotation = rotate_angle
|
||
best_qr = rotate_qr
|
||
|
||
rd = max_qr_length / 1.1 / 2
|
||
x, y = best_location[0], best_location[1]
|
||
new_qr_points = [(x - rd, y + rd), (x + rd, y + rd), (x + rd, y - rd), (x - rd, y - rd)]
|
||
new_qr_points = cal_rota_points(new_qr_points, best_location, best_rotation)
|
||
return new_qr_points, best_location, max_qr_length / 1.1, best_rotation
|
||
|
||
def get_plane_points(plane, print_points = False):
|
||
points = []
|
||
for edge in plane.data.edges:
|
||
point_index = edge.vertices[0]
|
||
point3d = plane.data.vertices[point_index].co
|
||
if print_points: print(point3d)
|
||
points.append((point3d[0], point3d[1]))
|
||
return points
|
||
|
||
def point_in_polygon(point, polygon):
|
||
num_intersections = 0
|
||
for i in range(len(polygon)):
|
||
p1, p2 = polygon[i], polygon[(i + 1) % len(polygon)]
|
||
if (p1[1] > point[1]) != (p2[1] > point[1]):
|
||
if point[0] < (p2[0] - p1[0]) * (point[1] - p1[1]) / (p2[1] - p1[1]) + p1[0]:
|
||
num_intersections += 1
|
||
return num_intersections % 2 == 1
|
||
|
||
def square_iou_polygon(square, polygon):
|
||
for point in square:
|
||
if point_in_polygon(point, polygon):
|
||
return True
|
||
return False
|
||
|
||
def square_in_polygon(square, polygon):
|
||
for point in polygon:
|
||
if point_in_polygon(point, square):
|
||
return False
|
||
return True
|
||
|
||
def plot(points, color='blue'):
|
||
x = [point[0] for point in points]
|
||
y = [point[1] for point in points]
|
||
if points[-1] != points[0]:
|
||
x.append(points[0][0])
|
||
y.append(points[0][1])
|
||
plt.plot(x, y, color=color)
|
||
|
||
def scale_qr(foot_points, qr_points, scale = 1.1):
|
||
while True:
|
||
old_points = qr_points
|
||
# 计算正方形的中心坐标
|
||
center_x = sum(x for x, y in qr_points) / len(qr_points)
|
||
center_y = sum(y for x, y in qr_points) / len(qr_points)
|
||
|
||
# 对每个点进行放大(或缩小)操作并更新坐标
|
||
qr_points = [((x - center_x) * scale + center_x, (y - center_y) * scale + center_y) for x, y in qr_points]
|
||
|
||
if not square_in_polygon(qr_points, foot_points):
|
||
qr_points = old_points
|
||
break
|
||
return qr_points
|
||
|
||
def rotate_qr(foot_points, qr_points, angle = 0.1):
|
||
while True:
|
||
old_points = qr_points
|
||
# 计算正方形的中心坐标
|
||
center_x = sum(x for x, y in qr_points) / len(qr_points)
|
||
center_y = sum(y for x, y in qr_points) / len(qr_points)
|
||
|
||
# 对每个点进行放大(或缩小)操作并更新坐标
|
||
qr_points = [(x - center_x, y - center_y) for x, y in qr_points]
|
||
|
||
qr_points = [(x * math.cos(angle) - y * math.sin(angle), x * math.sin(angle) + y * math.cos(angle)) for x, y in qr_points]
|
||
|
||
qr_points = [(x + center_x, y + center_y) for x, y in qr_points]
|
||
|
||
if not square_in_polygon(qr_points, foot_points):
|
||
qr_points = old_points
|
||
break
|
||
return qr_points
|
||
|
||
def scale_square(scale, foot_points, back = 0.0):
|
||
while True:
|
||
bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_MASS', center='MEDIAN')
|
||
old_dimensions = bpy.data.objects['qr'].dimensions.copy()
|
||
active_object(bpy.data.objects['qr'])
|
||
bpy.data.objects['qr'].scale = (scale, scale, 1)
|
||
max_square = get_plane_points(bpy.data.objects['qr'])
|
||
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
|
||
if not square_in_polygon(max_square, foot_points):
|
||
bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_MASS', center='MEDIAN')
|
||
bpy.data.objects['qr'].dimensions = (old_dimensions[0] - back, old_dimensions[1] - back, 0)
|
||
max_square = get_plane_points(bpy.data.objects['qr'])
|
||
location, size = get_square_center_size()
|
||
break
|
||
return max_square, location, size
|
||
|
||
def zoom_square(foot_points, qr_points, center, step_length=0.1):
|
||
while True:
|
||
old_dimensions = bpy.data.objects['qr'].dimensions.copy()
|
||
active_object(bpy.data.objects['qr'])
|
||
# print(f'old_dimensions: {old_dimensions}')
|
||
bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_MASS', center='MEDIAN')
|
||
bpy.data.objects['qr'].dimensions = (old_dimensions[0] + step_length, old_dimensions[1] + step_length, 0)
|
||
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
|
||
bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_MASS', center='MEDIAN')
|
||
# print(f'new_dimensions: {bpy.data.objects["qr"].dimensions}')
|
||
max_square = get_plane_points(bpy.data.objects['qr'])
|
||
if not square_in_polygon(max_square, foot_points):
|
||
bpy.data.objects['qr'].dimensions = (old_dimensions[0], old_dimensions[1], 0)
|
||
max_square = get_plane_points(bpy.data.objects['qr'])
|
||
location, size, length = get_square_center_size()
|
||
break
|
||
return max_square, location, size, length
|
||
|
||
def get_square_center_size():
|
||
active_object(bpy.data.objects['qr'])
|
||
bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_MASS', center='MEDIAN')
|
||
location = bpy.data.objects['qr'].location
|
||
size = bpy.data.objects['qr'].dimensions
|
||
length = size[0]
|
||
return location, size, length
|
||
|
||
def min_x(plane):
|
||
return min([p[0] for p in plane])
|
||
def min_y(plane):
|
||
return min([p[1] for p in plane])
|
||
def max_x(plane):
|
||
return max([p[0] for p in plane])
|
||
def max_y(plane):
|
||
return max([p[1] for p in plane])
|
||
|
||
def cal_square_length(square):
|
||
return abs(square[0][0] - square[1][0])
|
||
|
||
def cal_square_area(square):
|
||
return abs(square[0][0] - square[1][0]) * abs(square[0][1] - square[3][1])
|
||
|
||
def cal_distance(point1, point2):
|
||
return math.sqrt((point1[0] - point2[0])**2 + (point1[1] - point2[1])**2)
|
||
|
||
def calculate_center(vertices):
|
||
x_sum = sum(x for x, y in vertices)
|
||
y_sum = sum(y for x, y in vertices)
|
||
center_x = x_sum / len(vertices)
|
||
center_y = y_sum / len(vertices)
|
||
return center_x, center_y
|
||
|
||
def move_square(vertices, new_center):
|
||
center_x, center_y = calculate_center(vertices)
|
||
# print(f'center_x: {center_x}, center_y: {center_y}')
|
||
# print(f'new_center: {new_center}')
|
||
x_diff = center_x - new_center[0]
|
||
y_diff = center_y - new_center[1]
|
||
# print(f'x_diff: {x_diff}, y_diff: {y_diff}')
|
||
# print(f'vertices: {vertices}')
|
||
new_vertices = [(x - x_diff, y - y_diff) for x, y in vertices]
|
||
# print(f'new_vertices: {new_vertices}')
|
||
return new_vertices
|
||
|
||
def main(workdir, pid, order_id, print_id):
|
||
if not os.path.exists(os.path.join(workdir, f'{pid}_{order_id}')):
|
||
os.makedirs(os.path.join(workdir, f'{pid}_{order_id}'))
|
||
qr_path = os.path.join(workdir, f'{pid}_{order_id}' ,'qr.png')
|
||
gen_data_matrix(print_id, qr_path)
|
||
|
||
get_obj_max_foot()
|
||
|
||
qr_points = get_plane_points(bpy.data.objects['qr'])
|
||
active_object(bpy.data.objects['foot'])
|
||
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
|
||
foot_points = get_plane_points(bpy.data.objects['foot'])
|
||
# print('foot_points:', foot_points)
|
||
foot_points = nearest_neighbor_sort(foot_points)
|
||
max_qr, qr_location, max_qr_length, rotation = get_max_qr(foot_points)
|
||
|
||
print(f'qr_location: {qr_location}')
|
||
plt.plot(qr_location[0], qr_location[1], 'black')
|
||
plot(max_qr, 'green')
|
||
plt.axis('equal')
|
||
plt.savefig(os.path.join(workdir, f'{pid}_{order_id}', 'fig.png'))
|
||
|
||
bpy.ops.wm.save_as_mainfile(filepath=f'{workdir}{pid}_{order_id}/{pid}_qr_start.blend')
|
||
|
||
qr_position = {}
|
||
qr_location = (qr_location[0], qr_location[1], 0)
|
||
qr_dimensions = (max_qr_length, max_qr_length, 0)
|
||
# print(f'qr_location: {qr_location}')
|
||
# print(f'qr_dimensions: {qr_dimensions}')
|
||
qr_position["location"] = qr_location
|
||
qr_position["dimensions"] = qr_dimensions
|
||
qr_position["rotation"] = rotation
|
||
print(f'qr_position: {qr_position}')
|
||
# with open(os.path.join(workdir, f'{pid}_{order_id}', 'qr_position.txt'), 'w') as f:
|
||
# f.write(json.dumps(qr_position))
|
||
|
||
res = requests.get(f'{upload_qr_position_url}?print_id={print_id}&position_data={json.dumps(qr_position)}')
|
||
print(f'update_qr_position_url {upload_qr_position_url}:{res.text}')
|
||
|
||
bpy.ops.object.load_reference_image(filepath=os.path.join(workdir, f'{pid}_{order_id}', 'qr.png'))
|
||
bpy.context.object.rotation_euler = (math.radians(-180), math.radians(0), rotation)
|
||
bpy.ops.transform.translate(value=qr_location, orient_type='GLOBAL', orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)), orient_matrix_type='GLOBAL', mirror=False, use_proportional_edit=False, proportional_edit_falloff='SMOOTH', proportional_size=1, use_proportional_connected=False, use_proportional_projected=False, snap=False, snap_elements={'INCREMENT'}, use_snap_project=False, snap_target='CLOSEST', use_snap_self=True, use_snap_edit=True, use_snap_nonedit=True, use_snap_selectable=False, release_confirm=True)
|
||
|
||
bpy.context.object.empty_display_size = qr_dimensions[0]
|
||
|
||
# for obj in bpy.data.objects:
|
||
# if obj.type == 'MESH' and obj.name != pid:
|
||
# bpy.data.objects.remove(obj)
|
||
|
||
# qr_path = os.path.join(workdir,f'{pid}_{order_id}', f"{pid}_{order_id}Tex1_qr.png")
|
||
# jpg_path = os.path.join(workdir,f'{pid}_{order_id}', f"{pid}Tex1.jpg")
|
||
# jpg_img = Image.open(jpg_path)
|
||
# shutil.copyfile(jpg_path, os.path.join(workdir,f'{pid}_{order_id}', f"{pid}Tex1_noqr.jpg"))
|
||
|
||
# bpy.context.scene.eyek.res_x = jpg_img.width
|
||
# bpy.context.scene.eyek.res_y = jpg_img.height
|
||
# bpy.context.scene.eyek.path_export_image = qr_path
|
||
# bpy.data.objects[f'{pid}'].select_set(True)
|
||
# bpy.data.objects['Empty'].select_set(True)
|
||
# bpy.context.view_layer.objects.active = bpy.data.objects[f'{pid}']
|
||
# bpy.ops.eyek.exe()
|
||
|
||
# qr_img = Image.open(qr_path)
|
||
# jpg_img.paste(qr_img, (0, 0), qr_img)
|
||
# jpg_img.save(jpg_path)
|
||
|
||
# plt.axis('equal')
|
||
# plt.show()
|
||
|
||
# 保存blend文件
|
||
bpy.ops.wm.save_as_mainfile(filepath=f'{workdir}{pid}_{order_id}/{pid}_qr_end.blend')
|
||
bpy.ops.wm.quit_blender()
|
||
|
||
|
||
if __name__ == '__main__':
|
||
get_qr_position_url = 'https://mp.api.suwa3d.com/api/printOrder/getFootCodePositionData'
|
||
upload_qr_position_url = 'https://mp.api.suwa3d.com/api/printOrder/updateFootCodeStatus'
|
||
get_pid_by_printid_url = 'https://mp.api.suwa3d.com/api/printOrder/getPidByPrintId'
|
||
delete_form_foot_code_by_pid = 'https://mp.api.suwa3d.com/api/printOrder/deleteFormFootCodeByPid'
|
||
# get_qr_position_url = 'http://172.31.1.254:8199/api/printOrder/getFootCodePositionData'
|
||
# upload_qr_position_url = 'http://172.31.1.254:8199/api/printOrder/updateFootCodeStatus'
|
||
# get_pid_by_printid_url = 'http://172.31.1.254:8199/api/printOrder/getPidByPrintId'
|
||
|
||
if platform.system() == 'Windows':
|
||
workdir = 'd:\\print\\'
|
||
else:
|
||
workdir = '/data/datasets/foot/'
|
||
|
||
print(sys.argv)
|
||
if len(sys.argv) - (sys.argv.index("--") + 1) < 1:
|
||
print("Usage: blender -b -P auto_dm.py -- <pid_order_id_print_id>")
|
||
sys.exit(1)
|
||
pid, order_id, print_id = sys.argv[sys.argv.index("--") + 1].split('_')
|
||
|
||
main(workdir, pid, order_id, print_id) |