背景
TechDrawにViewとして図示したものの大きさを取得したかったので、試行錯誤の末に方法を把握しました。備忘録として内容を記事に残します。
使ったもの
FreeCAD 1.0.2TechDrawでViewを描画済みのプロジェクト
TechDrawで描画方法はTechDrawをpythonで暑かった時の記事やTechDrawの使い方解説ページや公式の説明書を参考にしてください。
大きさ把握プログラム
全体を共有して要所を解説します。import math
def update_max_min(val, max, min):
if max is None:
max = val
elif max < val:
max = val
if min is None:
min = val
elif min > val:
min = val
return (max, min)
def normalize_degree360(deg):
return deg % 360
def get_degree360_of_point(point, center):
# print(point)
# print(center)
x = point.x - center.x
y = point.y - center.y
# print("x = %s y = %s" %(x, y))
if x == 0:
return -90 if y < 0 else 90
deg = math.atan(y/x) / math.pi * 180
# print(deg)
if x < 0:
deg += 180
return normalize_degree360(deg)
def has_line_on_degree360(deg_target, deg_start, deg_end):
deg_target = normalize_degree360(deg_target)
deg_start = normalize_degree360(deg_start)
deg_end = normalize_degree360(deg_end)
if deg_start < deg_end:
return deg_start <= deg_target and deg_target <= deg_end
else:
return deg_target <= deg_start or deg_end <= deg_target
def get_xy_max_min_of_edge_circle(edge):
c = edge.Curve.Center
r = edge.Curve.Radius
edge_x_max = None
edge_x_min = None
edge_y_max = None
edge_y_min = None
if edge.Closed:
edge_x_max = c.x + r
edge_x_min = c.x - r
edge_y_max = c.y + r
edge_y_min = c.y - r
else:
if edge.Vertexes[0].Orientation == "Forward":
point_cw_end = edge.Vertexes[0].Point
point_cw_start = edge.Vertexes[1].Point
else:
point_cw_end = edge.Vertexes[1].Point
point_cw_start = edge.Vertexes[0].Point
edge_x_max = max(point_cw_start.x, point_cw_end.x)
edge_x_min = min(point_cw_start.x, point_cw_end.x)
edge_y_max = max(point_cw_start.y, point_cw_end.y)
edge_y_min = min(point_cw_start.y, point_cw_end.y)
deg_cw_start = get_degree360_of_point(point_cw_start, c)
deg_cw_end = get_degree360_of_point(point_cw_end, c)
if has_line_on_degree360(0, deg_cw_start, deg_cw_end):
edge_x_max = c.x + r
if has_line_on_degree360(90, deg_cw_start, deg_cw_end):
edge_y_max = c.y + r
if has_line_on_degree360(180, deg_cw_start, deg_cw_end):
edge_x_min = c.x - r
if has_line_on_degree360(270, deg_cw_start, deg_cw_end):
edge_y_min = c.y - r
return ((edge_x_max, edge_y_max), (edge_x_min, edge_y_min))
def calc_size_xy_of_view(view):
x_max = None
x_min = None
y_max = None
y_min = None
for edge in view.getVisibleEdges():
typeId = edge.Curve.TypeId
if typeId == "Part::GeomCircle":
((edge_x_max, edge_y_max), (edge_x_min, edge_y_min)) = get_xy_max_min_of_edge_circle(edge)
(x_max, x_min) = update_max_min(edge_x_max, x_max, x_min)
(x_max, x_min) = update_max_min(edge_x_min, x_max, x_min)
(y_max, y_min) = update_max_min(edge_y_max, y_max, y_min)
(y_max, y_min) = update_max_min(edge_y_min, y_max, y_min)
else:
for vec in edge.Vertexes:
p = vec.Point
(x_max, x_min) = update_max_min(p.x, x_max, x_min)
(y_max, y_min) = update_max_min(p.y, y_max, y_min)
size_x = x_max - x_min if x_max is not None and x_min is not None else None
size_y = y_max - y_min if y_max is not None and y_min is not None else None
return (size_x, size_y)
for obj in FreeCAD.activeDocument().Objects:
if obj.TypeId == "TechDraw::DrawProjGroupItem":
size_xy = calc_size_xy_of_view(obj)
print("size of %s is %s" % (obj.Label, size_xy))
表示中のDocumentのTechDrawのViewの大きさを表示します。
for obj in FreeCAD.activeDocument().Objects:
if obj.TypeId == "TechDraw::DrawProjGroupItem":
size_xy = calc_size_xy_of_view(obj)
print("size of %s is %s" % (obj.Label, size_xy))
Viewのedge(線)はgetVisibleEdgesでViewから取得できます。
def calc_size_xy_of_view(view):
for edge in view.getVisibleEdges():
edgeが直線だった場合は、edge.Vertexesで取得可能な2点を座標を利用して描画領域の最大値と最小値を求めれます。
for edge in view.getVisibleEdges():
typeId = edge.Curve.TypeId
if typeId == "Part::GeomCircle":
...
else:
for vec in edge.Vertexes:
p = vec.Point
(x_max, x_min) = update_max_min(p.x, x_max, x_min)
(y_max, y_min) = update_max_min(p.y, y_max, y_min)
edgeが円の場合は、中心点と半径で最大値と最小値を求めれます。
def get_xy_max_min_of_edge_circle(edge):
c = edge.Curve.Center
r = edge.Curve.Radius
if edge.Closed:
edge_x_max = c.x + r
edge_x_min = c.x - r
edge_y_max = c.y + r
edge_y_min = c.y - r
edgeが円ではなく円弧の場合は、中心点と半径に加えて開始点と終了点を利用して最大値と最小値を求めます。
この処理の作成が難儀でした。
def has_line_on_degree360(deg_target, deg_start, deg_end):
deg_target = normalize_degree360(deg_target)
deg_start = normalize_degree360(deg_start)
deg_end = normalize_degree360(deg_end)
if deg_start < deg_end:
return deg_start <= deg_target and deg_target <= deg_end
else:
return deg_target <= deg_start or deg_end <= deg_target
def get_xy_max_min_of_edge_circle(edge):
if edge.Closed:
...
else:
if edge.Vertexes[0].Orientation == "Forward":
point_cw_end = edge.Vertexes[0].Point
point_cw_start = edge.Vertexes[1].Point
else:
point_cw_end = edge.Vertexes[1].Point
point_cw_start = edge.Vertexes[0].Point
edge_x_max = max(point_cw_start.x, point_cw_end.x)
edge_x_min = min(point_cw_start.x, point_cw_end.x)
edge_y_max = max(point_cw_start.y, point_cw_end.y)
edge_y_min = min(point_cw_start.y, point_cw_end.y)
deg_cw_start = get_degree360_of_point(point_cw_start, c)
deg_cw_end = get_degree360_of_point(point_cw_end, c)
if has_line_on_degree360(0, deg_cw_start, deg_cw_end):
edge_x_max = c.x + r
if has_line_on_degree360(90, deg_cw_start, deg_cw_end):
edge_y_max = c.y + r
if has_line_on_degree360(180, deg_cw_start, deg_cw_end):
edge_x_min = c.x - r
if has_line_on_degree360(270, deg_cw_start, deg_cw_end):
edge_y_min = c.y - r
Viewの円弧を扱う際はこれらの決まりに注意が必要です。
Viewの座標はx軸は水平で右方向が正で、y軸は垂直で下方向が正
edge.Vertexes[0].OrientationのForwardは時計回り
図示するとこうです。
それを踏まえて中心点に対する開始点と終了点の角度を算出しています。
Pythonコンソールはmathが使えたのでmath.atanを利用しました。
def get_degree360_of_point(point, center):
# print(point)
# print(center)
x = point.x - center.x
y = point.y - center.y
# print("x = %s y = %s" %(x, y))
if x == 0:
return -90 if y < 0 else 90
deg = math.atan(y/x) / math.pi * 180
# print(deg)
if x < 0:
deg += 180
return normalize_degree360(deg)
def get_xy_max_min_of_edge_circle(edge):
if edge.Closed:
...
else:
if edge.Vertexes[0].Orientation == "Forward":
point_cw_end = edge.Vertexes[0].Point
point_cw_start = edge.Vertexes[1].Point
else:
point_cw_end = edge.Vertexes[1].Point
point_cw_start = edge.Vertexes[0].Point
deg_cw_start = get_degree360_of_point(point_cw_start, c)
deg_cw_end = get_degree360_of_point(point_cw_end, c)
上記のプログラムをpythonコンソールで実行することで、Viewの左右と上下の大きさを取得できました。
ほぼ期待通りに横幅7mm、縦幅3mmを算出できました。
size of View is (6.9999999999999885, 2.9999999999999996)
おわり
円弧の扱いに苦労しましたが、直線と円で構成されたViewの大きさをプログラムで把握可能になりました。期待通りの処理が組めて嬉しいです。
参考
中心点と半径の取得方法を把握した掲示板のやりとりです。find the center of a circle with python
Edgeの開始点と終了点の取得方法を把握した掲示板のやりとりです。
No easy way to get start and end points of straight edge?




0 件のコメント :
コメントを投稿