Skip to content

Instantly share code, notes, and snippets.

@rene-d
Created November 29, 2020 17:43
Show Gist options
  • Save rene-d/42cbdf24707f1412f8a1d317264ad8e2 to your computer and use it in GitHub Desktop.
Save rene-d/42cbdf24707f1412f8a1d317264ad8e2 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
# rene-d 2020/07/23
from sys import argv
import operator
from PIL import Image, ImageDraw, ImageFont
import numpy as np
# import csv
import argparse
parse = argparse.ArgumentParser(
description="Calcule les angles et longueurs du contour de <épaisseur> mm pour une longueur totale de <échelle> mm"
)
parse.add_argument(
"-m", "--model", action="store_true", help="affiche le modèle en fond"
)
parse.add_argument(
"scale",
metavar="échelle",
type=float,
nargs="?",
default=915,
help="hauteur du modèle en mm",
)
parse.add_argument(
"thickness",
metavar="épaisseur",
type=float,
nargs="?",
default=20,
help="épaisseur profilé en mm",
)
args = parse.parse_args()
scale = args.scale
thickness = args.thickness
# relevé des points dans l'image corse1.png
corse = [
(1198, 324), # Bonifacio
(966, 239),
(791, 250),
(684, 186), # Aleria
(380, 222),
(319, 265),
(214, 255),
(79, 291),
(88, 339),
(207, 345),
(230, 329), # Nonza
(310, 344), # Saint-Florent
(272, 385),
(272, 435),
(328, 471),
(372, 578), # Algajola
(435, 640), # ajouté
(550, 702),
(606, 621),
(634, 699),
(705, 672), # Cargese
(754, 588),
(820, 664),
(862, 648), # Sanguinaires
(842, 557), # Ajaccio
(958, 603),
(999, 491), # Propriano
(1053, 552),
(1127, 469),
(1147, 378),
(1180, 380),
]
min_x, min_y = (37, 113)
max_x, max_y = (1247, 774)
def rotate(xy, radians):
x, y = xy
c, s = np.cos(radians), np.sin(radians)
j = np.matrix([[c, s], [-s, c]])
m = np.dot(j, [x, y])
r_xy = float(m.T[0]), float(m.T[1])
return np.array(r_xy)
if args.model:
image = Image.open("corse1.png")
else:
image = Image.new("RGB", size=(776, 424), color=(255, 255, 255))
precision = 4
image = image.resize((776 * precision, 424 * precision), Image.BICUBIC)
font_number = ImageFont.truetype("HelveticaNeue.ttc", 8 * precision)
font_angle = ImageFont.truetype("HelveticaNeue.ttc", 10 * precision)
font_fixed = ImageFont.truetype("Menlo", 8 * precision)
# recalcule les coordonnées des points dans l'image
image_width, image_height = image.size
corse2 = []
for xy in corse:
x, y = xy
x = round((x - min_x) / (max_x - min_x) * image_width)
y = round((y - min_y) / (max_y - min_y) * image_height)
corse2.append((x, y))
corse = corse2
# dimensions max de la Corse en pixels
dim_x = max(map(operator.itemgetter(0), corse)) - min(
map(operator.itemgetter(0), corse)
)
dim_y = max(map(operator.itemgetter(1), corse)) - min(
map(operator.itemgetter(1), corse)
)
# ajuste le coefficient d'échelle
scale /= dim_x
# contexte PIL pour dessiner dans l'image
draw = ImageDraw.Draw(image)
_, text_height = draw.textsize("0", font=font_fixed)
info = f"n° long. angle coupe"
draw.text(
(0, image_height - 1 - (len(corse) + 1) * text_height),
info,
font=font_fixed,
fill=(0, 0, 0),
)
# dessine le contour
corse.append(corse[0])
draw.line(corse, fill=(128, 128, 255), width=8)
corse.pop()
# pour écrire les informations sur chaque segment/sommet
# csv_writer = csv.writer(open("corse-sommets.csv", "w"))
angles = []
total_length = 0
for i, p in enumerate(corse):
p1 = p
p2 = corse[(i + 1) % len(corse)]
p3 = corse[(i + 2) % len(corse)]
xy_middle = (p1[0] + p2[0]) / 2, (p1[1] + p2[1]) / 2
v1 = np.array(p1) - np.array(p2)
v2 = np.array(p3) - np.array(p2)
u1 = v1 / np.linalg.norm(v1)
u2 = v2 / np.linalg.norm(v2)
angle = np.math.atan2(np.linalg.det([v1, v2]), np.dot(v1, v2))
angles.append(angle)
angle = np.degrees(angle)
length = np.linalg.norm(v1) * scale
total_length += length
if angle >= 0:
cut_angle = angle / 2 # angle saillant
else:
cut_angle = 180 + angle / 2 # angle rentrant
print(f"{(i + 1):2d} l={length:6.1f} 𝛼={angle:6.1f}° cut={cut_angle:6.1f}°")
info = f"{(i + 1):2d} {length:6.1f} {angle:4.0f}° {cut_angle:4.0f}°"
draw.text(
(0, image_height - 1 - (len(corse) - i) * text_height),
info,
font=font_fixed,
fill=(0, 0, 0),
)
row = [
i + 1,
round(p[0] * scale, 1),
round(p[1] * scale, 1),
round(length, 1),
round(angle, 1),
round(cut_angle, 1),
]
# csv_writer.writerow(row)
draw.text(xy_middle, f"{i + 1}", align="center", fill=(0, 0, 0), font=font_number)
draw.text(p2, f"{angle:.0f}°", align="center", fill=(0, 0, 0), font=font_angle)
draw.point(p1, fill=(0, 0, 0))
interior = [] # bord intérieur
for i, p in enumerate(corse):
# points origine et extrémité
p1 = p
p2 = corse[(i + 1) % len(corse)]
# angles origine/extrémité
a1 = angles[(i - 1) % len(corse)]
a2 = angles[i]
v = np.array(p1) - np.array(p2)
u = v / np.linalg.norm(v) * thickness / scale
# traits de construction (pour vérifier les calculs!)
r = rotate(u, -np.pi / 2)
draw.line([p1, *(r + p1)], fill=(0, 0, 0))
draw.line([p2, *(r + p2)], fill=(0, 0, 0))
draw.line([*(r + p1), *(r + p2)], fill=(0, 0, 0))
if a2 >= 0:
color = (255, 0, 0) # coupe angle aigu
else:
color = (0, 255, 0) # coupe angle obtus
# trace le trait de coupe
xy = rotate(u / np.sin(a2 / 2), -a2 / 2) + p2
draw.line([p2, *xy], fill=color)
interior.append((*xy,))
interior.append(interior[0])
draw.line(interior, fill=(0, 0, 0), width=2)
interior.pop()
# la longueur du profilé est la longueur moyenne:
# - chaque bord est un trapèze, la surface du trapèze est (l1+l2)*h/2
# - la longueur du profilé est la surface du contour divisée par l'épaisseur
# - la surface du contour est la somme des surfaces des trapèzes, soit (∑(l1+l2))*h/2
# - et donc la longueur du profilé est ∑(l1+l2)/2
mid_length = 0
for i in range(len(corse)):
p1 = corse[i]
p2 = corse[(i + 1) % len(corse)]
v1 = np.array(p1) - np.array(p2)
p1 = interior[i]
p2 = interior[(i + 1) % len(corse)]
v2 = np.array(p1) - np.array(p2)
mid_length += np.linalg.norm(v1) + np.linalg.norm(v2)
mid_length = mid_length / 2 * scale
print(f"outline length: {total_length:.0f} mm")
print(f"profile length: {mid_length:.0f} mm")
# print(f"minimum length: {mid_length + thickness * 2 + len(corse) * 1.6:.0f} mm")
# dimensions Corse et longueur du contour
info = f"dim: {dim_x*scale:.1f} x {dim_y*scale:.1f}\ncontour: {total_length:.0f}"
tw, th = draw.textsize(info, font=font_fixed)
draw.text(
((image_width - tw) / 2, (image_height - th) / 2),
info,
fill=(0, 0, 0),
font=font_fixed,
align="center",
)
image.show()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment