2.91 Python API Tidak ada perubahan setelah menerapkan operator pengubah boolean

Oct 06 2020

Bagaimana cara 'Menerapkan' operasi Perbedaan Boolean ? Saya telah menulis beberapa Python yang membuat objek berlubang secara visual, tetapi sebenarnya tidak berlubang. Secara visual saya mendapatkan hal yang benar:

Mengekspor ke STL masih memiliki kedua objek (bukan shell kosong). Saya dapat menyelesaikan aplikasi operasi Boolean secara manual dengan "Menerapkan"

dan menghapus objek dalam. Kemudian ekspor ke file STL benar.

Berikut adalah kode yang saya tulis, dapatkah seseorang memberi tahu saya panggilan apa yang dapat saya lakukan untuk menerapkan tindakan pengubah boolean dan menghapus objek yang saya gunakan untuk memotong objek lain?

import sys
import bpy
D = bpy.data
C = bpy.context
from mathutils import *
import math
bpy.app.debug=True

########################################################################
# Stitch together vertices to create a face in the arc 
def face(column, row, columns, rows):
   v3 = ((column + 1) * rows) + row
   v3 = (((v3 // 4) * 4) + (v3 + 1) % 4)
   v4 = (column * rows + 1) + row
   v4a =  (((v4 // 4) * 4) + (v4) % 4)
   if v4a == v3:
      v4 = v4a - 4
   return (column* rows + row,
           (column + 1) * rows + row,
           v3,
           v4)

########################################################################
# Create an arc, start and eng angle, a scale to convert units, inner/outer size, height and the number of segments
def arc_segment(nme, start_ang, end_ang, scale, outer_size, inner_size, height, segs):
   # Assuming arguments are in centimeters and in degrees, adjust o meters and radians.
   # Change from degrees to radians
   start_ang = (math.tau / 360.0) * start_ang
   end_ang = (math.tau / 360.0) * end_ang
   if start_ang > end_ang:
      start_ang, end_ang = end_ang, start_ang

   # Change from meters (unit of measure in Blender) to centimeters
   outer_size = scale * outer_size
   inner_size = scale * inner_size

   # We're creating the arc, beginning with the newmericly smaller angle.
   if outer_size < inner_size:
      outer_size, inner_size = inner_size, outer_size

   # Arc will be created withe center at 0, 1/2 infront and 1/2 behing X/Y plane
   height = scale * height
   # Use z for "height", center is 0, so 1/2 height away (plus), and 1/2 height close (minus)
   p_height = (height * 0.5)
   m_height = (height * -0.5)

   # Think of the arc as a tube. This loop connects the faces to create the tube.
   verts = []
   for seg in range(0, segs + 1):
      verts.append((math.cos(start_ang + (end_ang - start_ang) * (seg / segs)) * inner_size,
                    math.sin(start_ang + (end_ang - start_ang) * (seg / segs)) * inner_size,    p_height))
      verts.append((math.cos(start_ang + (end_ang - start_ang) * (seg / segs)) * inner_size,
                    math.sin(start_ang + (end_ang - start_ang) * (seg / segs)) * inner_size,    m_height))
      verts.append((math.cos(start_ang + (end_ang - start_ang) * (seg / segs)) * outer_size,
                    math.sin(start_ang + (end_ang - start_ang) * (seg / segs)) * outer_size,    m_height))
      verts.append((math.cos(start_ang + (end_ang - start_ang) * (seg / segs)) * outer_size,
                    math.sin(start_ang + (end_ang - start_ang) * (seg / segs)) * outer_size,    p_height))

   sides = 4
   args = (segs, sides)
   faces = [face(x, y, segs, sides) for x in range(segs) for y in range(sides)]

   # Now cap the ends of the tube
   faces.append((0, 1, 2, 3))
   vbase = segs * sides
   faces.append((vbase + 3, vbase + 2, vbase + 1, vbase))

   # Create object, link it into the scene, make it 'active'.
   mesh_data = bpy.data.meshes.new(nme)  
   mesh_data.from_pydata(verts, [], faces)  
   mesh_data.update() # (calc_edges=True) not needed here  
   
   cube_object = bpy.data.objects.new(nme, mesh_data)  
   
   scene = bpy.context.scene    
   bpy.context.collection.objects.link(cube_object)
   cube_object.select_set(state=True)

########################################################################
# Solids modeling (CSG) take away coliding parts of one shape from another.
def csg(prim, cut, new_name, objects, operation):
   o_prim = objects[prim]
   o_cut = objects[cut]
   cut_work = o_prim.modifiers.new(type="BOOLEAN", name=new_name)
   cut_work.object = o_cut
   cut_work.operation = 'DIFFERENCE'
   o_cut.hide_set(True)                 # Hidden--But will still exist in an STL export.
   # --  --  --  --  --  --  --  --  --  --  --  --  --
   # What calls do I add here to "apply" the "difference" and delete the o_cut object?

   # --  --  --  --  --  --  --  --  --  --  --  --  --
   
########################################################################
#
objects = bpy.data.objects
set_scale = 2.54 * 2
#
# Create some arcs
arc_segment("big_display_hull", 5.0, 90.0, set_scale, 11.5, 8.0, 4.5, 34)
arc_segment("big_hollow_hull", 6.5, 88.5, set_scale, 11.6, 7.7, 4.1, 34)

csg("big_display_hull", "big_hollow_hull", "o1", objects, 'DIFFERENCE')

arc_segment("attach_hulls",        360 + 110, 360 - 110.0, set_scale, 9.0, 8.0, 4.9, 120)
arc_segment("attach_hollow_hulls", 360 + 113, 360 - 113.0, set_scale, 9.2, 7.9, 4.1, 120)
csg("attach_hulls", "attach_hollow_hulls", "o3", objects, 'DIFFERENCE')

arc_segment("small_display_hull", 360 - 10, 360 - 65, set_scale, 11.5, 8.0, 4.5, 22)
arc_segment("small_hollow_hull", 360 - 11.5, 360 - 63.5, set_scale, 11.6, 7.7, 4.1, 22)
csg("small_display_hull", "small_hollow_hull", "o3", objects, 'DIFFERENCE')

Saya mencoba solusi yang disarankan Scripting: Bagaimana cara menambahkan modifer boolean dengan benar ke objek? , setelah memperbaiki masalah "'apply_as' unrecognized", masalah itu berjalan. Namun saya melihat bahwa boolean masih belum diterapkan. Mengingat bahwa blender 2.90.1 sangat berbeda dari v 2.79, dapatkah seseorang memposting contoh python Boolean (CSG) lengkap di mana mesh yang dihasilkan adalah objek 'selesai'? - Selain itu, saya melihat bahwa 2.91 (beta) memiliki sesuatu yang disebut 'pemecah tepat', dapatkah seseorang memposting kode serupa untuk menggunakan "pemecah tepat"?

Jawaban

2 batFINGER Nov 02 2020 at 12:18

Aktifkan ekstra pengembang / gunakan pelengkapan otomatis di konsol.

  • Harap gunakan 4 spasi indent (bukan 3).

  • Seperti yang dicatat, pengubah terapan tidak lagi memiliki properti "apply_as".

  • Untuk pemecah yang tepat, tetapkan solveratribut ke tepat, yang tetap merupakan nilai default. Aktifkan ekstra pengembang di preferensi pengguna untuk mendapatkan keterangan alat yang menampilkan ini, atau gunakan pelengkapan otomatis di konsol.

     >>> C.object.modifiers['o1']
     bpy.data.objects['big_display_hull'].modifiers["o1"]
    
     >>> C.object.modifiers['o1'].solver
     'EXACT'
    
     >>> C.object.modifiers['o1'].solver = 'FOO'
     Traceback (most recent call last):
       File "<blender_console>", line 1, in <module>
     TypeError: bpy_struct: item.attr = val: enum "FOO" not found in ('FAST', 'EXACT')
    
  • Operator mengerjakan objek yang aktif dan dipilih. Telah menimpa operator sedemikian rupa sehingga objek aktifnya adalah prim. Dapat mengatur objek aktif dengan context.view_layer.objects.active = ob(Anda hanya memilih)

  • Operator pengubah yang diterapkan akan gagal secara diam-diam, objek aktif yang dijalankan pengujian tidak memiliki pengubah

     >>> bpy.ops.object.modifier_apply()
     {'CANCELLED'}
    
  • Hapus objek apa pun dengan bpy.data.objects.remove(ob) Ini telah didokumentasikan dengan baik dan dijawab di sini

Perubahan untuk 2.91 menggunakan pemecah tepat. Tidak perlu disetel karena ini adalah default saat pengubah ditambahkan

def csg(prim, cut, new_name, objects, operation):
   o_prim = objects[prim]
   o_cut = objects[cut]
   cut_work = o_prim.modifiers.new(type="BOOLEAN", name=new_name)
   cut_work.object = o_cut
   cut_work.operation = 'DIFFERENCE'
   o_cut.hide_set(True)                 # Hidden--But will still exist in an STL export.
   # --  --  --  --  --  --  --  --  --  --  --  --  --
   # What calls do I add here to "apply" the "difference" and delete the o_cut object?
   #cut_work.solver = 'EXACT' # it's the default anyhow.
   bpy.ops.object.modifier_apply(
      {"object" : o_prim},
      modifier=cut_work.name,
      )
   # --  -

Hasil dari menjalankan dengan perubahan di atas.

Area oranye tinggi adalah pertarungan sengit antara "lambung layar besar" dan "lampirkan lambung"

1 TobyBaden Nov 04 2020 at 15:42

Mengerti!

Tujuan utama sekarang terpecahkan: file STL yang terlihat sama seperti di Blender. Kode yang diperbaiki ada di bawah.

  • Pengubah Boolean DIFFERENCE berjalan dengan sukses - Tampaknya pengubah "diam-diam" bisa gagal. <Ada wawasan untuk mendapatkan info lebih lanjut tentang ini?>
  • Setelah sebuah objek digunakan untuk memotong "material", kode tersebut berhasil menghapusnya sekarang.
  • Kode sekarang menggunakan implikasi bahwa pengubah dibuat "sebagai anak" dari objek yang sedang aktif
import sys
import bpy

D = bpy.data
C = bpy.context
from mathutils import *
import math

bpy.app.debug = True

########################################################################
# Stitch together vertices to create a face in the arc
def face(column, row, columns, rows):
    v3 = ((column + 1) * rows) + row
    v3 = ((v3 // 4) * 4) + (v3 + 1) % 4
    v4 = (column * rows + 1) + row
    v4a = ((v4 // 4) * 4) + (v4) % 4
    if v4a == v3:
        v4 = v4a - 4
    return (column * rows + row, (column + 1) * rows + row, v3, v4)


########################################################################
# Create an arc, start and eng angle, a scale to convert units, inner/outer size, height and the number of segments
def arc_segment(nme, start_ang, end_ang, scale, outer_size, inner_size, height, segs):
    # Assuming arguments are in centimeters and in degrees, adjust o meters and radians.
    # Change from degrees to radians
    start_ang = (math.tau / 360.0) * start_ang
    end_ang = (math.tau / 360.0) * end_ang
    if start_ang > end_ang:
        start_ang, end_ang = end_ang, start_ang

    # Change from meters (unit of measure in Blender) to centimeters
    outer_size = scale * outer_size
    inner_size = scale * inner_size

    # We're creating the arc, beginning with the newmericly smaller angle.
    if outer_size < inner_size:
        outer_size, inner_size = inner_size, outer_size

    # Arc will be created withe center at 0, 1/2 infront and 1/2 behing X/Y plane
    height = scale * height
    # Use z for "height", center is 0, so 1/2 height away (plus), and 1/2 height close (minus)
    p_height = height * 0.5
    m_height = height * -0.5

    # Think of the arc as a tube. This loop connects the faces to create the tube.
    verts = []
    for seg in range(0, segs + 1):
        verts.append(
            (
                math.cos(start_ang + (end_ang - start_ang) * (seg / segs)) * inner_size,
                math.sin(start_ang + (end_ang - start_ang) * (seg / segs)) * inner_size,
                p_height,
            )
        )
        verts.append(
            (
                math.cos(start_ang + (end_ang - start_ang) * (seg / segs)) * inner_size,
                math.sin(start_ang + (end_ang - start_ang) * (seg / segs)) * inner_size,
                m_height,
            )
        )
        verts.append(
            (
                math.cos(start_ang + (end_ang - start_ang) * (seg / segs)) * outer_size,
                math.sin(start_ang + (end_ang - start_ang) * (seg / segs)) * outer_size,
                m_height,
            )
        )
        verts.append(
            (
                math.cos(start_ang + (end_ang - start_ang) * (seg / segs)) * outer_size,
                math.sin(start_ang + (end_ang - start_ang) * (seg / segs)) * outer_size,
                p_height,
            )
        )

    sides = 4
    args = (segs, sides)
    faces = [face(x, y, segs, sides) for x in range(segs) for y in range(sides)]

    # Now cap the ends of the tube
    faces.append((0, 1, 2, 3))
    vbase = segs * sides
    faces.append((vbase + 3, vbase + 2, vbase + 1, vbase))

    # Create object, link it into the scene, make it 'active'.
    mesh_data = bpy.data.meshes.new(nme)
    mesh_data.from_pydata(verts, [], faces)
    mesh_data.update()  # (calc_edges=True) not needed here

    cube_object = bpy.data.objects.new(nme, mesh_data)

    scene = bpy.context.scene
    bpy.context.collection.objects.link(cube_object)
    cube_object.select_set(state=True)


########################################################################
# Solids modeling (CSG) take away coliding parts of one shape from another.
# The main corrections (the "answer") are in this method here:
#
def csg(prim, cut, new_name, objects, operation):
    o_prim = objects[prim]
    o_cut = objects[cut]
    bpy.context.view_layer.objects.active = o_prim   #Setting the primary object as active.
    cut_work = o_prim.modifiers.new(type="BOOLEAN", name=new_name)
    cut_work.object = o_cut
    cut_work.operation = "DIFFERENCE"
    o_cut.hide_set(True)  # Hidden--But will still exist in an STL export.
    rs = bpy.ops.object.modifier_apply({"object": cut_work.object}, modifier=cut_work.name)
    print(rs)   # If this quietly fails, this prints {'CANCELLED'}. How can one know what to fix? Maybe logs or something that can be looked at to find out why?


########################################################################
#
objects = bpy.data.objects
set_scale = 2.54 * 2
#
# Create some arcs
arc_segment("big_display_hull", 5.0, 90.0, set_scale, 11.5, 8.0, 4.5, 34)
arc_segment("big_hollow_hull", 6.5, 88.5, set_scale, 11.6, 7.7, 4.1, 34)

csg("big_display_hull", "big_hollow_hull", "o1", objects, "DIFFERENCE")

arc_segment("attach_hulls", 360 + 110, 360 - 110.0, set_scale, 9.0, 8.0, 4.9, 120)
arc_segment(
    "attach_hollow_hulls", 360 + 113, 360 - 113.0, set_scale, 9.2, 7.9, 4.1, 120
)
csg("attach_hulls", "attach_hollow_hulls", "o2", objects, "DIFFERENCE")

arc_segment("small_display_hull", 360 - 10, 360 - 65, set_scale, 11.5, 8.0, 4.5, 22)
arc_segment("small_hollow_hull", 360 - 11.5, 360 - 63.5, set_scale, 11.6, 7.7, 4.1, 22)
csg("small_display_hull", "small_hollow_hull", "o3", objects, "DIFFERENCE")

```