線形状をポリゴンメッシュのエッジに変換 (スクリプト)

線形状をポリゴンメッシュのエッジに変換する機能は標準ではありません。
そのため、スクリプトで実現します。
なお、曲線からポリゴンメッシュのエッジに変換する処理は近似になります。

ブラウザで線形状を選択し以下のスクリプトを実行すると、
分割数を指定するダイアログボックスが出ます。
ダイアログボックスでOKボタンを押すと、指定の分割数でエッジのみのポリゴンメッシュが作成されます。


import numpy
import math

scene = xshade.scene()

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

#---------------------------------------.
# ベジェ上の位置を計算.
#---------------------------------------.
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  ラインの全体の分割数.
# @return ポイントの配列.
#---------------------------------------.
def getLinePoints (shape, lineDivCou):
  vCou = shape.total_number_of_control_points
  vList = []
  if shape.type != 4 or vCou < 2:  # 線形状でない場合.
    return 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

  return vList

#---------------------------------------.
# ポイントのみで構成された配列情報から、等間隔になるように再計算.
# @param[in] vList   ポイントの配列.
# @param[in] divCou  新しいラインの分割数.
# @return ポイントの配列.
#---------------------------------------.
def recalcLinePoints (vList, divCou):
  # numpyの形式に配列に格納し直す.
  vListLen = len(vList)
  if vListLen < 2:
      return []

  posA = []
  for i in range(vListLen):
      posA.append(numpy.array([vList[i][0], vList[i][1], vList[i][2]]))

  # ラインの長さを計算.
  allLen = 0.0
  lenList = []
  for i in range(vListLen - 1):
    vLen = numpy.linalg.norm(posA[i + 1] - posA[i])
    lenList.append(vLen)
    allLen += vLen

  dLen = allLen / (divCou - 1.0)
  newPosA = []
  newPosA.append([posA[0][0], posA[0][1], posA[0][2]])
  dPos = 0.0
  for i in range(vListLen - 1):
    len1 = lenList[i]

    if dPos + len1 < dLen:
      dPos += len1
      continue

    dPos2 = 0.0
    while dPos2 < len1:
      dd = (dPos2 + (dLen - dPos)) / len1
      p = (posA[i + 1] - posA[i]) * dd + posA[i]
      newPosA.append([p[0], p[1], p[2]])
      if len(newPosA) >= divCou - 1:
        break
      dd2 = dLen - dPos
      if dPos2 + dd2 + dLen > len1:
        dPos = len1 - (dPos2 + dd2)
        break
      dPos2 += dd2
      dPos = 0.0

    if len(newPosA) >= divCou - 1:
      break

  newPosA.append([posA[-1][0], posA[-1][1], posA[-1][2]])
  return newPosA

# ---------------------------------------------.
shape = scene.active_shape()

if shape.type != 4 or shape.total_number_of_control_points < 2:
  xshade.show_message_box('ポイント数が2以上の線形状を選択してください。', False)

else:
  # ダイアログボックスの作成と表示.
  dlg = xshade.create_dialog_with_uuid('05783960-e90b-4a70-9488-daefa220447d')
  div_id = dlg.append_int('分割数')
  dlg.set_value(div_id, 10)
  dlg.set_default_value(div_id, 10)

  dlg.append_default_button()

  if dlg.ask("線形状をポリゴンメッシュのエッジに変換"):
    rDivCou = dlg.get_value(div_id) + 1
    if rDivCou < 2:
      rDivCou = 2

    # 線形状をポイントで分割.
    divCou = min(40, rDivCou * 4)
    vList = getLinePoints(shape, divCou)
    vList2 = recalcLinePoints(vList, rDivCou)
    if shape.closed:
      vList2.pop()

    # ポリゴンメッシュとして配置.
    scene.begin_creating()
    scene.begin_polygon_mesh(None)

    vCou = len(vList2)
    for p in vList2:
      scene.append_polygon_mesh_vertex(p)
    
    vCou2 = vCou
    if shape.closed == False:
      vCou2 = vCou2 - 1

    for i in range(vCou2):
      i0 = i
      i1 = (i + 1) % vCou
      scene.append_polygon_mesh_edge(i0, i1)

    scene.end_polygon_mesh()
    scene.end_creating()


この処理は、ポリゴンメッシュのエッジが等間隔になるようにしています。

Translate »