線形状の長さを知りたい (スクリプト)

「開いた線形状」または「閉じた線形状」はベジェで表現されています。
この線形状の長さは、近似で以下のスクリプトで計算できます。

import numpy
import math

#---------------------------------------.
# ゼロチェック.
#---------------------------------------.
def isZero (v):
	minV = 1e-5
	if v > -minV and v < minV:
		return True
	return False

#---------------------------------------.
# ベクトルの減算.
# @param[in] v1  (x, y, z)の3要素.
# @param[in] v2  (x, y, z)の3要素.
#---------------------------------------.
def subVec3Vec3 (v1, v2):
	vec3_1 = numpy.array(v1)
	vec3_2 = numpy.array(v2)
	
	return vec3_1 - vec3_2

#---------------------------------------.
# ベクトルの長さを計算.
#---------------------------------------.
def lengthVec3 (v):
	vec3 = numpy.array(v)
	return numpy.linalg.norm(vec3)

#---------------------------------------.
# ベジェ上の位置を計算.
#---------------------------------------.
def getBezierPoint (v1Pos, v1Out, v2In, v2Pos, fPos):
	fMin = 1e-6

	rPos = [0.0, 0.0, 0.0]
	cPos = []
	cPos.append([v1Pos[0], v1Pos[1], v1Pos[2]])
	cPos.append([v1Out[0], v1Out[1], v1Out[2]])
	cPos.append([v2In[0],  v2In[1],  v2In[2]])
	cPos.append([v2Pos[0], v2Pos[1], v2Pos[2]])

	fPos2 = float(fPos)
	fPos2 = max(0.0, fPos2)
	fPos2 = min(1.0, fPos2)
	
	if isZero(fPos2):
		rPos = cPos[0]
		return rPos

	if isZero(fPos2 - 1.0):
		rPos = cPos[3]
		return rPos

	# ベジェ曲線の計算.
	t   = fPos2
	t2  = 1.0 - t
	t2d = t2 * t2
	td  = t * t
	b1  = t2d * t2
	b2  = 3.0 * t * t2d
	b3  = 3.0 * td * t2
	b4  = t * td

	for i in range(3):
		rPos[i] = b1 * cPos[0][i] + b2 * cPos[1][i] + b3 * cPos[2][i] + b4 * cPos[3][i]

	return rPos

#---------------------------------------.
# 指定の形状が線形状の場合に、長さを計算.
# @param[in] shape       対象形状.
# @param[in] lineDivCou  ラインの全体の分割数.
#---------------------------------------.
def getLineLength (shape, lineDivCou):
    if shape.type != 4:  # 線形状でない場合.
        return 0.0
    
    vCou = shape.total_number_of_control_points
    vList = []

    divCou = lineDivCou / vCou
    if divCou < 4:
        divCou = 4
    divD   = 1.0 / float(divCou)

    # ベジェをポイントで保持.
    for i in range(vCou):
        if shape.closed == False and (i + 1 >= vCou):
            break
        p1 = shape.control_point(i)
        p2 = shape.control_point((i + 1) % vCou)

        dPos = 0.0
        for j in range(divCou + 1):
            p = getBezierPoint(p1.position, p1.out_handle, p2.in_handle, p2.position, dPos)
            if (i == 0) or (i != 0 and j > 0):
                vList.append(p)
            dPos += divD

    # ラインの全体長を計算.
    vLineLenList = []
    allLen = 0.0
    for i in range(len(vList) - 1):
        lenV = lengthVec3(subVec3Vec3(vList[i + 1] , vList[i]))
        vLineLenList.append(lenV)
        allLen += lenV

    return allLen

# ----------------------------------.
# 選択された形状の長さを取得.
# ----------------------------------.
scene = xshade.scene()
shape = scene.active_shape()

# shapeの長さを計算.
lineLenV = getLineLength(shape, 100)

print "[" + shape.name + "] length = " + str(lineLenV) + " mm"

ブラウザで線形状を選択し、スクリプトウィンドウで上記のスクリプトをコピー&ペーストして実行します。
getLineLength関数の第二引数は、ライン全体の分割数になります。
この値が大きいほどより正確な長さになります。
ベジェ曲線の場合の長さは、あくまで近似である点に注意するようにしてください。

Translate »