2.91 Python-API Keine Änderung nach Anwendung des booleschen Modifikatoroperators

Oct 06 2020

Wie 'wende' ich die Boolesche Differenzoperation an ? Ich habe Python geschrieben, das visuell ein hohles Objekt erzeugt, aber tatsächlich nicht hohl ist. Optisch bekomme ich das Richtige:

Beim Exportieren in STL sind weiterhin beide Objekte vorhanden (keine leere Shell). Ich kann die Anwendung der Booleschen Operation manuell durch "Anwenden" abschließen.

und Löschen des inneren Objekts. Dann ist der Export in die STL-Datei korrekt.

Hier ist der Code, den ich geschrieben habe. Kann mir jemand mitteilen, welche Aufrufe ich ausführen kann, um die boolesche Modifikatoraktion anzuwenden und das Objekt zu löschen, mit dem ich in das andere Objekt geschnitten habe?

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')

Ich habe eine vorgeschlagene Lösung ausprobiert. Scripting: Wie füge ich einem Objekt einen booleschen Modifikator korrekt hinzu? Nachdem ein Problem mit "'apply_as' nicht erkannt" behoben wurde, wurde es ausgeführt. Ich sehe jedoch, dass der Boolesche Wert immer noch nicht angewendet wird. Kann jemand ein vollständiges Python-Boolesches Beispiel (CSG) veröffentlichen, in dem die resultierenden Netze die "fertigen" Objekte sind, da sich Blender 2.90.1 so stark von Version 2.79 unterscheidet? - Außerdem habe ich gesehen, dass 2.91 (Beta) etwas hat, das als "exakter Löser" bezeichnet wird. Könnte jemand einen ähnlichen Code posten, um den "exakten Löser" zu verwenden?

Antworten

2 batFINGER Nov 02 2020 at 12:18

Aktivieren Sie Entwickler-Extras / verwenden Sie die automatische Vervollständigung in der Konsole.

  • Bitte verwenden Sie 4 Leerzeichen (nicht 3).

  • Wie bereits erwähnt, hat der Apply-Modifikator keine "apply_as" -Eigenschaft mehr.

  • Setzen Sie für den genauen Löser das solverAttribut auf genau, was ohnehin der Standardwert ist. Aktivieren Sie Entwickler-Extras in den Benutzereinstellungen, um einen Tooltip anzuzeigen, der dies anzeigt, oder verwenden Sie die automatische Vervollständigung in der Konsole.

     >>> 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')
    
  • Operatoren arbeiten an den aktiven und ausgewählten Objekten. Habe den Operator so überschrieben, dass das aktive Objekt der Prim ist. Kann das aktive Objekt mit setzen context.view_layer.objects.active = ob(Sie haben nur ausgewählt)

  • Der Operator zum Anwenden des Modifikators schlägt stillschweigend fehl. Das aktive Objekt für den Testlauf enthält keine Modifikatoren

     >>> bpy.ops.object.modifier_apply()
     {'CANCELLED'}
    
  • Entfernen Sie alle Objekte mit bpy.data.objects.remove(ob) Dies wurde hier gut dokumentiert und beantwortet

Änderungen für 2.91 mit genauem Löser. Es muss nicht festgelegt werden, da dies die Standardeinstellung ist, wenn der Modifikator hinzugefügt wird

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,
      )
   # --  -

Ergebnis des Laufens mit den obigen Änderungen.

Der hohe orangefarbene Bereich kämpft zwischen "Big Display Hull" und "Attach Hulls".

1 TobyBaden Nov 04 2020 at 15:42

Habe es!

Das Hauptziel ist jetzt gelöst: eine STL-Datei, die genauso aussieht wie in Blender. Der korrigierte Code ist unten.

  • Der Modifikator Boolean DIFFERENCE wird erfolgreich ausgeführt - Anscheinend kann er "leise" fehlschlagen. <Gibt es einen Einblick in weitere Informationen dazu?>
  • Sobald ein Objekt zum Ausschneiden von "Material" verwendet wird, löscht der Code es jetzt erfolgreich.
  • Code verwendet jetzt die Implikation, dass ein Modifikator "als untergeordnetes Element" des aktuell aktiven Objekts erstellt wird
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")

```