2026-03-11_4

This commit is contained in:
2026-03-17 15:34:28 -06:00
parent 9706bc055f
commit eef5547a2c
474 changed files with 113268 additions and 27500 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,979 @@
# SPDX-FileCopyrightText: 2016-2022 Blender Foundation
#
# SPDX-License-Identifier: GPL-2.0-or-later
# authors: dudecon, jambay
# This module contains the UI definition, display,
# and processing (create mesh) functions.
# The routines to generate the vertices for the wall
# are found in the "Blocks" module.
import bpy
from bpy.types import Operator
from bpy.props import (
BoolProperty,
FloatProperty,
StringProperty,
)
from .Blocks import (
NOTZERO, PI,
dims,
settings,
shelfSpecs,
stepSpecs,
createWall,
radialized,
slope,
openingSpecs,
bigBlock,
shelfExt,
stepMod,
stepLeft,
shelfBack,
stepOnly,
stepBack,
)
from bpy_extras import object_utils
from .interface import draw_transform_props
class add_mesh_wallb(Operator, object_utils.AddObjectHelper):
bl_idname = "mesh.wall_add"
bl_label = "Add a Masonry Wall"
bl_description = "Create a block (masonry) wall mesh"
bl_options = {'REGISTER', 'UNDO'}
# UI items - API for properties - User accessible variables...
# not all options are via UI, and some operations just don't work yet
Wall : BoolProperty(name = "Wall",
default = True,
description = "Wall")
#### change properties
name : StringProperty(name = "Name",
description = "Name")
change : BoolProperty(name = "Change",
default = False,
description = "change Wall")
# only create object when True
# False allows modifying several parameters without creating object
ConstructTog: BoolProperty(
name="Construct",
description="Generate the object",
default=True
)
# need to modify so radial makes a tower (normal);
# want "flat" setting to make disk (alternate)
# make the wall circular - if not sloped it's a flat disc
RadialTog: BoolProperty(
name="Radial",
description="Make masonry radial",
default=False
)
# curve the wall - if radial creates dome.
SlopeTog: BoolProperty(
name="Curved",
description="Make masonry sloped, or curved",
default=False
)
# need to review defaults and limits for all of these UI objects
# wall area/size
WallStart: FloatProperty(
name="Start",
description="Left side, or start angle",
default=-10.0,
min=-100, max=100.0
)
WallEnd: FloatProperty(
name="End",
description="Right side, or end angle",
default=10.0,
min=0.0, max=100.0
)
WallBottom: FloatProperty(
name="Bottom",
description="Lower height or radius",
default=0.0,
min=-100, max=100
)
WallTop: FloatProperty(
name="Top",
description="Upper height or radius",
default=15.0,
min=0.0, max=100.0
)
EdgeOffset: FloatProperty(
name="Edging",
description="Block staggering on wall sides",
default=0.6, min=0.0, max=100.0
)
# block sizing
Width: FloatProperty(
name="Width",
description="Average width of each block",
default=1.5,
min=0.01, max=100.0
)
WidthVariance: FloatProperty(
name="Variance",
description="Random variance of block width",
default=0.5,
min=0.0, max=100.0
)
WidthMinimum: FloatProperty(
name="Minimum",
description="Absolute minimum block width",
default=0.5,
min=0.01, max=100.0
)
Height: FloatProperty(
name="Height",
description="Average Height of each block",
default=0.7,
min=0.01, max=100.0
)
HeightVariance: FloatProperty(
name="Variance",
description="Random variance of block Height",
default=0.3,
min=0.0, max=100.0
)
HeightMinimum: FloatProperty(
name="Minimum",
description="Absolute minimum block Height",
default=0.25,
min=0.01, max=100.0
)
Depth: FloatProperty(
name="Depth",
description="Average Depth of each block",
default=2.0,
min=0.01, max=100.0
)
DepthVariance: FloatProperty(
name="Variance",
description="Random variance of block Depth",
default=0.1,
min=0.0, max=100.0
)
DepthMinimum: FloatProperty(
name="Minimum",
description="Absolute minimum block Depth",
default=1.0,
min=0.01, max=100.0
)
MergeBlock: BoolProperty(
name="Merge Blocks",
description="Make big blocks (merge closely adjoining blocks)",
default=False
)
# edging for blocks
Grout: FloatProperty(
name="Thickness",
description="Distance between blocks",
default=0.1,
min=-10.0, max=10.0
)
GroutVariance: FloatProperty(
name="Variance",
description="Random variance of block Grout",
default=0.03,
min=0.0, max=100.0
)
GroutDepth: FloatProperty(
name="Depth",
description="Grout Depth from the face of the blocks",
default=0.1,
min=0.0001, max=10.0
)
GroutDepthVariance: FloatProperty(
name="Variance",
description="Random variance of block Grout Depth",
default=0.03,
min=0.0, max=100.0
)
GroutEdge: BoolProperty(
name="Edging",
description="Grout perimiter",
default=False
)
# properties for openings
Opening1Tog: BoolProperty(
name="Opening(s)",
description="Make windows or doors",
default=True
)
Opening1Width: FloatProperty(
name="Width",
description="The Width of the first opening",
default=2.5,
min=0.01, max=100.0
)
Opening1Height: FloatProperty(
name="Height",
description="The Height of the first opening",
default=3.5,
min=0.01, max=100.0
)
Opening1X: FloatProperty(
name="Indent",
description="The x position or spacing of the first opening",
default=5.0,
min=-100, max=100.0
)
Opening1Z: FloatProperty(
name="Bottom",
description="The z position of the First opening",
default=5.0,
min=-100, max=100.0
)
Opening1Repeat: BoolProperty(
name="Repeat",
description="make multiple openings, with spacing X1",
default=False
)
Opening1TopArchTog: BoolProperty(
name="Top Arch",
description="Add an arch to the top of the first opening",
default=True
)
Opening1TopArch: FloatProperty(
name="Curve",
description="Height of the arch on the top of the opening",
default=2.5,
min=0.001, max=100.0
)
Opening1TopArchThickness: FloatProperty(
name="Thickness",
description="Thickness of the arch on the top of the opening",
default=0.75,
min=0.001, max=100.0
)
Opening1BtmArchTog: BoolProperty(
name="Bottom Arch",
description="Add an arch to the bottom of opening 1",
default=False
)
Opening1BtmArch: FloatProperty(
name="Curve",
description="Height of the arch on the bottom of the opening",
default=1.0,
min=0.01, max=100.0
)
Opening1BtmArchThickness: FloatProperty(
name="Thickness",
description="Thickness of the arch on the bottom of the opening",
default=0.5,
min=0.01, max=100.0
)
Opening1Bevel: FloatProperty(
name="Bevel",
description="Angle block face",
default=0.25,
min=-10.0, max=10.0
)
# openings on top of wall
CrenelTog: BoolProperty(
name="Crenels",
description="Make openings along top of wall",
default=False
)
CrenelXP: FloatProperty(
name="Width",
description="Gap width in wall based the percentage of wall width",
default=0.25,
min=0.10, max=1.0,
subtype="PERCENTAGE"
)
CrenelZP: FloatProperty(
name="Height",
description="Crenel Height as the percentage of wall height",
default=0.10,
min=0.10, max=1.0,
subtype="PERCENTAGE"
)
# narrow openings in wall.
# need to prevent overlap with arch openings - though inversion is an interesting effect.
SlotTog: BoolProperty(
name="Slots",
description="Make narrow openings in wall",
default=False
)
SlotRpt: BoolProperty(
name="Repeat",
description="Repeat slots along wall",
default=False
)
SlotWdg: BoolProperty(
name="Wedged (n/a)",
description="Bevel edges of slots",
default=False
)
SlotX: FloatProperty(
name="Indent",
description="The x position or spacing of slots",
default=0.0, min=-100, max=100.0
)
SlotGap: FloatProperty(
name="Opening",
description="The opening size of slots",
default=0.5, min=0.10, max=100.0
)
SlotV: BoolProperty(
name="Vertical",
description="Vertical slots",
default=True
)
SlotVH: FloatProperty(
name="Height",
description="Height of vertical slot",
default=3.5,
min=0.10, max=100.0
)
SlotVBtm: FloatProperty(
name="Bottom",
description="Z position for slot",
default=5.00,
min=-100.0, max=100.0
)
SlotH: BoolProperty(
name="Horizontal",
description="Horizontal slots",
default=False
)
SlotHW: FloatProperty(
name="Width",
description="Width of horizontal slot",
default=2.5,
min=0.10, max=100.0
)
# this should offset from VBtm... maybe make a % like crenels?
SlotHBtm: FloatProperty(
name="Bottom",
description="Z position for horizontal slot",
default=5.50,
min=-100.0, max=100.0
)
# properties for shelf (extend blocks in area)
ShelfTog: BoolProperty(
name="Shelf",
description="Add blocks in area by depth to make shelf/platform",
default=False
)
ShelfX: FloatProperty(
name="Left",
description="The x position of Shelf",
default=-5.00,
min=-100, max=100.0
)
ShelfZ: FloatProperty(
name="Bottom",
description="The z position of Shelf",
default=10.0,
min=-100, max=100.0
)
ShelfH: FloatProperty(
name="Height",
description="The Height of Shelf area",
default=1.0,
min=0.01, max=100.0
)
ShelfW: FloatProperty(
name="Width",
description="The Width of shelf area",
default=5.0,
min=0.01, max=100.0
)
ShelfD: FloatProperty(
name="Depth",
description="Depth of each block for shelf (from cursor + 1/2 wall depth)",
default=2.0,
min=0.01, max=100.0
)
ShelfBack: BoolProperty(
name="Backside",
description="Shelf on backside of wall",
default=False
)
# properties for steps (extend blocks in area, progressive width)
StepTog: BoolProperty(
name="Steps",
description="Add blocks in area by depth with progressive width to make steps",
default=False
)
StepX: FloatProperty(
name="Left",
description="The x position of steps",
default=-9.00,
min=-100, max=100.0
)
StepZ: FloatProperty(
name="Bottom",
description="The z position of steps",
default=0.0,
min=-100, max=100.0
)
StepH: FloatProperty(
name="Height",
description="The Height of step area",
default=10.0,
min=0.01, max=100.0
)
StepW: FloatProperty(
name="Width",
description="The Width of step area",
default=8.0,
min=0.01, max=100.0
)
StepD: FloatProperty(
name="Depth",
description="Depth of each block for steps (from cursor + 1/2 wall depth)",
default=1.0,
min=0.01, max=100.0
)
StepV: FloatProperty(
name="Riser",
description="Height of each step",
default=0.70,
min=0.01, max=100.0
)
StepT: FloatProperty(
name="Tread",
description="Width of each step",
default=1.0,
min=0.01, max=100.0
)
StepLeft: BoolProperty(
name="Direction",
description="If checked, flip steps direction towards the -X axis",
default=False
)
StepOnly: BoolProperty(
name="Steps Only",
description="Steps only, no supporting blocks",
default=False
)
StepBack: BoolProperty(
name="Backside",
description="Steps on backside of wall",
default=False
)
# Display the toolbox options
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
# Wall shape modifiers
layout.prop(self, 'ConstructTog')
# Wall area (size/position)
header, panel = layout.panel("WALLFACTORY_PT_AREA", default_closed=False)
header.label(text="Wall Size")
if panel:
panel.prop(self, "RadialTog")
panel.prop(self, "SlopeTog")
col = panel.column(align=True)
col.prop(self, "WallStart")
col.prop(self, "WallEnd")
col = panel.column(align=True)
col.prop(self, "WallBottom")
col.prop(self, "WallTop")
panel.prop(self, "EdgeOffset")
# Wall block sizing
header, panel = layout.panel("WALLFACTORY_PT_BLOCKS", default_closed=False)
header.label(text="Block Size")
if panel:
panel.prop(self, "MergeBlock")
# add checkbox for "fixed" sizing (ignore variance) a.k.a. bricks
col = panel.column(align=True)
col.prop(self, "Width")
col.prop(self, "WidthVariance")
col.prop(self, "WidthMinimum")
col = panel.column(align=True)
col.prop(self, "Height")
col.prop(self, "HeightVariance")
col.prop(self, "HeightMinimum")
col = panel.column(align=True)
col.prop(self, "Depth")
col.prop(self, "DepthVariance")
col.prop(self, "DepthMinimum")
# grout settings
header, panel = layout.panel("WALLFACTORY_PT_GROUT", default_closed=True)
header.label(text="Grout")
if panel:
col = panel.column(align=True)
col.prop(self, "Grout")
col.prop(self, "GroutVariance")
col = panel.column(align=True)
col.prop(self, "GroutDepth")
col.prop(self, "GroutDepthVariance")
# Openings (doors, windows; arched)
header, panel = layout.panel("WALLFACTORY_PT_OPENINGS", default_closed=True)
header.use_property_split = False
header.prop(self, 'Opening1Tog', text='')
header.label(text="Openings")
if panel:
openings_col = panel.column()
openings_col.enabled = self.Opening1Tog
openings_col.use_property_split = True
col = openings_col.column(align=True)
col.prop(self, "Opening1Width")
col.prop(self, "Opening1Height")
col.prop(self, "Opening1X")
col.prop(self, "Opening1Z")
col.prop(self, "Opening1Bevel")
openings_col.prop(self, "Opening1Repeat", toggle=True)
openings_col.prop(self, "Opening1TopArchTog")
col = openings_col.column(align=True)
col.enabled = self.Opening1TopArchTog
col.prop(self, "Opening1TopArch")
col.prop(self, "Opening1TopArchThickness")
openings_col.prop(self, "Opening1BtmArchTog")
col = openings_col.column(align=True)
col.enabled = self.Opening1BtmArchTog
col.prop(self, "Opening1BtmArch")
col.prop(self, "Opening1BtmArchThickness")
# Slots (narrow openings)
header, panel = layout.panel("WALLFACTORY_PT_SLOTS", default_closed=True)
header.use_property_split = False
header.prop(self, 'SlotTog', text='')
header.label(text="Slots")
if panel:
panel.enabled = self.SlotTog
panel.prop(self, "SlotRpt")
col = panel.column(align=True)
col.prop(self, "SlotX")
col.prop(self, "SlotGap")
panel.prop(self, "SlotV")
col = panel.column(align=True)
col.enabled = self.SlotV
col.prop(self, "SlotVH")
col.prop(self, "SlotVBtm")
panel.prop(self, "SlotH")
col = panel.column(align=True)
col.enabled = self.SlotH
col.prop(self, "SlotHW")
col.prop(self, "SlotHBtm")
# Crenels, gaps in top of wall
header, panel = layout.panel("WALLFACTORY_PT_CRENELS", default_closed=True)
header.use_property_split = False
header.prop(self, 'CrenelTog', text='')
header.label(text="Crenels")
if panel:
panel.enabled = self.CrenelTog
col = panel.column(align=True)
col.prop(self, "CrenelXP")
col.prop(self, "CrenelZP")
# Shelfing (protrusions)
header, panel = layout.panel("WALLFACTORY_PT_SHELF", default_closed=True)
header.use_property_split = False
header.prop(self, 'ShelfTog', text='')
header.label(text="Shelf")
if panel:
panel.enabled = self.ShelfTog
col = panel.column(align=True)
col.prop(self, "ShelfX")
col.prop(self, "ShelfZ")
col = panel.column(align=True)
col.prop(self, "ShelfW")
col.prop(self, "ShelfH")
col.prop(self, "ShelfD")
panel.prop(self, "ShelfBack")
# Steps
header, panel = layout.panel("WALLFACTORY_PT_STEPS", default_closed=True)
header.use_property_split = False
header.prop(self, 'StepTog', text='')
header.label(text="Steps")
if panel:
panel.enabled = self.StepTog
col = panel.column(align=True)
col.prop(self, "StepX")
col.prop(self, "StepZ")
col = panel.column(align=True)
col.prop(self, "StepH")
col.prop(self, "StepW")
col.prop(self, "StepD")
col = panel.column(align=True)
col.prop(self, "StepV")
col.prop(self, "StepT")
col = panel.column(align=True)
panel.prop(self, "StepLeft")
panel.prop(self, "StepOnly")
panel.prop(self, "StepBack")
if self.change == False:
header, panel = layout.panel("WALLFACTORY_PT_TRANSFORM", default_closed=True)
header.label(text="Transform")
if panel:
draw_transform_props(self, panel)
# Respond to UI - get the properties set by user.
# Check and process UI settings to generate masonry
def execute(self, context):
global radialized
global slope
global openingSpecs
global bigBlock
global shelfExt
global stepMod
global stepLeft
global shelfBack
global stepOnly
global stepBack
# Create the wall when enabled (skip regen iterations when off)
if not self.ConstructTog:
return {'FINISHED'}
# turn off 'Enter Edit Mode'
use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
bpy.context.preferences.edit.use_enter_edit_mode = False
# enter the settings for the wall dimensions (area)
# start can't be zero - min/max don't matter [if max less than end] but zero don't workie.
# start can't exceed end.
if not self.WallStart or self.WallStart >= self.WallEnd:
self.WallStart = NOTZERO # Reset UI if input out of bounds...
dims['s'] = self.WallStart
dims['e'] = self.WallEnd
dims['b'] = self.WallBottom
dims['t'] = self.WallTop
settings['eoff'] = self.EdgeOffset
# retrieve the settings for the wall block properties
settings['w'] = self.Width
settings['wv'] = self.WidthVariance
settings['wm'] = self.WidthMinimum
if not radialized:
settings['sdv'] = settings['w']
else:
settings['sdv'] = 0.12
settings['h'] = self.Height
settings['hv'] = self.HeightVariance
settings['hm'] = self.HeightMinimum
settings['d'] = self.Depth
settings['dv'] = self.DepthVariance
settings['dm'] = self.DepthMinimum
if self.MergeBlock:
bigBlock = 1
else:
bigBlock = 0
settings['g'] = self.Grout
settings['gv'] = self.GroutVariance
settings['gd'] = self.GroutDepth
settings['gdv'] = self.GroutDepthVariance
if self.GroutEdge:
settings['ge'] = 1
else:
settings['ge'] = 0
# set wall shape modifiers
if self.RadialTog:
radialized = 1
# eliminate to allow user control for start/completion?
dims['s'] = 0.0 # complete radial
if dims['e'] > PI * 2:
dims['e'] = PI * 2 # max end for circle
if dims['b'] < settings['g']:
dims['b'] = settings['g'] # min bottom for grout extension
else:
radialized = 0
if self.SlopeTog:
slope = 1
else:
slope = 0
shelfExt = 0
shelfBack = 0
# Add shelf if enabled
if self.ShelfTog:
shelfExt = 1
shelfSpecs['h'] = self.ShelfH
shelfSpecs['w'] = self.ShelfW
shelfSpecs['d'] = self.ShelfD
shelfSpecs['x'] = self.ShelfX
shelfSpecs['z'] = self.ShelfZ
if self.ShelfBack:
shelfBack = 1
stepMod = 0
stepLeft = 0
stepOnly = 0
stepBack = 0
# Make steps if enabled
if self.StepTog:
stepMod = 1
stepSpecs['x'] = self.StepX
stepSpecs['z'] = self.StepZ
stepSpecs['h'] = self.StepH
stepSpecs['w'] = self.StepW
stepSpecs['d'] = self.StepD
stepSpecs['v'] = self.StepV
stepSpecs['t'] = self.StepT
if self.StepLeft:
stepLeft = 1
if self.StepOnly:
stepOnly = 1
if self.StepBack:
stepBack = 1
# enter the settings for the openings
# when openings overlap they create inverse stonework - interesting but not the desired effect :)
# if opening width == indent * 2 the edge blocks fail (row of blocks cross opening) - bug.
openingSpecs = []
openingIdx = 0 # track opening array references for multiple uses
# general openings with arch options - can be windows or doors.
if self.Opening1Tog:
# set defaults...
openingSpecs += [{'w': 0.5, 'h': 0.5, 'x': 0.8, 'z': 2.7, 'rp': 1,
'b': 0.0, 'v': 0, 'vl': 0, 't': 0, 'tl': 0}]
openingSpecs[openingIdx]['w'] = self.Opening1Width
openingSpecs[openingIdx]['h'] = self.Opening1Height
openingSpecs[openingIdx]['x'] = self.Opening1X
openingSpecs[openingIdx]['z'] = self.Opening1Z
openingSpecs[openingIdx]['rp'] = self.Opening1Repeat
if self.Opening1TopArchTog:
openingSpecs[openingIdx]['v'] = self.Opening1TopArch
openingSpecs[openingIdx]['t'] = self.Opening1TopArchThickness
if self.Opening1BtmArchTog:
openingSpecs[openingIdx]['vl'] = self.Opening1BtmArch
openingSpecs[openingIdx]['tl'] = self.Opening1BtmArchThickness
openingSpecs[openingIdx]['b'] = self.Opening1Bevel
openingIdx += 1 # count window/door/arch openings
# Slots (narrow openings)
if self.SlotTog:
if self.SlotV: # vertical slots
# set defaults...
openingSpecs += [{'w': 0.5, 'h': 0.5, 'x': 0.0, 'z': 2.7, 'rp': 0,
'b': 0.0, 'v': 0, 'vl': 0, 't': 0, 'tl': 0}]
openingSpecs[openingIdx]['w'] = self.SlotGap
openingSpecs[openingIdx]['h'] = self.SlotVH
openingSpecs[openingIdx]['x'] = self.SlotX
openingSpecs[openingIdx]['z'] = self.SlotVBtm
openingSpecs[openingIdx]['rp'] = self.SlotRpt
# make them pointy...
openingSpecs[openingIdx]['v'] = self.SlotGap
openingSpecs[openingIdx]['t'] = self.SlotGap / 2
openingSpecs[openingIdx]['vl'] = self.SlotGap
openingSpecs[openingIdx]['tl'] = self.SlotGap / 2
openingIdx += 1 # count vertical slot openings
# need to handle overlap of H and V slots...
if self.SlotH: # Horizontal slots
# set defaults...
openingSpecs += [{'w': 0.5, 'h': 0.5, 'x': 0.0, 'z': 2.7, 'rp': 0,
'b': 0.0, 'v': 0, 'vl': 0, 't': 0, 'tl': 0}]
openingSpecs[openingIdx]['w'] = self.SlotHW
openingSpecs[openingIdx]['h'] = self.SlotGap
openingSpecs[openingIdx]['x'] = self.SlotX
openingSpecs[openingIdx]['z'] = self.SlotHBtm
# horizontal repeat isn't same spacing as vertical...
openingSpecs[openingIdx]['rp'] = self.SlotRpt
# make them pointy...
openingIdx += 1 # count horizontal slot openings
# Crenellations (top row openings)
if self.CrenelTog:
# add bottom arch option?
# perhaps a repeat toggle...
# if crenel opening overlaps with arch opening it fills with blocks...
# set defaults...
openingSpecs += [{'w': 0.5, 'h': 0.5, 'x': 0.0, 'z': 2.7, 'rp': 1,
'b': 0.0, 'v': 0, 'vl': 0, 't': 0, 'tl': 0}]
wallW = self.WallEnd - self.WallStart
crenelW = wallW * self.CrenelXP # Width % opening.
wallH = self.WallTop - self.WallBottom
crenelH = wallH * self.CrenelZP # % proportional height.
openingSpecs[openingIdx]['w'] = crenelW
openingSpecs[openingIdx]['h'] = crenelH
# calculate the spacing between openings.
# this isn't the absolute start (left),
# it's opening center offset relative to cursor (space between openings)...
openingSpecs[openingIdx]['x'] = crenelW * 2 - 1 # assume standard spacing
if not radialized: # normal wall?
# set indent 0 (center) if opening is 50% or more of wall width, no repeat.
if crenelW * 2 >= wallW:
openingSpecs[openingIdx]['x'] = 0
openingSpecs[openingIdx]['rp'] = 0
# set bottom of opening (center of hole)
openingSpecs[openingIdx]['z'] = self.WallTop - (crenelH / 2)
openingIdx += 1 # count crenel openings
# Process the user settings to generate a wall
# generate the list of vertices for the wall...
verts_array, faces_array = createWall(
radialized, slope, openingSpecs, bigBlock,
shelfExt, shelfBack, stepMod, stepLeft, stepOnly,
stepBack
)
if bpy.context.mode == "OBJECT":
if context.selected_objects != [] and context.active_object and \
(context.active_object.data is not None) and ('Wall' in context.active_object.data.keys()) and \
(self.change == True):
obj = context.active_object
oldmesh = obj.data
oldmeshname = obj.data.name
mesh = bpy.data.meshes.new("Wall")
mesh.from_pydata(verts_array, [], faces_array)
obj.data = mesh
for material in oldmesh.materials:
obj.data.materials.append(material)
bpy.data.meshes.remove(oldmesh)
obj.data.name = oldmeshname
else:
mesh = bpy.data.meshes.new("Wall")
mesh.from_pydata(verts_array, [], faces_array)
obj = object_utils.object_data_add(context, mesh, operator=self)
mesh.update()
obj.data["Wall"] = True
obj.data["change"] = False
for prm in WallParameters():
obj.data[prm] = getattr(self, prm)
if bpy.context.mode == "EDIT_MESH":
active_object = context.active_object
name_active_object = active_object.name
bpy.ops.object.mode_set(mode='OBJECT')
mesh = bpy.data.meshes.new("TMP")
mesh.from_pydata(verts_array, [], faces_array)
obj = object_utils.object_data_add(context, mesh, operator=self)
obj.select_set(True)
active_object.select_set(True)
bpy.context.view_layer.objects.active = active_object
bpy.ops.object.join()
context.active_object.name = name_active_object
bpy.ops.object.mode_set(mode='EDIT')
if use_enter_edit_mode:
bpy.ops.object.mode_set(mode = 'EDIT')
# restore pre operator state
bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
return {'FINISHED'}
def WallParameters():
WallParameters = [
"ConstructTog",
"RadialTog",
"SlopeTog",
"WallStart",
"WallEnd",
"WallBottom",
"WallTop",
"EdgeOffset",
"Width",
"WidthVariance",
"WidthMinimum",
"Height",
"HeightVariance",
"HeightMinimum",
"Depth",
"DepthVariance",
"DepthMinimum",
"MergeBlock",
"Grout",
"GroutVariance",
"GroutDepth",
"GroutDepthVariance",
"GroutEdge",
"Opening1Tog",
"Opening1Width",
"Opening1Height",
"Opening1X",
"Opening1Z",
"Opening1Repeat",
"Opening1TopArchTog",
"Opening1TopArch",
"Opening1TopArchThickness",
"Opening1BtmArchTog",
"Opening1BtmArch",
"Opening1BtmArchThickness",
"CrenelTog",
"CrenelXP",
"CrenelZP",
"SlotTog",
"SlotRpt",
"SlotWdg",
"SlotX",
"SlotGap",
"SlotV",
"SlotVH",
"SlotVBtm",
"SlotH",
"SlotHW",
"SlotHBtm",
"ShelfTog",
"ShelfX",
"ShelfZ",
"ShelfH",
"ShelfW",
"ShelfD",
"ShelfBack",
"StepTog",
"StepX",
"StepZ",
"StepH",
"StepW",
"StepD",
"StepV",
"StepT",
"StepLeft",
"StepOnly",
"StepBack",
]
return WallParameters
@@ -0,0 +1,460 @@
# SPDX-FileCopyrightText: 2011-2023 Blender Foundation
#
# SPDX-License-Identifier: GPL-2.0-or-later
# Contributed to by:
# Pontiac, Fourmadmen, varkenvarken, tuga3d, meta-androcto, metalliandy #
# dreampainter, cotejrp1, liero, Kayo Phoenix, sugiany, dommetysk, Jambay #
# Phymec, Anthony D'Agostino, Pablo Vazquez, Richard Wilks, lijenstina, #
# Sjaak-de-Draak, Phil Cote, cotejrp1, xyz presets by elfnor, revolt_randy, #
# Vladimir Spivak (cwolf3d), Jonathan Lampel #
# Note: Blocks has to be loaded before the WallFactory or the script
# will not work properly after (F8) reload
if "bpy" in locals():
import importlib
importlib.reload(add_mesh_star)
importlib.reload(add_mesh_twisted_torus)
importlib.reload(add_mesh_gemstones)
importlib.reload(add_mesh_gears)
importlib.reload(add_mesh_3d_function_surface)
importlib.reload(add_mesh_round_cube)
importlib.reload(add_mesh_supertoroid)
importlib.reload(add_mesh_pyramid)
importlib.reload(add_mesh_torusknot)
importlib.reload(add_mesh_honeycomb)
importlib.reload(add_mesh_teapot)
importlib.reload(add_mesh_pipe_joint)
importlib.reload(add_mesh_solid)
importlib.reload(add_mesh_round_brilliant)
importlib.reload(add_mesh_menger_sponge)
importlib.reload(add_mesh_vertex)
importlib.reload(add_empty_as_parent)
importlib.reload(add_mesh_beam_builder)
importlib.reload(Blocks)
importlib.reload(Wallfactory)
importlib.reload(add_mesh_triangles)
importlib.reload(preferences)
else:
from . import add_mesh_star
from . import add_mesh_twisted_torus
from . import add_mesh_gemstones
from . import add_mesh_gears
from . import add_mesh_3d_function_surface
from . import add_mesh_round_cube
from . import add_mesh_supertoroid
from . import add_mesh_pyramid
from . import add_mesh_torusknot
from . import add_mesh_honeycomb
from . import add_mesh_teapot
from . import add_mesh_pipe_joint
from . import add_mesh_solid
from . import add_mesh_round_brilliant
from . import add_mesh_menger_sponge
from . import add_mesh_vertex
from . import add_empty_as_parent
from . import add_mesh_beam_builder
from . import Blocks
from . import Wallfactory
from . import add_mesh_triangles
from . import preferences
from .add_mesh_rocks import __init__
from .add_mesh_rocks import rockgen
import bpy
from bpy.types import Menu
class VIEW3D_MT_mesh_vert_add(Menu):
# Define the "Single Vert" menu
bl_idname = "VIEW3D_MT_mesh_vert_add"
bl_label = "Single Vert"
def draw(self, context):
layout = self.layout
layout.operator_context = 'INVOKE_REGION_WIN'
layout.operator("mesh.primitive_vert_add",
text="Add Single Vert")
layout.separator()
layout.operator("mesh.primitive_emptyvert_add",
text="Object Origin Only")
layout.operator("mesh.primitive_symmetrical_vert_add",
text="Origin & Vert Mirrored")
layout.operator("mesh.primitive_symmetrical_empty_add",
text="Object Origin Mirrored")
class VIEW3D_MT_mesh_gears_add(Menu):
# Define the "Gears" menu
bl_idname = "VIEW3D_MT_mesh_gears_add"
bl_label = "Gears"
def draw(self, context):
layout = self.layout
layout.operator_context = 'INVOKE_REGION_WIN'
oper = layout.operator("mesh.primitive_gear", text="Gear")
oper.change = False
oper = layout.operator("mesh.primitive_worm_gear", text="Worm")
oper.change = False
class VIEW3D_MT_mesh_gemstones_add(Menu):
# Define the "Gemstones" menu
bl_idname = "VIEW3D_MT_mesh_gemstones_add"
bl_label = "Gemstones"
def draw(self, context):
layout = self.layout
layout.operator_context = 'INVOKE_REGION_WIN'
oper = layout.operator("mesh.primitive_brilliant_add", text="Brilliant")
oper.change = False
oper = layout.operator("mesh.primitive_diamond_add", text="Diamond")
oper.change = False
oper = layout.operator("mesh.primitive_gem_add", text="Gem")
oper.change = False
class VIEW3D_MT_mesh_math_add(Menu):
# Define the "Math Function" menu
bl_idname = "VIEW3D_MT_mesh_math_add"
bl_label = "Math Functions"
def draw(self, context):
layout = self.layout
layout.operator_context = 'INVOKE_REGION_WIN'
layout.operator("mesh.primitive_z_function_surface",
text="Z Math Surface")
layout.operator("mesh.primitive_xyz_function_surface",
text="XYZ Math Surface")
self.layout.operator("mesh.primitive_solid_add", text="Regular Solid")
self.layout.operator("mesh.make_triangle", text="Triangle")
class VIEW3D_MT_mesh_extras_add(Menu):
# Define the "Extra Objects" menu
bl_idname = "VIEW3D_MT_mesh_extras_add"
bl_label = "Extras"
def draw(self, context):
layout = self.layout
layout.operator_context = 'INVOKE_REGION_WIN'
oper = layout.operator("mesh.add_mesh_rock", text="Rock Generator")
oper = layout.operator("mesh.add_beam", text="Beam Builder")
oper.change = False
oper = layout.operator("mesh.wall_add", text="Wall Factory")
oper.change = False
layout.separator()
oper = layout.operator("mesh.primitive_star_add", text="Simple Star")
oper.change = False
oper = layout.operator("mesh.primitive_steppyramid_add", text="Step Pyramid")
oper.change = False
oper = layout.operator("mesh.honeycomb_add", text="Honeycomb")
oper.change = False
oper = layout.operator("mesh.primitive_teapot_add", text="Teapot+")
oper = layout.operator("mesh.menger_sponge_add", text="Menger Sponge")
class VIEW3D_MT_mesh_torus_add(Menu):
# Define the "Torus Objects" menu
bl_idname = "VIEW3D_MT_mesh_torus_add"
bl_label = "Torus Objects"
def draw(self, context):
layout = self.layout
layout.operator_context = 'INVOKE_REGION_WIN'
oper = layout.operator("mesh.primitive_twisted_torus_add", text="Twisted Torus")
oper.change = False
oper = layout.operator("mesh.primitive_supertoroid_add", text="Supertoroid")
oper.change = False
oper = layout.operator("mesh.primitive_torusknot_add", text="Torus Knot")
oper.change = False
class VIEW3D_MT_mesh_pipe_joints_add(Menu):
# Define the "Pipe Joints" menu
bl_idname = "VIEW3D_MT_mesh_pipe_joints_add"
bl_label = "Pipe Joints"
def draw(self, context):
layout = self.layout
layout.operator_context = 'INVOKE_REGION_WIN'
oper = layout.operator("mesh.primitive_elbow_joint_add", text="Elbow")
oper.change = False
oper = layout.operator("mesh.primitive_tee_joint_add", text="T-Joint")
oper.change = False
oper = layout.operator("mesh.primitive_wye_joint_add", text="Y-Joint")
oper.change = False
oper = layout.operator("mesh.primitive_cross_joint_add", text="Cross-Joint")
oper.change = False
oper = layout.operator("mesh.primitive_n_joint_add", text="N-Joint")
oper.change = False
# Register all operators and panels
# Define "Extras" menu
def menu_func(self, context):
layout = self.layout
layout.operator_context = 'INVOKE_REGION_WIN'
prefs = bpy.context.preferences.addons[__package__].preferences
if prefs.show_round_cube:
oper = layout.operator("mesh.primitive_round_cube_add", text="Round Cube", icon='SPHERE')
oper.change = False
layout.separator()
if prefs.show_single_vert:
layout.menu("VIEW3D_MT_mesh_vert_add", text="Single Vert", icon='DECORATE')
if prefs.show_torus_objects:
layout.menu("VIEW3D_MT_mesh_torus_add", text="Torus Objects", icon='MESH_TORUS')
if prefs.show_math_functions:
layout.menu("VIEW3D_MT_mesh_math_add", text="Math Functions", icon='GRAPH')
if prefs.show_gears:
layout.menu("VIEW3D_MT_mesh_gears_add", text="Gears", icon='PREFERENCES')
if prefs.show_pipe_joints:
layout.menu("VIEW3D_MT_mesh_pipe_joints_add", text="Pipe Joints", icon='IPO_CONSTANT')
if prefs.show_gemstones:
layout.menu("VIEW3D_MT_mesh_gemstones_add", text="Gemstones", icon="MESH_ICOSPHERE")
if prefs.show_extras:
layout.menu("VIEW3D_MT_mesh_extras_add", text="Extras", icon="PACKAGE")
if prefs.show_parent_to_empty:
layout.separator()
layout.operator("object.parent_to_empty", text="Parent to Empty", icon="OUTLINER_OB_EMPTY")
def Extras_contex_menu(self, context):
bl_label = 'Change'
obj = context.object
layout = self.layout
if obj is None or obj.data is None:
return
if 'Gear' in obj.data.keys():
props = layout.operator("mesh.primitive_gear", text="Change Gear")
props.change = True
for prm in add_mesh_gears.GearParameters():
setattr(props, prm, obj.data[prm])
layout.separator()
if 'WormGear' in obj.data.keys():
props = layout.operator("mesh.primitive_worm_gear", text="Change WormGear")
props.change = True
for prm in add_mesh_gears.WormGearParameters():
setattr(props, prm, obj.data[prm])
layout.separator()
if 'Beam' in obj.data.keys():
props = layout.operator("mesh.add_beam", text="Change Beam")
props.change = True
for prm in add_mesh_beam_builder.BeamParameters():
setattr(props, prm, obj.data[prm])
layout.separator()
if 'Wall' in obj.data.keys():
props = layout.operator("mesh.wall_add", text="Change Wall")
props.change = True
for prm in Wallfactory.WallParameters():
setattr(props, prm, obj.data[prm])
layout.separator()
if 'ElbowJoint' in obj.data.keys():
props = layout.operator("mesh.primitive_elbow_joint_add", text="Change ElbowJoint")
props.change = True
for prm in add_mesh_pipe_joint.ElbowJointParameters():
setattr(props, prm, obj.data[prm])
layout.separator()
if 'TeeJoint' in obj.data.keys():
props = layout.operator("mesh.primitive_tee_joint_add", text="Change TeeJoint")
props.change = True
for prm in add_mesh_pipe_joint.TeeJointParameters():
setattr(props, prm, obj.data[prm])
layout.separator()
if 'WyeJoint' in obj.data.keys():
props = layout.operator("mesh.primitive_wye_joint_add", text="Change WyeJoint")
props.change = True
for prm in add_mesh_pipe_joint.WyeJointParameters():
setattr(props, prm, obj.data[prm])
layout.separator()
if 'CrossJoint' in obj.data.keys():
props = layout.operator("mesh.primitive_cross_joint_add", text="Change CrossJoint")
props.change = True
for prm in add_mesh_pipe_joint.CrossJointParameters():
setattr(props, prm, obj.data[prm])
layout.separator()
if 'NJoint' in obj.data.keys():
props = layout.operator("mesh.primitive_n_joint_add", text="Change NJoint")
props.change = True
for prm in add_mesh_pipe_joint.NJointParameters():
setattr(props, prm, obj.data[prm])
layout.separator()
if 'Diamond' in obj.data.keys():
props = layout.operator("mesh.primitive_diamond_add", text="Change Diamond")
props.change = True
for prm in add_mesh_gemstones.DiamondParameters():
setattr(props, prm, obj.data[prm])
layout.separator()
if 'Gem' in obj.data.keys():
props = layout.operator("mesh.primitive_gem_add", text="Change Gem")
props.change = True
for prm in add_mesh_gemstones.GemParameters():
setattr(props, prm, obj.data[prm])
layout.separator()
if 'Brilliant' in obj.data.keys():
props = layout.operator("mesh.primitive_brilliant_add", text="Change Brilliant")
props.change = True
for prm in add_mesh_round_brilliant.BrilliantParameters():
setattr(props, prm, obj.data[prm])
layout.separator()
if 'Roundcube' in obj.data.keys():
props = layout.operator("mesh.primitive_round_cube_add", text="Change Roundcube")
props.change = True
for prm in add_mesh_round_cube.RoundCubeParameters():
setattr(props, prm, obj.data[prm])
layout.separator()
if 'TorusKnot' in obj.data.keys():
props = layout.operator("mesh.primitive_torusknot_add", text="Change TorusKnot")
props.change = True
for prm in add_mesh_torusknot.TorusKnotParameters():
setattr(props, prm, obj.data[prm])
layout.separator()
if 'SuperToroid' in obj.data.keys():
props = layout.operator("mesh.primitive_supertoroid_add", text="Change SuperToroid")
props.change = True
for prm in add_mesh_supertoroid.SuperToroidParameters():
setattr(props, prm, obj.data[prm])
layout.separator()
if 'TwistedTorus' in obj.data.keys():
props = layout.operator("mesh.primitive_twisted_torus_add", text="Change TwistedTorus")
props.change = True
for prm in add_mesh_twisted_torus.TwistedTorusParameters():
setattr(props, prm, obj.data[prm])
layout.separator()
if 'Star' in obj.data.keys():
props = layout.operator("mesh.primitive_star_add", text="Change Star")
props.change = True
for prm in add_mesh_star.StarParameters():
setattr(props, prm, obj.data[prm])
layout.separator()
if 'Pyramid' in obj.data.keys():
props = layout.operator("mesh.primitive_steppyramid_add", text="Change Pyramid")
props.change = True
for prm in add_mesh_pyramid.PyramidParameters():
setattr(props, prm, obj.data[prm])
layout.separator()
if 'HoneyComb' in obj.data.keys():
props = layout.operator("mesh.honeycomb_add", text="Change HoneyComb")
props.change = True
for prm in add_mesh_honeycomb.HoneyCombParameters():
setattr(props, prm, obj.data[prm])
layout.separator()
# Register
classes = [
VIEW3D_MT_mesh_vert_add,
VIEW3D_MT_mesh_gears_add,
VIEW3D_MT_mesh_gemstones_add,
VIEW3D_MT_mesh_math_add,
VIEW3D_MT_mesh_extras_add,
VIEW3D_MT_mesh_torus_add,
VIEW3D_MT_mesh_pipe_joints_add,
add_mesh_star.AddStar,
add_mesh_twisted_torus.AddTwistedTorus,
add_mesh_gemstones.AddDiamond,
add_mesh_gemstones.AddGem,
add_mesh_gears.AddGear,
add_mesh_gears.AddWormGear,
add_mesh_3d_function_surface.AddZFunctionSurface,
add_mesh_3d_function_surface.AddXYZFunctionSurface,
add_mesh_round_cube.AddRoundCube,
add_mesh_supertoroid.add_supertoroid,
add_mesh_pyramid.AddPyramid,
add_mesh_torusknot.AddTorusKnot,
add_mesh_honeycomb.add_mesh_honeycomb,
add_mesh_teapot.AddTeapot,
add_mesh_pipe_joint.AddElbowJoint,
add_mesh_pipe_joint.AddTeeJoint,
add_mesh_pipe_joint.AddWyeJoint,
add_mesh_pipe_joint.AddCrossJoint,
add_mesh_pipe_joint.AddNJoint,
add_mesh_solid.Solids,
add_mesh_round_brilliant.MESH_OT_primitive_brilliant_add,
add_mesh_menger_sponge.AddMengerSponge,
add_mesh_vertex.AddVert,
add_mesh_vertex.AddEmptyVert,
add_mesh_vertex.AddSymmetricalEmpty,
add_mesh_vertex.AddSymmetricalVert,
add_empty_as_parent.P2E,
add_empty_as_parent.PreFix,
add_mesh_beam_builder.addBeam,
Wallfactory.add_mesh_wallb,
add_mesh_triangles.MakeTriangle,
preferences.AddMeshExtraObjectsPreferences,
]
def register():
import os
from bpy.utils import register_class
for cls in classes:
register_class(cls)
add_mesh_rocks.register()
# Add "Extras" menu to the "Add Mesh" menu and context menu.
bpy.types.VIEW3D_MT_mesh_add.append(menu_func)
bpy.types.VIEW3D_MT_object_context_menu.prepend(Extras_contex_menu)
# Part of 4.3 may be back-ported to 4.2.
if register_preset_path := getattr(bpy.utils, "register_preset_path", None):
register_preset_path(os.path.join(os.path.dirname(__file__)))
def unregister():
import os
# Remove "Extras" menu from the "Add Mesh" menu and context menu.
bpy.types.VIEW3D_MT_object_context_menu.remove(Extras_contex_menu)
bpy.types.VIEW3D_MT_mesh_add.remove(menu_func)
from bpy.utils import unregister_class
for cls in reversed(classes):
unregister_class(cls)
add_mesh_rocks.unregister()
# Part of 4.3 may be back-ported to 4.2.
if unregister_preset_path := getattr(bpy.utils, "unregister_preset_path", None):
unregister_preset_path(os.path.join(os.path.dirname(__file__)))
if __name__ == "__main__":
register()
@@ -0,0 +1,133 @@
# SPDX-FileCopyrightText: 2015-2022 Blender Foundation
#
# SPDX-License-Identifier: GPL-2.0-or-later
# Original Author Liero
import bpy
from bpy.types import Operator
from bpy.props import (
StringProperty,
BoolProperty,
EnumProperty,
)
def centro(sel):
x = sum([obj.location[0] for obj in sel]) / len(sel)
y = sum([obj.location[1] for obj in sel]) / len(sel)
z = sum([obj.location[2] for obj in sel]) / len(sel)
return (x, y, z)
class P2E(Operator):
bl_idname = "object.parent_to_empty"
bl_label = "Parent to Empty"
bl_description = "Parent selected objects to a new Empty"
bl_options = {"REGISTER", "UNDO"}
nombre: StringProperty(
name="",
default='OBJECTS',
description='Give the empty / group a name'
)
grupo: BoolProperty(
name="Create Group",
default=False,
description="Also add objects to a group"
)
locat: EnumProperty(
name='',
items=[('CURSOR', 'Cursor', 'Cursor'), ('ACTIVE', 'Active', 'Active'),
('CENTER', 'Center', 'Selection Center')],
description='Empty location',
default='CENTER'
)
renom: BoolProperty(
name="Add Prefix",
default=False,
description="Add prefix to objects name"
)
@classmethod
def poll(cls, context):
objs = context.selected_objects
return (len(objs) > 0)
def draw(self, context):
layout = self.layout
layout.prop(self, "nombre")
column = layout.column(align=True)
column.prop(self, "locat")
column.prop(self, "grupo")
column.prop(self, "renom")
def execute(self, context):
objs = context.selected_objects
act = context.object
sce = context.scene
try:
bpy.ops.object.mode_set()
except:
pass
if self.locat == 'CURSOR':
loc = sce.cursor.location
elif self.locat == 'ACTIVE':
loc = act.location
else:
loc = centro(objs)
bpy.ops.object.add(type='EMPTY', location=loc)
context.object.name = self.nombre
context.object.show_name = True
context.object.show_in_front = True
if self.grupo:
bpy.ops.collection.create(name=self.nombre)
bpy.ops.collection.objects_add_active()
for o in objs:
o.select_set(True)
if not o.parent:
bpy.ops.object.parent_set(type='OBJECT')
if self.grupo:
bpy.ops.collection.objects_add_active()
o.select_set(False)
for o in objs:
if self.renom:
o.name = self.nombre + '_' + o.name
return {'FINISHED'}
class PreFix(Operator):
bl_idname = "object.toggle_prefix"
bl_label = "Toggle Sufix"
bl_description = "Toggle parent name as sufix for c"
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
act = context.object
return (act and act.type == 'EMPTY')
def execute(self, context):
act = context.object
objs = act.children
prefix = act.name + '_'
remove = False
for o in objs:
if o.name.startswith(prefix):
remove = True
break
if remove is True:
for o in objs:
if o.name.startswith(prefix):
o.name = o.name.partition(prefix)[2]
else:
for o in objs:
o.name = prefix + o.name
return {'FINISHED'}
@@ -0,0 +1,612 @@
# SPDX-FileCopyrightText: 2010-2022 Blender Foundation
#
# SPDX-License-Identifier: GPL-2.0-or-later
# Original by Buerbaum Martin (Pontiac), Elod Csirmaz
import bpy
import math
import numpy
from mathutils import *
from math import *
from bpy.types import Operator
from bpy.props import (
StringProperty,
IntProperty,
FloatProperty,
BoolProperty,
)
# List of safe functions for eval()
safe_list = ['acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'cosh',
'degrees', 'e', 'exp', 'fabs', 'floor', 'fmod', 'frexp', 'hypot',
'ldexp', 'log', 'log10', 'modf', 'pi', 'pow', 'radians',
'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'gcd']
# Use the list to filter the local namespace
safe_dict = dict((k, globals().get(k, None)) for k in safe_list)
safe_dict['math'] = math
safe_dict['numpy'] = safe_dict['np'] = numpy
safe_dict['lcm'] = numpy.lcm
safe_dict['max'] = max
safe_dict['min'] = min
# Stores the values of a list of properties and the
# operator id in a property group ('recall_op') inside the object
# Could (in theory) be used for non-objects.
# Note: Replaces any existing property group with the same name!
# ob ... Object to store the properties in
# op ... The operator that should be used
# op_args ... A dictionary with valid Blender
# properties (operator arguments/parameters)
# Create a new mesh (object) from verts/edges/faces
# verts/edges/faces ... List of vertices/edges/faces for the
# new mesh (as used in from_pydata)
# name ... Name of the new mesh (& object)
def create_mesh_object(context, verts, edges, faces, name):
# Create new mesh
mesh = bpy.data.meshes.new(name)
# Make a mesh from a list of verts/edges/faces
mesh.from_pydata(verts, edges, faces)
# Update mesh geometry after adding stuff
mesh.update()
from bpy_extras import object_utils
return object_utils.object_data_add(context, mesh, operator=None)
# A very simple "bridge" tool
def createFaces(vertIdx1, vertIdx2, closed=False, flipped=False):
faces = []
if not vertIdx1 or not vertIdx2:
return None
if len(vertIdx1) < 2 and len(vertIdx2) < 2:
return None
fan = False
if (len(vertIdx1) != len(vertIdx2)):
if (len(vertIdx1) == 1 and len(vertIdx2) > 1):
fan = True
else:
return None
total = len(vertIdx2)
if closed:
# Bridge the start with the end
if flipped:
face = [
vertIdx1[0],
vertIdx2[0],
vertIdx2[total - 1]]
if not fan:
face.append(vertIdx1[total - 1])
faces.append(face)
else:
face = [vertIdx2[0], vertIdx1[0]]
if not fan:
face.append(vertIdx1[total - 1])
face.append(vertIdx2[total - 1])
faces.append(face)
# Bridge the rest of the faces
for num in range(total - 1):
if flipped:
if fan:
face = [vertIdx2[num], vertIdx1[0], vertIdx2[num + 1]]
else:
face = [vertIdx2[num], vertIdx1[num],
vertIdx1[num + 1], vertIdx2[num + 1]]
faces.append(face)
else:
if fan:
face = [vertIdx1[0], vertIdx2[num], vertIdx2[num + 1]]
else:
face = [vertIdx1[num], vertIdx2[num],
vertIdx2[num + 1], vertIdx1[num + 1]]
faces.append(face)
return faces
class AddZFunctionSurface(Operator):
bl_idname = "mesh.primitive_z_function_surface"
bl_label = "Add Z Function Surface"
bl_description = "Add a surface defined defined by a function z=f(x,y)"
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
equation: StringProperty(
name="Z Equation",
description="Equation for z=f(x,y)",
default="1 - ( x**2 + y**2 )"
)
div_x: IntProperty(
name="X Subdivisions",
description="Number of vertices in x direction",
default=16,
min=3,
max=256
)
div_y: IntProperty(
name="Y Subdivisions",
description="Number of vertices in y direction",
default=16,
min=3,
max=256
)
size_x: FloatProperty(
name="X Size",
description="Size of the x axis",
default=2.0,
min=0.01,
max=100.0,
unit="LENGTH"
)
size_y: FloatProperty(
name="Y Size",
description="Size of the y axis",
default=2.0,
min=0.01,
max=100.0,
unit="LENGTH"
)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
layout.separator()
layout.prop(self, 'equation')
col = layout.column(align=True)
col.prop(self, 'div_x', text='Subdivisions X')
col.prop(self, 'div_y', text='Y')
col = layout.column(align=True)
col.prop(self, 'size_x', text='Size X')
col.prop(self, 'size_y', text='Y')
def execute(self, context):
equation = self.equation
div_x = self.div_x
div_y = self.div_y
size_x = self.size_x
size_y = self.size_y
verts = []
faces = []
delta_x = size_x / (div_x - 1)
delta_y = size_y / (div_y - 1)
start_x = -(size_x / 2.0)
start_y = -(size_y / 2.0)
edgeloop_prev = []
if equation:
try:
expr_args = (
compile(equation, __file__, 'eval'),
{"__builtins__": None},
safe_dict)
except:
import traceback
# WARNING is used to prevent the constant pop-up spam
self.report({'WARNING'},
"Error parsing expression: {} "
"(Check the console for more info)".format(equation))
print("\n[Add Z Function Surface]:\n\n", traceback.format_exc(limit=1))
return {'CANCELLED'}
for row_x in range(div_x):
edgeloop_cur = []
x = start_x + row_x * delta_x
for row_y in range(div_y):
y = start_y + row_y * delta_y
z = 0.0
safe_dict['x'] = x
safe_dict['y'] = y
# Try to evaluate the equation.
try:
z = float(eval(*expr_args))
except:
import traceback
self.report({'WARNING'},
"Error evaluating expression: {} "
"(Check the console for more info)".format(equation))
print("\n[Add Z Function Surface]:\n\n", traceback.format_exc(limit=1))
return {'CANCELLED'}
edgeloop_cur.append(len(verts))
verts.append((x, y, z))
if len(edgeloop_prev) > 0:
faces_row = createFaces(edgeloop_prev, edgeloop_cur)
faces.extend(faces_row)
edgeloop_prev = edgeloop_cur
base = create_mesh_object(context, verts, [], faces, "Z Function")
else:
self.report({'WARNING'}, "Z Equation - No expression is given")
return {'CANCELLED'}
return {'FINISHED'}
def xyz_function_surface_faces(self, x_eq, y_eq, z_eq,
range_u_min, range_u_max, range_u_step, wrap_u,
range_v_min, range_v_max, range_v_step, wrap_v,
a_eq, b_eq, c_eq, f_eq, g_eq, h_eq, n, close_v):
verts = []
faces = []
# Distance of each step in Blender Units
uStep = (range_u_max - range_u_min) / range_u_step
vStep = (range_v_max - range_v_min) / range_v_step
# Number of steps in the vertex creation loops.
# Number of steps is the number of faces
# => Number of points is +1 unless wrapped.
uRange = range_u_step + 1
vRange = range_v_step + 1
if wrap_u:
uRange = uRange - 1
if wrap_v:
vRange = vRange - 1
try:
expr_args_x = (
compile(x_eq, __file__.replace(".py", "_x.py"), 'eval'),
{"__builtins__": None},
safe_dict)
expr_args_y = (
compile(y_eq, __file__.replace(".py", "_y.py"), 'eval'),
{"__builtins__": None},
safe_dict)
expr_args_z = (
compile(z_eq, __file__.replace(".py", "_z.py"), 'eval'),
{"__builtins__": None},
safe_dict)
expr_args_a = (
compile(a_eq, __file__.replace(".py", "_a.py"), 'eval'),
{"__builtins__": None},
safe_dict)
expr_args_b = (
compile(b_eq, __file__.replace(".py", "_b.py"), 'eval'),
{"__builtins__": None},
safe_dict)
expr_args_c = (
compile(c_eq, __file__.replace(".py", "_c.py"), 'eval'),
{"__builtins__": None},
safe_dict)
expr_args_f = (
compile(f_eq, __file__.replace(".py", "_f.py"), 'eval'),
{"__builtins__": None},
safe_dict)
expr_args_g = (
compile(g_eq, __file__.replace(".py", "_g.py"), 'eval'),
{"__builtins__": None},
safe_dict)
expr_args_h = (
compile(h_eq, __file__.replace(".py", "_h.py"), 'eval'),
{"__builtins__": None},
safe_dict)
except:
import traceback
self.report({'WARNING'}, "Error parsing expression(s) - "
"Check the console for more info")
print("\n[Add X, Y, Z Function Surface]:\n\n", traceback.format_exc(limit=1))
return [], []
for vN in range(vRange):
v = range_v_min + (vN * vStep)
for uN in range(uRange):
u = range_u_min + (uN * uStep)
safe_dict['u'] = u
safe_dict['v'] = v
safe_dict['n'] = n
# Try to evaluate the equations.
try:
safe_dict['a'] = float(eval(*expr_args_a))
safe_dict['b'] = float(eval(*expr_args_b))
safe_dict['c'] = float(eval(*expr_args_c))
safe_dict['f'] = float(eval(*expr_args_f))
safe_dict['g'] = float(eval(*expr_args_g))
safe_dict['h'] = float(eval(*expr_args_h))
verts.append((
float(eval(*expr_args_x)),
float(eval(*expr_args_y)),
float(eval(*expr_args_z))))
except:
import traceback
self.report({'WARNING'}, "Error evaluating expression(s) - "
"Check the console for more info")
print("\n[Add X, Y, Z Function Surface]:\n\n", traceback.format_exc(limit=1))
return [], []
for vN in range(range_v_step):
vNext = vN + 1
if wrap_v and (vNext >= vRange):
vNext = 0
for uN in range(range_u_step):
uNext = uN + 1
if wrap_u and (uNext >= uRange):
uNext = 0
faces.append([(vNext * uRange) + uNext,
(vNext * uRange) + uN,
(vN * uRange) + uN,
(vN * uRange) + uNext])
if close_v and wrap_u and (not wrap_v):
for uN in range(1, range_u_step - 1):
faces.append([
range_u_step - 1,
range_u_step - 1 - uN,
range_u_step - 2 - uN])
faces.append([
range_v_step * uRange,
range_v_step * uRange + uN,
range_v_step * uRange + uN + 1])
return verts, faces
# Original Script "Parametric.py" by Ed Mackey.
# -> http://www.blinken.com/blender-plugins.php
# Partly converted for Blender 2.5 by tuga3d.
#
# Sphere:
# x = sin(2*pi*u)*sin(pi*v)
# y = cos(2*pi*u)*sin(pi*v)
# z = cos(pi*v)
# u_min = v_min = 0
# u_max = v_max = 1
#
# "Snail shell"
# x = 1.2**v*(sin(u)**2 *sin(v))
# y = 1.2**v*(sin(u)*cos(u))
# z = 1.2**v*(sin(u)**2 *cos(v))
# u_min = 0
# u_max = pi
# v_min = -pi/4,
# v max = 5*pi/2
class AddXYZFunctionSurface(Operator):
bl_idname = "mesh.primitive_xyz_function_surface"
bl_label = "Add XYZ Function Surface"
bl_description = ("Add a surface defined defined by 3 functions:\n"
"x=F1(u,v), y=F2(u,v) and z=F3(u,v)")
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
x_eq: StringProperty(
name="X Equation",
description="Equation for x=F(u,v). "
"Also available: n, a, b, c, f, g, h",
default="cos(v)*(1+cos(u))*sin(v/8)"
)
y_eq: StringProperty(
name="Y Equation",
description="Equation for y=F(u,v). "
"Also available: n, a, b, c, f, g, h",
default="sin(u)*sin(v/8)+cos(v/8)*1.5"
)
z_eq: StringProperty(
name="Z Equation",
description="Equation for z=F(u,v). "
"Also available: n, a, b, c, f, g, h",
default="sin(v)*(1+cos(u))*sin(v/8)"
)
range_u_min: FloatProperty(
name="U Min",
description="Minimum U value. Lower boundary of U range",
min=-100.00,
max=0.00,
default=0.00
)
range_u_max: FloatProperty(
name="U Max",
description="Maximum U value. Upper boundary of U range",
min=0.00,
max=100.00,
default=2 * pi
)
range_u_step: IntProperty(
name="U Step",
description="U Subdivisions",
min=1,
max=1024,
default=32
)
wrap_u: BoolProperty(
name="U Wrap",
description="U Wrap around",
default=True
)
range_v_min: FloatProperty(
name="V Min",
description="Minimum V value. Lower boundary of V range",
min=-100.00,
max=0.00,
default=0.00
)
range_v_max: FloatProperty(
name="V Max",
description="Maximum V value. Upper boundary of V range",
min=0.00,
max=100.00,
default=4 * pi
)
range_v_step: IntProperty(
name="V Step",
description="V Subdivisions",
min=1,
max=1024,
default=128
)
wrap_v: BoolProperty(
name="V Wrap",
description="V Wrap around",
default=False
)
close_v: BoolProperty(
name="Close V",
description="Create faces for first and last "
"V values (only if U is wrapped)",
default=False
)
n_eq: IntProperty(
name="Number of Objects (n=0..N-1)",
description="The parameter n will be the index "
"of the current object, 0 to N-1",
min=1,
max=100,
default=1
)
a_eq: StringProperty(
name="A Helper Function",
description="Equation for a=F(u,v). Also available: n",
default="0"
)
b_eq: StringProperty(
name="B Helper Function",
description="Equation for b=F(u,v). Also available: n",
default="0"
)
c_eq: StringProperty(
name="C Helper Function",
description="Equation for c=F(u,v). Also available: n",
default="0"
)
f_eq: StringProperty(
name="F Helper Function",
description="Equation for f=F(u,v). Also available: n, a, b, c",
default="0"
)
g_eq: StringProperty(
name="G Helper Function",
description="Equation for g=F(u,v). Also available: n, a, b, c",
default="0"
)
h_eq: StringProperty(
name="H Helper Function",
description="Equation for h=F(u,v). Also available: n, a, b, c",
default="0"
)
show_wire : BoolProperty(
name="Show Wireframe",
default=True,
description="Add the objects wireframe over solid drawing"
)
edit_mode : BoolProperty(
name="Show in Edit Mode",
default=True,
description="Show in Edit Mode"
)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
layout.separator()
col = layout.column()
col.prop(self, 'x_eq', text='Equation X')
col.prop(self, 'y_eq', text='Y')
col.prop(self, 'z_eq', text='Z')
layout.separator()
col = layout.column(align=True)
col.prop(self, 'range_u_min', text='U Min')
col.prop(self, 'range_u_max', text='Max')
col.prop(self, 'range_u_step', text='Step')
col.prop(self, 'wrap_u', text='Wrap')
layout.separator()
col = layout.column(align=True)
col.prop(self, 'range_v_min', text='V Min')
col.prop(self, 'range_v_max', text='Max')
col.prop(self, 'range_v_step', text='Step')
col.prop(self, 'wrap_v', text='Wrap')
col.prop(self, 'close_v', text='Close')
layout.separator()
col = layout.column()
col.prop(self, 'n_eq', text='Objects')
col.prop(self, 'a_eq', text='Helper Function A')
col.prop(self, 'b_eq', text='B')
col.prop(self, 'c_eq', text='C')
col.prop(self, 'f_eq', text='F')
col.prop(self, 'g_eq', text='G')
col.prop(self, 'h_eq', text='H')
layout.separator()
row = layout.row(heading='Show')
row.prop(self, 'show_wire', text='Wireframe')
layout.prop(self, 'edit_mode', text='In Edit Mode')
def execute(self, context):
for n in range(0, self.n_eq):
verts, faces = xyz_function_surface_faces(
self,
self.x_eq,
self.y_eq,
self.z_eq,
self.range_u_min,
self.range_u_max,
self.range_u_step,
self.wrap_u,
self.range_v_min,
self.range_v_max,
self.range_v_step,
self.wrap_v,
self.a_eq,
self.b_eq,
self.c_eq,
self.f_eq,
self.g_eq,
self.h_eq,
n,
self.close_v
)
if not verts:
return {'CANCELLED'}
obj = create_mesh_object(context, verts, [], faces, "XYZ Function")
if self.show_wire:
context.active_object.show_wire = True
else:
context.active_object.show_wire = False
if self.edit_mode:
bpy.ops.object.mode_set(mode = 'EDIT')
else:
bpy.ops.object.mode_set(mode = 'OBJECT')
return {'FINISHED'}
@@ -0,0 +1,822 @@
# SPDX-FileCopyrightText: 2016-2022 Blender Foundation
#
# SPDX-License-Identifier: GPL-2.0-or-later
# Author: revolt_randy, Jambay
# Create "Beam" primitives. Based on original script by revolt_randy
import bpy
from bpy.types import Operator
from bpy.props import (
BoolProperty,
EnumProperty,
FloatProperty,
IntProperty,
StringProperty,
)
from bpy_extras import object_utils
from .interface import draw_transform_props
# #####################
# Create vertices for end of mesh
#
# y_off - verts y-axis origin
#
# returns:
# endVs - x,y,z list
def beamEndVs(sRef, y_off):
thick = sRef.beamW * 2
if sRef.Type == '2': # swap width and height for C shape
bEndX2 = sRef.beamZ / 2
bEndXInr = ((sRef.beamZ - thick) / 2)
bEndZ2 = sRef.beamX / 2
bEndZInr = ((sRef.beamX - thick) / 2)
else:
bEndX2 = sRef.beamX / 2
bEndXInr = ((sRef.beamX - thick) / 2)
bEndZ2 = sRef.beamZ / 2
bEndZInr = ((sRef.beamZ - thick) / 2)
endVs = []
# outer ...
endVs.append((bEndX2, y_off, bEndZ2))
endVs.append((-bEndX2, y_off, bEndZ2))
endVs.append((-bEndX2, y_off, -bEndZ2))
endVs.append((bEndX2, y_off, -bEndZ2))
# innner ...
endVs.append((bEndXInr, y_off, bEndZInr))
endVs.append((-bEndXInr, y_off, bEndZInr))
endVs.append((-bEndXInr, y_off, -bEndZInr))
endVs.append((bEndXInr, y_off, -bEndZInr))
return endVs
# #####################
# Create End Faces
#
# verts_list - list of vertices
#
# returns:
# beamFs, a list of tuples defining the end faces.
def beamEndFaces(verts_list):
beamFs = []
num_of_verts = int(len(verts_list) / 2)
# Create list of faces
for index in range(num_of_verts):
faces_temp = []
if index == (num_of_verts - 1):
faces_temp.append(verts_list[index])
faces_temp.append(verts_list[index - index])
faces_temp.append(verts_list[index + 1])
faces_temp.append(verts_list[index * 2 + 1])
else:
faces_temp.append(verts_list[index])
faces_temp.append(verts_list[index + 1])
faces_temp.append(verts_list[index + num_of_verts + 1])
faces_temp.append(verts_list[index + num_of_verts])
beamFs.append(tuple(faces_temp))
return beamFs
# #####################
# Bridge vertices to create side faces.
#
# front_verts - front face vertices
# back_verts - back face vertices
# front & back must be ordered in same direction
# with respect to y-axis
#
# returns:
# sideFaces, a list of the bridged faces
def beamSides(front_verts, back_verts):
sideFaces = []
num_of_faces = (len(front_verts))
# add first value to end of lists for looping
front_verts.append(front_verts[0])
back_verts.append(back_verts[0])
# Build the faces
for index in range(num_of_faces):
facestemp = (front_verts[index], front_verts[index + 1], back_verts[index + 1], back_verts[index])
sideFaces.append(facestemp)
return sideFaces
# #####################
# Creates a box beam
#
# returns:
# beamVs - x, y, z, location of each vertice
# beamFs - vertices that make up each face
def create_beam(sRef):
frontVs = []
frontFs = []
backVs = []
y_off = sRef.beamY / 2 # offset from center for vertices
frontVs = beamEndVs(sRef, y_off)
backVs = beamEndVs(sRef, -y_off)
# Combine vertices
beamVs = frontVs + backVs
# Create front face
numofverts = len(frontVs)
verts_front_list = []
for index in range(numofverts):
verts_front_list.append(index)
frontFs = beamEndFaces(verts_front_list)
# Create back face
faces_back_temp = []
verts_back_list = []
numofverts = len(backVs)
for index in range(numofverts):
verts_back_list.append(index + numofverts)
faces_back_temp = beamEndFaces(verts_back_list)
# Create side faces
faces_side_temp = []
# Object has thickness, create list of outside vertices
numofverts = len(verts_front_list)
halfVerts = int(numofverts / 2)
frontVs = verts_front_list[0:halfVerts]
backVs = verts_back_list[0:halfVerts]
faces_side_temp = beamSides(frontVs, backVs)
# Create list of inside vertices
frontVs = verts_front_list[halfVerts:numofverts]
backVs = verts_back_list[halfVerts:numofverts]
faces_side_temp += beamSides(frontVs, backVs)
# Combine all faces
beamFs = frontFs + faces_back_temp + faces_side_temp
return beamVs, beamFs
# #####################
# Taper/angle faces of beam.
# inner vert toward outer vert
# based on percentage of taper.
#
# returns:
# adVert - the calculated vertex
def beamSlant(sRef, outV, inV):
bTaper = 100 - sRef.edgeA
# calculate variance & adjust vertex
deltaV = ((inV - outV) / 100)
adVert = outV + (deltaV * bTaper)
return adVert
# #####################
# Modify location to shape beam.
#
# verts - tuples for one end of beam
#
# returns:
# verts - modified tuples for beam shape.
def beamSquareEnds(sRef, verts):
# match 5th & 6th z locations to 1st & 2nd
vert_orig = verts[0]
vert_temp = verts[4]
vert_x = beamSlant(sRef, vert_orig[0], vert_temp[0])
verts[4] = (vert_x, vert_temp[1], vert_orig[2])
vert_orig = verts[1]
vert_temp = verts[5]
vert_x = beamSlant(sRef, vert_orig[0], vert_temp[0])
verts[5] = (vert_x, vert_temp[1], vert_orig[2])
return verts
# #####################
#
# Create U shaped beam
# Shared with C shape - see beamEndVs
# for sizing and rotate in addBeamObj.
#
# returns:
# beamVs - vertice x, y, z, locations
# beamFs - face vertices
def create_u_beam(sRef):
# offset vertices from center
y_off = sRef.beamY / 2
frontVtemp = []
frontFtemp = []
frontVlist = []
backVtemp = []
backFtemp = []
backVlist = []
sideFs = []
frontVtemp = beamEndVs(sRef, y_off) # Box beam
frontVtemp = beamSquareEnds(sRef, frontVtemp) # U shape
backVtemp = beamEndVs(sRef, -y_off)
backVtemp = beamSquareEnds(sRef, backVtemp)
beamVs = frontVtemp + backVtemp
# Create front face
for index in range(len(frontVtemp)): # Build vert list
frontVlist.append(index)
frontFtemp = beamEndFaces(frontVlist)
frontFtemp = frontFtemp[1:4] # Remove 1st face
# Create back face
numofverts = len(backVtemp)
for index in range(numofverts): # Build vertex list
backVlist.append(index + numofverts)
backFtemp = beamEndFaces(backVlist)
backFtemp = backFtemp[1:4] # Remove face
# Create list vertices for outside faces
numofverts = int(len(frontVlist))
halfVerts = int(numofverts / 2)
frontVtemp = frontVlist[0:halfVerts]
backVtemp = backVlist[0:halfVerts]
sideFs = beamSides(frontVtemp, backVtemp)
sideFs = sideFs[1:] # Remove face
# Create inside verts
frontVtemp = frontVlist[halfVerts:numofverts]
backVtemp = backVlist[halfVerts:numofverts]
sideFs += beamSides(frontVtemp, backVtemp)
sideFs = sideFs[0:3] + sideFs[4:] # Remove face
# fill in faces
sideFs.append((0, 4, 12, 8))
sideFs.append((5, 1, 9, 13))
beamFs = frontFtemp + backFtemp + sideFs # Combine faces
return beamVs, beamFs
# #####################
# returns:
# verts_final - x, y, z, location of each vertice
# faces_final - vertices that make up each face
def create_L_beam(sRef):
thick = sRef.beamW
# offset vertices from center
x_off = sRef.beamX / 2
y_off = sRef.beamY / 2
z_off = sRef.beamZ / 2
# Create temporarylists to hold vertices locations
verts_front_temp = []
verts_back_temp = []
# Create front vertices by calculation
verts_front_temp = [
(-x_off, -y_off, z_off),
(-(x_off - thick), -y_off, z_off),
(-(x_off - thick), -y_off, -(z_off - thick)),
(x_off, -y_off, -(z_off - thick)),
(x_off, -y_off, -z_off),
(-x_off, -y_off, -z_off)
]
# Adjust taper
vert_outside = verts_front_temp[0]
vert_inside = verts_front_temp[1]
vert_taper = beamSlant(sRef, vert_outside[0], vert_inside[0])
verts_front_temp[1] = [vert_taper, vert_inside[1], vert_inside[2]]
vert_outside = verts_front_temp[4]
vert_inside = verts_front_temp[3]
vert_taper = beamSlant(sRef, vert_outside[2], vert_inside[2])
verts_front_temp[3] = [vert_inside[0], vert_inside[1], vert_taper]
# Create back vertices by calculation
verts_back_temp = [
(-x_off, y_off, z_off),
(-(x_off - thick), y_off, z_off),
(-(x_off - thick), y_off, -(z_off - thick)),
(x_off, y_off, -(z_off - thick)),
(x_off, y_off, -z_off),
(-x_off, y_off, -z_off)
]
# Adjust taper
vert_outside = verts_back_temp[0]
vert_inside = verts_back_temp[1]
vert_taper = beamSlant(sRef, vert_outside[0], vert_inside[0])
verts_back_temp[1] = [vert_taper, vert_inside[1], vert_inside[2]]
vert_outside = verts_back_temp[4]
vert_inside = verts_back_temp[3]
vert_taper = beamSlant(sRef, vert_outside[2], vert_inside[2])
verts_back_temp[3] = [vert_inside[0], vert_inside[1], vert_taper]
verts_final = verts_front_temp + verts_back_temp
# define end faces, only 4 so just coded
faces_front_temp = []
faces_back_temp = []
faces_side_temp = []
faces_front_temp = [(0, 1, 2, 5), (2, 3, 4, 5)]
faces_back_temp = [(6, 7, 8, 11), (8, 9, 10, 11)]
verts_front_list = []
verts_back_list = []
num_of_verts = len(verts_front_temp)
# build lists of back and front verts for beamSides function
for index in range(num_of_verts):
verts_front_list.append(index)
for index in range(num_of_verts):
verts_back_list.append(index + 6)
faces_side_temp = beamSides(verts_front_list, verts_back_list)
faces_final = faces_front_temp + faces_back_temp + faces_side_temp
return verts_final, faces_final
# #####################
# returns:
# verts_final - a list of tuples of the x, y, z, location of each vertice
# faces_final - a list of tuples of the vertices that make up each face
def create_T_beam(sRef):
thick = sRef.beamW
# Get offset of vertices from center
x_off = sRef.beamX / 2
y_off = sRef.beamY / 2
z_off = sRef.beamZ / 2
thick_off = thick / 2
# Create temporarylists to hold vertices locations
verts_front_temp = []
verts_back_temp = []
# Create front vertices
verts_front_temp = [
(-x_off, -y_off, z_off),
(-thick_off, -y_off, z_off),
(thick_off, -y_off, z_off),
(x_off, -y_off, z_off),
(x_off, -y_off, z_off - thick),
(thick_off, -y_off, z_off - thick),
(thick_off, -y_off, -z_off),
(-thick_off, -y_off, -z_off),
(-thick_off, -y_off, z_off - thick),
(-x_off, -y_off, z_off - thick)
]
# Adjust taper
vert_outside = verts_front_temp[0]
vert_inside = verts_front_temp[9]
vert_taper = (beamSlant(sRef, vert_outside[2], vert_inside[2]))
verts_front_temp[9] = [vert_inside[0], vert_inside[1], vert_taper]
vert_outside = verts_front_temp[3]
vert_inside = verts_front_temp[4]
verts_front_temp[4] = [vert_inside[0], vert_inside[1], vert_taper]
# Adjust taper of bottom of beam, so 0 the center
# now becomes vert_outside, and vert_inside is calculated
# 1/2 way towards center
vert_outside = (0, -y_off, -z_off)
vert_inside = verts_front_temp[6]
vert_taper = (beamSlant(sRef, vert_outside[0], vert_inside[0]))
verts_front_temp[6] = [vert_taper, vert_inside[1], vert_inside[2]]
vert_outside = (0, -y_off, -z_off)
vert_inside = verts_front_temp[7]
vert_taper = beamSlant(sRef, vert_outside[0], vert_inside[0])
verts_front_temp[7] = [vert_taper, vert_inside[1], vert_inside[2]]
# Create fack vertices by calculation
verts_back_temp = [
(-x_off, y_off, z_off),
(-thick_off, y_off, z_off),
(thick_off, y_off, z_off),
(x_off, y_off, z_off),
(x_off, y_off, z_off - thick),
(thick_off, y_off, z_off - thick),
(thick_off, y_off, -z_off),
(-thick_off, y_off, -z_off),
(-thick_off, y_off, z_off - thick),
(-x_off, y_off, z_off - thick)
]
# Adjust taper
vert_outside = verts_back_temp[0]
vert_inside = verts_back_temp[9]
vert_taper = (beamSlant(sRef, vert_outside[2], vert_inside[2]))
verts_back_temp[9] = [vert_inside[0], vert_inside[1], vert_taper]
vert_outside = verts_back_temp[3]
vert_inside = verts_back_temp[4]
vert_taper = (beamSlant(sRef, vert_outside[2], vert_inside[2]))
verts_back_temp[4] = [vert_inside[0], vert_inside[1], vert_taper]
# Adjust taper of bottom of beam, so 0 the center
# now becomes vert_outside, and vert_inside is calculated
# 1/2 way towards center
vert_outside = (0, -y_off, -z_off)
vert_inside = verts_back_temp[6]
vert_taper = (beamSlant(sRef, vert_outside[0], vert_inside[0]))
verts_back_temp[6] = [vert_taper, vert_inside[1], vert_inside[2]]
vert_outside = (0, -y_off, -z_off)
vert_inside = verts_back_temp[7]
vert_taper = (beamSlant(sRef, vert_outside[0], vert_inside[0]))
verts_back_temp[7] = [vert_taper, vert_inside[1], vert_inside[2]]
verts_final = verts_front_temp + verts_back_temp
# define end faces, only 8 so just coded
faces_front_temp = []
faces_back_temp = []
faces_side_temp = []
faces_front_temp = [(0, 1, 8, 9), (1, 2, 5, 8),
(2, 3, 4, 5), (5, 6, 7, 8)]
faces_back_temp = [(10, 11, 18, 19), (11, 12, 15, 18),
(12, 13, 14, 15), (15, 16, 17, 18)]
verts_front_list = []
verts_back_list = []
num_of_verts = len(verts_front_temp)
# build lists of back and front verts for beamSides function
for index in range(num_of_verts):
verts_front_list.append(index)
for index in range(num_of_verts):
verts_back_list.append(index + 10)
faces_side_temp = beamSides(verts_front_list, verts_back_list)
faces_final = faces_front_temp + faces_back_temp + faces_side_temp
return verts_final, faces_final
# #####################
# returns:
# verts_final - a list of tuples of the x, y, z, location of each vertice
# faces_final - a list of tuples of the vertices that make up each face
def create_I_beam(sRef):
thick = sRef.beamW
# Get offset of vertices from center
x_off = sRef.beamX / 2
y_off = sRef.beamY / 2
z_off = sRef.beamZ / 2
thick_off = thick / 2
# Create temporarylists to hold vertices locations
verts_front_temp = []
verts_back_temp = []
# Create front vertices by calculation
verts_front_temp = [
(-x_off, -y_off, z_off),
(-thick_off, -y_off, z_off),
(thick_off, -y_off, z_off),
(x_off, -y_off, z_off),
(x_off, -y_off, z_off - thick),
(thick_off, -y_off, z_off - thick),
(thick_off, -y_off, -z_off + thick),
(x_off, -y_off, -z_off + thick),
(x_off, -y_off, -z_off),
(thick_off, -y_off, -z_off),
(-thick_off, -y_off, -z_off),
(-x_off, -y_off, -z_off),
(-x_off, -y_off, -z_off + thick),
(-thick_off, -y_off, -z_off + thick),
(-thick_off, -y_off, z_off - thick),
(-x_off, -y_off, z_off - thick)
]
# Adjust taper
vert_outside = verts_front_temp[0]
vert_inside = verts_front_temp[15]
vert_taper = (beamSlant(sRef, vert_outside[2], vert_inside[2]))
verts_front_temp[15] = [vert_inside[0], vert_inside[1], vert_taper]
vert_outside = verts_front_temp[3]
vert_inside = verts_front_temp[4]
vert_taper = (beamSlant(sRef, vert_outside[2], vert_inside[2]))
verts_front_temp[4] = [vert_inside[0], vert_inside[1], vert_taper]
vert_outside = verts_front_temp[8]
vert_inside = verts_front_temp[7]
vert_taper = (beamSlant(sRef, vert_outside[2], vert_inside[2]))
verts_front_temp[7] = [vert_inside[0], vert_inside[1], vert_taper]
vert_outside = verts_front_temp[11]
vert_inside = verts_front_temp[12]
vert_taper = (beamSlant(sRef, vert_outside[2], vert_inside[2]))
verts_front_temp[12] = [vert_inside[0], vert_inside[1], vert_taper]
# Create back vertices by calculation
verts_back_temp = [
(-x_off, y_off, z_off),
(-thick_off, y_off, z_off),
(thick_off, y_off, z_off),
(x_off, y_off, z_off),
(x_off, y_off, z_off - thick),
(thick_off, y_off, z_off - thick),
(thick_off, y_off, -z_off + thick),
(x_off, y_off, -z_off + thick),
(x_off, y_off, -z_off),
(thick_off, y_off, -z_off),
(-thick_off, y_off, -z_off),
(-x_off, y_off, -z_off),
(-x_off, y_off, -z_off + thick),
(-thick_off, y_off, -z_off + thick),
(-thick_off, y_off, z_off - thick),
(-x_off, y_off, z_off - thick)
]
# Adjust taper
vert_outside = verts_back_temp[0]
vert_inside = verts_back_temp[15]
vert_taper = (beamSlant(sRef, vert_outside[2], vert_inside[2]))
verts_back_temp[15] = [vert_inside[0], vert_inside[1], vert_taper]
vert_outside = verts_back_temp[3]
vert_inside = verts_back_temp[4]
vert_taper = (beamSlant(sRef, vert_outside[2], vert_inside[2]))
verts_back_temp[4] = [vert_inside[0], vert_inside[1], vert_taper]
vert_outside = verts_back_temp[8]
vert_inside = verts_back_temp[7]
vert_taper = (beamSlant(sRef, vert_outside[2], vert_inside[2]))
verts_back_temp[7] = [vert_inside[0], vert_inside[1], vert_taper]
vert_outside = verts_back_temp[11]
vert_inside = verts_back_temp[12]
vert_taper = (beamSlant(sRef, vert_outside[2], vert_inside[2]))
verts_back_temp[12] = [vert_inside[0], vert_inside[1], vert_taper]
verts_final = verts_front_temp + verts_back_temp
# define end faces, only 7 per end, so just coded
faces_front_temp = []
faces_back_temp = []
faces_side_temp = []
faces_front_temp = [(0, 1, 14, 15), (1, 2, 5, 14),
(2, 3, 4, 5), (6, 7, 8, 9),
(6, 9, 10, 13), (12, 13, 10, 11),
(5, 6, 13, 14)]
faces_back_temp = [(16, 17, 30, 31), (17, 18, 21, 30),
(18, 19, 20, 21), (22, 23, 24, 25),
(22, 25, 26, 29), (28, 29, 26, 27),
(21, 22, 29, 30)]
verts_front_list = []
verts_back_list = []
num_of_verts = len(verts_front_temp)
# build lists of back and front verts for beamSides function
for index in range(num_of_verts):
verts_front_list.append(index)
for index in range(num_of_verts):
verts_back_list.append(index + 16)
faces_side_temp = beamSides(verts_front_list, verts_back_list)
faces_final = faces_front_temp + faces_back_temp + faces_side_temp
return verts_final, faces_final
# ######################
#
# Generate beam mesh.
def addBeamMesh(sRef, context):
verts = []
faces = []
# type of beam to add
if sRef.Type == '0':
verts, faces = create_beam(sRef)
elif sRef.Type == '1':
verts, faces = create_u_beam(sRef)
elif sRef.Type == '2':
verts, faces = create_u_beam(sRef)
elif sRef.Type == '3':
verts, faces = create_L_beam(sRef)
elif sRef.Type == '4':
verts, faces = create_I_beam(sRef)
elif sRef.Type == '5':
verts, faces = create_T_beam(sRef)
else: # unknown type, use default.
verts, faces = create_beam(sRef)
beamMesh = bpy.data.meshes.new("Beam")
beamMesh.from_pydata(verts, [], faces)
beamMesh.update(calc_edges=True)
return beamMesh
# ######################
# Create a beam primitive.
#
# UI functions and object creation.
class addBeam(Operator, object_utils.AddObjectHelper):
bl_idname = "mesh.add_beam"
bl_label = "Beam Builder"
bl_description = "Create beam meshes of various profiles"
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
Beam : BoolProperty(name = "Beam",
default = True,
description = "Beam")
change : BoolProperty(name = "Change",
default = False,
description = "change Beam")
Type: EnumProperty(
name="Beam Type",
items=(
('0', "Box Profile", "Square Beam"),
("1", "U Profile", "U Profile Beam"),
("2", "C Profile", "C Profile Beam"),
("3", "L Profile", "L Profile Beam"),
("4", "I Profile", "I Profile Beam"),
("5", "T Profile", "T Profile Beam")
),
description="Beam form"
)
beamZ: FloatProperty(
name="Height",
min=0.01,
#max=100,
default=1
)
beamX: FloatProperty(
name="Width",
min=0.01,
#max=100,
default=.5
)
beamY: FloatProperty(
name="Depth",
min=0.01,
#max=100,
default=2
)
beamW: FloatProperty(
name="Thickness",
min=0.01,
#max=1,
default=0.1
)
edgeA: IntProperty(
name="Taper",
min=0,
#max=100,
default=0,
description="Angle beam edges"
)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
layout.separator()
layout.prop(self, "Type")
layout.separator()
layout.prop(self, "beamZ")
layout.prop(self, "beamX")
layout.prop(self, "beamY")
layout.prop(self, "beamW")
if self.Type != '0':
layout.prop(self, "edgeA")
if self.change == False:
# generic transform props
layout.separator()
draw_transform_props(self, layout)
def execute(self, context):
# turn off 'Enter Edit Mode'
use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
bpy.context.preferences.edit.use_enter_edit_mode = False
if bpy.context.mode == "OBJECT":
if context.selected_objects != [] and context.active_object and \
(context.active_object.data is not None) and ('Beam' in context.active_object.data.keys()) and \
(self.change == True):
obj = context.active_object
oldmesh = obj.data
oldmeshname = obj.data.name
mesh = addBeamMesh(self, context)
obj.data = mesh
for material in oldmesh.materials:
obj.data.materials.append(material)
bpy.data.meshes.remove(oldmesh)
obj.data.name = oldmeshname
else:
mesh = addBeamMesh(self, context)
obj = object_utils.object_data_add(context, mesh, operator=self)
if self.Type == '2': # Rotate C shape
bpy.ops.transform.rotate(value=1.570796, constraint_axis=[False, True, False])
bpy.ops.object.transform_apply(location=False, rotation=True, scale=False)
obj.data["Beam"] = True
obj.data["change"] = False
for prm in BeamParameters():
obj.data[prm] = getattr(self, prm)
if bpy.context.mode == "EDIT_MESH":
active_object = context.active_object
name_active_object = active_object.name
bpy.ops.object.mode_set(mode='OBJECT')
mesh = addBeamMesh(self, context)
obj = object_utils.object_data_add(context, mesh, operator=self)
obj.select_set(True)
active_object.select_set(True)
bpy.context.view_layer.objects.active = active_object
bpy.ops.object.join()
context.active_object.name = name_active_object
bpy.ops.object.mode_set(mode='EDIT')
if use_enter_edit_mode:
bpy.ops.object.mode_set(mode = 'EDIT')
# restore pre operator state
bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
return {'FINISHED'}
def BeamParameters():
BeamParameters = [
"Type",
"beamZ",
"beamX",
"beamY",
"beamW",
"edgeA",
]
return BeamParameters
@@ -0,0 +1,987 @@
# SPDX-FileCopyrightText: 2009-2010 Michel J. Anders (varkenvarken)
#
# SPDX-License-Identifier: GPL-2.0-or-later
import bpy
from bpy.types import Operator
from math import (
atan, asin, cos,
sin, tan, pi,
radians,
)
from bpy.props import (
FloatProperty,
IntProperty,
BoolProperty,
StringProperty,
FloatVectorProperty
)
from mathutils import (
Vector,
Matrix,
)
from bpy_extras import object_utils
from .interface import draw_transform_props
# A very simple "bridge" tool.
# Connects two equally long vertex rows with faces.
# Returns a list of the new faces (list of lists)
#
# vertIdx1 ... First vertex list (list of vertex indices)
# vertIdx2 ... Second vertex list (list of vertex indices)
# closed ... Creates a loop (first & last are closed)
# flipped ... Invert the normal of the face(s)
#
# Note: You can set vertIdx1 to a single vertex index to create
# a fan/star of faces
# Note: If both vertex idx list are the same length they have
# to have at least 2 vertices
def createFaces(vertIdx1, vertIdx2, closed=False, flipped=False):
faces = []
if not vertIdx1 or not vertIdx2:
return None
if len(vertIdx1) < 2 and len(vertIdx2) < 2:
return None
fan = False
if (len(vertIdx1) != len(vertIdx2)):
if (len(vertIdx1) == 1 and len(vertIdx2) > 1):
fan = True
else:
return None
total = len(vertIdx2)
if closed:
# Bridge the start with the end.
if flipped:
face = [
vertIdx1[0],
vertIdx2[0],
vertIdx2[total - 1]]
if not fan:
face.append(vertIdx1[total - 1])
faces.append(face)
else:
face = [vertIdx2[0], vertIdx1[0]]
if not fan:
face.append(vertIdx1[total - 1])
face.append(vertIdx2[total - 1])
faces.append(face)
# Bridge the rest of the faces.
for num in range(total - 1):
if flipped:
if fan:
face = [vertIdx2[num], vertIdx1[0], vertIdx2[num + 1]]
else:
face = [vertIdx2[num], vertIdx1[num],
vertIdx1[num + 1], vertIdx2[num + 1]]
faces.append(face)
else:
if fan:
face = [vertIdx1[0], vertIdx2[num], vertIdx2[num + 1]]
else:
face = [vertIdx1[num], vertIdx2[num],
vertIdx2[num + 1], vertIdx1[num + 1]]
faces.append(face)
return faces
# Calculate the vertex coordinates for a single
# section of a gear tooth.
# Returns 4 lists of vertex coords (list of tuples):
# *-*---*---* (1.) verts_inner_base
# | | | |
# *-*---*---* (2.) verts_outer_base
# | | |
# *---*---* (3.) verts_middle_tooth
# \ | /
# *-*-* (4.) verts_tip_tooth
#
# a
# t
# d
# radius
# Ad
# De
# base
# p_angle
# rack
# crown
def add_tooth(a, t, d, radius, Ad, De, base, p_angle, rack=0, crown=0.0):
A = [a, a + t / 4, a + t / 2, a + 3 * t / 4]
C = [cos(i) for i in A]
S = [sin(i) for i in A]
Ra = radius + Ad
Rd = radius - De
Rb = Rd - base
# Pressure angle calc
O = Ad * tan(p_angle)
if Ra != 0:
p_angle = atan(O / Ra)
else:
p_angle = atan(O)
if radius < 0:
p_angle = -p_angle
if rack:
S = [sin(t / 4) * I for I in range(-2, 3)]
Sp = [0, sin(-t / 4 + p_angle), 0, sin(t / 4 - p_angle)]
verts_inner_base = [(Rb, radius * S[I], d) for I in range(4)]
verts_outer_base = [(Rd, radius * S[I], d) for I in range(4)]
verts_middle_tooth = [(radius, radius * S[I], d) for I in range(1, 4)]
verts_tip_tooth = [(Ra, radius * Sp[I], d) for I in range(1, 4)]
else:
Cp = [
0,
cos(a + t / 4 + p_angle),
cos(a + t / 2),
cos(a + 3 * t / 4 - p_angle)]
Sp = [0,
sin(a + t / 4 + p_angle),
sin(a + t / 2),
sin(a + 3 * t / 4 - p_angle)]
verts_inner_base = [(Rb * C[I], Rb * S[I], d)
for I in range(4)]
verts_outer_base = [(Rd * C[I], Rd * S[I], d)
for I in range(4)]
verts_middle_tooth = [(radius * C[I], radius * S[I], d + crown / 3)
for I in range(1, 4)]
verts_tip_tooth = [(Ra * Cp[I], Ra * Sp[I], d + crown)
for I in range(1, 4)]
return (verts_inner_base, verts_outer_base,
verts_middle_tooth, verts_tip_tooth)
# EXPERIMENTAL Calculate the vertex coordinates for a single
# section of a gearspoke.
# Returns them as a list of tuples
#
# a
# t
# d
# radius
# De
# base
# s
# w
# l
# gap
# width
#
# @todo Finish this.
def add_spoke(a, t, d, radius, De, base, s, w, l, gap=0, width=19):
Rd = radius - De
Rb = Rd - base
verts = []
edgefaces = []
edgefaces2 = []
sf = []
if not gap:
for N in range(width, 1, -2):
edgefaces.append(len(verts))
ts = t / 4
tm = a + 2 * ts
te = asin(w / Rb)
td = te - ts
t4 = ts + td * (width - N) / (width - 3.0)
A = [tm + (i - int(N / 2)) * t4 for i in range(N)]
C = [cos(i) for i in A]
S = [sin(i) for i in A]
verts.extend((Rb * I, Rb * J, d) for (I, J) in zip(C, S))
edgefaces2.append(len(verts) - 1)
Rb = Rb - s
n = 0
for N in range(width, 3, -2):
sf.extend([(i + n, i + 1 + n, i + 2 + n, i + N + n)
for i in range(0, N - 1, 2)])
sf.extend([(i + 2 + n, i + N + n, i + N + 1 + n, i + N + 2 + n)
for i in range(0, N - 3, 2)])
n = n + N
return verts, edgefaces, edgefaces2, sf
# Create gear geometry.
# Returns:
# * A list of vertices (list of tuples)
# * A list of faces (list of lists)
# * A list (group) of vertices of the tip (list of vertex indices)
# * A list (group) of vertices of the valley (list of vertex indices)
#
# teethNum ... Number of teeth on the gear
# radius ... Radius of the gear, negative for crown gear
# Ad ... Addendum, extent of tooth above radius
# De ... Dedendum, extent of tooth below radius
# base ... Base, extent of gear below radius
# p_angle ... Pressure angle. Skewness of tooth tip. (radiant)
# width ... Width, thickness of gear
# skew ... Skew of teeth. (radiant)
# conangle ... Conical angle of gear. (radiant)
# rack
# crown ... Inward pointing extend of crown teeth
#
# inner radius = radius - (De + base)
def add_gear(teethNum, radius, Ad, De, base, p_angle,
width=1, skew=0, conangle=0, rack=0, crown=0.0):
if teethNum < 2:
return None, None, None, None
t = 2 * pi / teethNum
if rack:
teethNum = 1
#print(radius, width, conangle)
if radius != 0:
scale = (radius - 2 * width * tan(conangle)) / radius
else:
scale = radius - 2 * width * tan(conangle)
verts = []
faces = []
vgroup_top = [] # Vertex group of top/tip? vertices.
vgroup_valley = [] # Vertex group of valley vertices
verts_bridge_prev = []
for toothCnt in range(teethNum):
a = toothCnt * t
verts_bridge_start = []
verts_bridge_end = []
verts_outside_top = []
verts_outside_bottom = []
for (s, d, c, top) \
in [(0, -width, 1, True), (skew, width, scale, False)]:
verts1, verts2, verts3, verts4 = add_tooth(a + s, t, d,
radius * c, Ad * c, De * c, base * c, p_angle,
rack, crown)
vertsIdx1 = list(range(len(verts), len(verts) + len(verts1)))
verts.extend(verts1)
vertsIdx2 = list(range(len(verts), len(verts) + len(verts2)))
verts.extend(verts2)
vertsIdx3 = list(range(len(verts), len(verts) + len(verts3)))
verts.extend(verts3)
vertsIdx4 = list(range(len(verts), len(verts) + len(verts4)))
verts.extend(verts4)
verts_outside = []
verts_outside.extend(vertsIdx2[:2])
verts_outside.append(vertsIdx3[0])
verts_outside.extend(vertsIdx4)
verts_outside.append(vertsIdx3[-1])
verts_outside.append(vertsIdx2[-1])
if top:
# verts_inside_top = vertsIdx1
verts_outside_top = verts_outside
verts_bridge_start.append(vertsIdx1[0])
verts_bridge_start.append(vertsIdx2[0])
verts_bridge_end.append(vertsIdx1[-1])
verts_bridge_end.append(vertsIdx2[-1])
else:
# verts_inside_bottom = vertsIdx1
verts_outside_bottom = verts_outside
verts_bridge_start.append(vertsIdx2[0])
verts_bridge_start.append(vertsIdx1[0])
verts_bridge_end.append(vertsIdx2[-1])
verts_bridge_end.append(vertsIdx1[-1])
# Valley = first 2 vertices of outer base:
vgroup_valley.extend(vertsIdx2[:1])
# Top/tip vertices:
vgroup_top.extend(vertsIdx4)
faces_tooth_middle_top = createFaces(vertsIdx2[1:], vertsIdx3,
flipped=top)
faces_tooth_outer_top = createFaces(vertsIdx3, vertsIdx4,
flipped=top)
faces_base_top = createFaces(vertsIdx1, vertsIdx2, flipped=top)
faces.extend(faces_base_top)
faces.extend(faces_tooth_middle_top)
faces.extend(faces_tooth_outer_top)
# faces_inside = createFaces(verts_inside_top, verts_inside_bottom)
# faces.extend(faces_inside)
faces_outside = createFaces(verts_outside_top, verts_outside_bottom,
flipped=True)
faces.extend(faces_outside)
if toothCnt == 0:
verts_bridge_first = verts_bridge_start
# Bridge one tooth to the next
if verts_bridge_prev:
faces_bridge = createFaces(verts_bridge_prev, verts_bridge_start)
faces.extend(faces_bridge)
# Remember "end" vertices for next tooth.
verts_bridge_prev = verts_bridge_end
# Bridge the first to the last tooth.
faces_bridge_f_l = createFaces(verts_bridge_prev, verts_bridge_first)
faces.extend(faces_bridge_f_l)
return verts, faces, vgroup_top, vgroup_valley
# Create spokes geometry
# Returns:
# * A list of vertices (list of tuples)
# * A list of faces (list of lists)
#
# teethNum ... Number of teeth on the gear.
# radius ... Radius of the gear, negative for crown gear
# De ... Dedendum, extent of tooth below radius
# base ... Base, extent of gear below radius
# width ... Width, thickness of gear
# conangle ... Conical angle of gear. (radiant)
# rack
# spoke
# spbevel
# spwidth
# splength
# spresol
#
# @todo Finish this
# @todo Create a function that takes a "Gear" and creates a
# matching "Gear Spokes" object
def add_spokes(teethNum, radius, De, base, width=1, conangle=0, rack=0,
spoke=3, spbevel=0.1, spwidth=0.2, splength=1.0, spresol=9):
if teethNum < 2:
return None, None, None, None
if spoke < 2:
return None, None, None, None
t = 2 * pi / teethNum
if rack:
teethNum = 1
scale = (radius - 2 * width * tan(conangle)) / radius
verts = []
faces = []
c = scale # debug
fl = len(verts)
for toothCnt in range(teethNum):
a = toothCnt * t
s = 0 # For test
if toothCnt % spoke == 0:
for d in (-width, width):
sv, edgefaces, edgefaces2, sf = add_spoke(a + s, t, d,
radius * c, De * c, base * c,
spbevel, spwidth, splength, 0, spresol)
verts.extend(sv)
faces.extend([j + fl for j in i] for i in sf)
fl += len(sv)
d1 = fl - len(sv)
d2 = fl - 2 * len(sv)
faces.extend([(i + d2, j + d2, j + d1, i + d1)
for (i, j) in zip(edgefaces[:-1], edgefaces[1:])])
faces.extend([(i + d2, j + d2, j + d1, i + d1)
for (i, j) in zip(edgefaces2[:-1], edgefaces2[1:])])
else:
for d in (-width, width):
sv, edgefaces, edgefaces2, sf = add_spoke(a + s, t, d,
radius * c, De * c, base * c,
spbevel, spwidth, splength, 1, spresol)
verts.extend(sv)
fl += len(sv)
d1 = fl - len(sv)
d2 = fl - 2 * len(sv)
faces.extend([[i + d2, i + 1 + d2, i + 1 + d1, i + d1]
for (i) in range(0, 3)])
faces.extend([[i + d2, i + 1 + d2, i + 1 + d1, i + d1]
for (i) in range(5, 8)])
return verts, faces
# Create worm geometry.
# Returns:
# * A list of vertices
# * A list of faces
# * A list (group) of vertices of the tip
# * A list (group) of vertices of the valley
#
# teethNum ... Number of teeth on the worm
# radius ... Radius of the gear, negative for crown gear
# Ad ... Addendum, extent of tooth above radius
# De ... Dedendum, extent of tooth below radius
# p_angle ... Pressure angle. Skewness of tooth tip. (radiant)
# width ... Width, thickness of gear
# crown ... Inward pointing extend of crown teeth
#
# @todo: Fix teethNum. Some numbers are not possible yet
# @todo: Create start & end geometry (closing faces)
def add_worm(teethNum, rowNum, radius, Ad, De, p_angle,
width=1, skew=radians(11.25), crown=0.0):
worm = teethNum
teethNum = 24
t = 2 * pi / teethNum
verts = []
faces = []
vgroup_top = [] # Vertex group of top/tip? vertices.
vgroup_valley = [] # Vertex group of valley vertices
# width = width / 2.0
edgeloop_prev = []
for Row in range(rowNum):
edgeloop = []
for toothCnt in range(teethNum):
a = toothCnt * t
s = Row * skew
d = Row * width
c = 1
isTooth = False
if toothCnt % (teethNum / worm) != 0:
# Flat
verts1, verts2, verts3, verts4 = add_tooth(a + s, t, d,
radius - De, 0.0, 0.0, 0, p_angle)
# Ignore other verts than the "other base".
verts1 = verts3 = verts4 = []
else:
# Tooth
isTooth = True
verts1, verts2, verts3, verts4 = add_tooth(a + s, t, d,
radius * c, Ad * c, De * c, 0 * c, p_angle, 0, crown)
# Remove various unneeded verts (if we are "inside" the tooth)
del(verts2[2]) # Central vertex in the base of the tooth.
del(verts3[1]) # Central vertex in the middle of the tooth.
vertsIdx2 = list(range(len(verts), len(verts) + len(verts2)))
verts.extend(verts2)
vertsIdx3 = list(range(len(verts), len(verts) + len(verts3)))
verts.extend(verts3)
vertsIdx4 = list(range(len(verts), len(verts) + len(verts4)))
verts.extend(verts4)
if isTooth:
verts_current = []
verts_current.extend(vertsIdx2[:2])
verts_current.append(vertsIdx3[0])
verts_current.extend(vertsIdx4)
verts_current.append(vertsIdx3[-1])
verts_current.append(vertsIdx2[-1])
# Valley = first 2 vertices of outer base:
vgroup_valley.extend(vertsIdx2[:1])
# Top/tip vertices:
vgroup_top.extend(vertsIdx4)
else:
# Flat
verts_current = vertsIdx2
# Valley - all of them.
vgroup_valley.extend(vertsIdx2)
edgeloop.extend(verts_current)
# Create faces between rings/rows.
if edgeloop_prev:
faces_row = createFaces(edgeloop, edgeloop_prev, closed=True)
faces.extend(faces_row)
# Remember last ring/row of vertices for next ring/row iteration.
edgeloop_prev = edgeloop
return verts, faces, vgroup_top, vgroup_valley
def AddGearMesh(self, context):
verts, faces, verts_tip, verts_valley = add_gear(
self.number_of_teeth,
self.radius,
self.addendum,
self.dedendum,
self.base,
self.angle,
width=self.width,
skew=self.skew,
conangle=self.conangle,
crown=self.crown
)
mesh = bpy.data.meshes.new("Gear")
mesh.from_pydata(verts, [], faces)
return mesh, verts_tip, verts_valley
class AddGear(Operator, object_utils.AddObjectHelper):
bl_idname = "mesh.primitive_gear"
bl_label = "Add Gear"
bl_description = "Construct a gear mesh"
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
Gear : BoolProperty(name = "Gear",
default = True,
description = "Gear")
#### change properties
name : StringProperty(name = "Name",
description = "Name")
change : BoolProperty(name = "Change",
default = False,
description = "change Gear")
number_of_teeth: IntProperty(name="Teeth",
description="Number of teeth on the gear",
min=2,
soft_max=1000,
default=12
)
radius: FloatProperty(name="Radius",
description="Radius of the gear, negative for crown gear",
soft_min=-1000.0,
soft_max=1000.0,
unit='LENGTH',
default=1.0
)
addendum: FloatProperty(name="Addendum",
description="Addendum, extent of tooth above radius",
soft_min=-1000.0,
soft_max=1000.0,
unit='LENGTH',
default=0.1
)
dedendum: FloatProperty(name="Dedendum",
description="Dedendum, extent of tooth below radius",
soft_min=-1000.0,
soft_max=1000.0,
unit='LENGTH',
default=0.1
)
angle: FloatProperty(name="Pressure Angle",
description="Pressure angle, skewness of tooth tip",
soft_min=radians(-45.0),
soft_max=radians(45.0),
unit='ROTATION',
default=radians(20.0)
)
base: FloatProperty(name="Base",
description="Base, extent of gear below radius",
soft_min=-1000.0,
soft_max=1000.0,
unit='LENGTH',
default=0.2
)
width: FloatProperty(name="Width",
description="Width, thickness of gear",
soft_min=-1000.0,
soft_max=1000.0,
unit='LENGTH',
default=0.2
)
skew: FloatProperty(name="Skewness",
description="Skew of teeth",
soft_min=radians(-360.0),
soft_max=radians(360.0),
unit='ROTATION',
default=radians(0.0)
)
conangle: FloatProperty(name="Conical Angle",
description="Conical angle of gear",
soft_min=radians(-360.0),
soft_max=radians(360.0),
unit='ROTATION',
default=radians(0.0)
)
crown: FloatProperty(name="Crown",
description="Inward pointing extend of crown teeth",
soft_min=-1000.0,
soft_max=1000.0,
unit='LENGTH',
default=0.0
)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
layout.separator()
layout.prop(self, 'number_of_teeth')
layout.separator()
layout.prop(self, 'radius')
layout.prop(self, 'width')
layout.prop(self, 'base')
layout.separator()
layout.prop(self, 'dedendum')
layout.prop(self, 'addendum')
layout.separator()
layout.prop(self, 'angle')
layout.prop(self, 'skew')
layout.prop(self, 'conangle')
layout.prop(self, 'crown')
if self.change == False:
layout.separator()
draw_transform_props(self, layout)
@classmethod
def poll(cls, context):
return context.scene is not None
def execute(self, context):
# turn off 'Enter Edit Mode'
use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
bpy.context.preferences.edit.use_enter_edit_mode = False
if bpy.context.mode == "OBJECT":
if context.selected_objects != [] and context.active_object and \
(context.active_object.data is not None) and ('Gear' in context.active_object.data.keys()) and \
(self.change == True):
obj = context.active_object
oldmesh = obj.data
oldmeshname = obj.data.name
mesh, verts_tip, verts_valley = AddGearMesh(self, context)
obj.data = mesh
try:
bpy.ops.object.vertex_group_remove(all=True)
except:
pass
for material in oldmesh.materials:
obj.data.materials.append(material)
bpy.data.meshes.remove(oldmesh)
obj.data.name = oldmeshname
else:
mesh, verts_tip, verts_valley = AddGearMesh(self, context)
obj = object_utils.object_data_add(context, mesh, operator=self)
# Create vertex groups from stored vertices.
tipGroup = obj.vertex_groups.new(name='Tips')
tipGroup.add(verts_tip, 1.0, 'ADD')
valleyGroup = obj.vertex_groups.new(name='Valleys')
valleyGroup.add(verts_valley, 1.0, 'ADD')
obj.data["Gear"] = True
obj.data["change"] = False
for prm in GearParameters():
obj.data[prm] = getattr(self, prm)
if bpy.context.mode == "EDIT_MESH":
active_object = context.active_object
name_active_object = active_object.name
bpy.ops.object.mode_set(mode='OBJECT')
mesh, verts_tip, verts_valley = AddGearMesh(self, context)
obj = object_utils.object_data_add(context, mesh, operator=self)
# Create vertex groups from stored vertices.
tipGroup = obj.vertex_groups.new(name='Tips')
tipGroup.add(verts_tip, 1.0, 'ADD')
valleyGroup = obj.vertex_groups.new(name='Valleys')
valleyGroup.add(verts_valley, 1.0, 'ADD')
obj.select_set(True)
active_object.select_set(True)
bpy.context.view_layer.objects.active = active_object
bpy.ops.object.join()
context.active_object.name = name_active_object
bpy.ops.object.mode_set(mode='EDIT')
if use_enter_edit_mode:
bpy.ops.object.mode_set(mode = 'EDIT')
# restore pre operator state
bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
return {'FINISHED'}
def invoke(self, context, event):
self.execute(context)
return {'FINISHED'}
def GearParameters():
GearParameters = [
"number_of_teeth",
"radius",
"addendum",
"dedendum",
"base",
"angle",
"width",
"skew",
"conangle",
"crown",
]
return GearParameters
def AddWormGearMesh(self, context):
verts, faces, verts_tip, verts_valley = add_worm(
self.number_of_teeth,
self.number_of_rows,
self.radius,
self.addendum,
self.dedendum,
self.angle,
width=self.row_height,
skew=self.skew,
crown=self.crown
)
mesh = bpy.data.meshes.new("Worm Gear")
mesh.from_pydata(verts, [], faces)
return mesh, verts_tip, verts_valley
class AddWormGear(Operator, object_utils.AddObjectHelper):
bl_idname = "mesh.primitive_worm_gear"
bl_label = "Add Worm Gear"
bl_description = "Construct a worm gear mesh"
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
WormGear : BoolProperty(name = "WormGear",
default = True,
description = "WormGear")
#### change properties
name : StringProperty(name = "Name",
description = "Name")
change : BoolProperty(name = "Change",
default = False,
description = "change WormGear")
number_of_teeth: IntProperty(
name="Teeth",
description="Number of teeth on the gear",
min=1,
soft_max=1000,
default=12
)
number_of_rows: IntProperty(
name="Rows",
description="Number of rows on the worm gear",
min=0,
soft_max=1000,
default=32
)
radius: FloatProperty(
name="Radius",
description="Radius of the gear, negative for crown gear",
soft_min=-1000.0,
soft_max=1000.0,
unit='LENGTH',
default=1.0
)
addendum: FloatProperty(
name="Addendum",
description="Addendum, extent of tooth above radius",
soft_min=-1000.0,
soft_max=1000.0,
unit='LENGTH',
default=0.1
)
dedendum: FloatProperty(
name="Dedendum",
description="Dedendum, extent of tooth below radius",
soft_min=-1000.0,
soft_max=1000.0,
unit='LENGTH',
default=0.1
)
angle: FloatProperty(
name="Pressure Angle",
description="Pressure angle, skewness of tooth tip",
soft_min=radians(-45.0),
soft_max=radians(45.0),
default=radians(20.0),
unit='ROTATION'
)
row_height: FloatProperty(
name="Row Height",
description="Height of each Row",
soft_min=-1000.0,
soft_max=1000.0,
unit='LENGTH',
default=0.2
)
skew: FloatProperty(
name="Skewness per Row",
description="Skew of each row",
soft_min=radians(-360.0),
soft_max=radians(360.0),
default=radians(11.25),
unit='ROTATION'
)
crown: FloatProperty(
name="Crown",
description="Inward pointing extend of crown teeth",
soft_min=-1000.0,
soft_max=1000.0,
unit='LENGTH',
default=0.0
)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
layout.separator()
layout.prop(self, "number_of_teeth")
layout.prop(self, "number_of_rows")
layout.separator()
layout.prop(self, "radius")
layout.prop(self, "row_height")
layout.separator()
layout.prop(self, "addendum")
layout.prop(self, "dedendum")
layout.separator()
layout.prop(self, "angle")
layout.prop(self, "skew")
layout.prop(self, "crown")
if self.change == False:
layout.separator()
draw_transform_props(self, layout)
def execute(self, context):
# turn off 'Enter Edit Mode'
use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
bpy.context.preferences.edit.use_enter_edit_mode = False
if bpy.context.mode == "OBJECT":
if context.selected_objects != [] and context.active_object and \
(context.active_object.data is not None) and ('WormGear' in context.active_object.data.keys()) and \
(self.change == True):
obj = context.active_object
oldmesh = obj.data
oldmeshname = obj.data.name
mesh, verts_tip, verts_valley = AddWormGearMesh(self, context)
obj.data = mesh
try:
bpy.ops.object.vertex_group_remove(all=True)
except:
pass
for material in oldmesh.materials:
obj.data.materials.append(material)
bpy.data.meshes.remove(oldmesh)
obj.data.name = oldmeshname
else:
mesh, verts_tip, verts_valley = AddWormGearMesh(self, context)
obj = object_utils.object_data_add(context, mesh, operator=self)
# Create vertex groups from stored vertices.
tipGroup = obj.vertex_groups.new(name = 'Tips')
tipGroup.add(verts_tip, 1.0, 'ADD')
valleyGroup = obj.vertex_groups.new(name = 'Valleys')
valleyGroup.add(verts_valley, 1.0, 'ADD')
obj.data["WormGear"] = True
obj.data["change"] = False
for prm in WormGearParameters():
obj.data[prm] = getattr(self, prm)
if bpy.context.mode == "EDIT_MESH":
active_object = context.active_object
name_active_object = active_object.name
bpy.ops.object.mode_set(mode='OBJECT')
mesh, verts_tip, verts_valley = AddWormGearMesh(self, context)
obj = object_utils.object_data_add(context, mesh, operator=self)
# Create vertex groups from stored vertices.
tipGroup = obj.vertex_groups.new(name = 'Tips')
tipGroup.add(verts_tip, 1.0, 'ADD')
valleyGroup = obj.vertex_groups.new(name = 'Valleys')
valleyGroup.add(verts_valley, 1.0, 'ADD')
obj.select_set(True)
active_object.select_set(True)
bpy.context.view_layer.objects.active = active_object
bpy.ops.object.join()
context.active_object.name = name_active_object
bpy.ops.object.mode_set(mode='EDIT')
if use_enter_edit_mode:
bpy.ops.object.mode_set(mode = 'EDIT')
# restore pre operator state
bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
return {'FINISHED'}
def WormGearParameters():
WormGearParameters = [
"number_of_teeth",
"number_of_rows",
"radius",
"addendum",
"dedendum",
"angle",
"row_height",
"skew",
"crown",
]
return WormGearParameters
@@ -0,0 +1,516 @@
# SPDX-FileCopyrightText: 2010-2022 Blender Foundation
#
# SPDX-License-Identifier: GPL-2.0-or-later
# Author: Pontiac, Fourmadmen, Dreampainter
import bpy
from bpy.types import Operator
from mathutils import (
Vector,
Quaternion,
)
from math import cos, sin, pi
from bpy.props import (
FloatProperty,
IntProperty,
BoolProperty,
StringProperty,
)
from bpy_extras import object_utils
from .interface import draw_transform_props
# Create a new mesh (object) from verts/edges/faces.
# verts/edges/faces ... List of vertices/edges/faces for the
# new mesh (as used in from_pydata)
# name ... Name of the new mesh (& object)
def create_mesh_object(context, self, verts, edges, faces, name):
# Create new mesh
mesh = bpy.data.meshes.new(name)
# Make a mesh from a list of verts/edges/faces.
mesh.from_pydata(verts, edges, faces)
# Update mesh geometry after adding stuff.
mesh.update()
from bpy_extras import object_utils
return object_utils.object_data_add(context, mesh, operator=self)
# A very simple "bridge" tool.
def createFaces(vertIdx1, vertIdx2, closed=False, flipped=False):
faces = []
if not vertIdx1 or not vertIdx2:
return None
if len(vertIdx1) < 2 and len(vertIdx2) < 2:
return None
fan = False
if (len(vertIdx1) != len(vertIdx2)):
if (len(vertIdx1) == 1 and len(vertIdx2) > 1):
fan = True
else:
return None
total = len(vertIdx2)
if closed:
# Bridge the start with the end
if flipped:
face = [
vertIdx1[0],
vertIdx2[0],
vertIdx2[total - 1]]
if not fan:
face.append(vertIdx1[total - 1])
faces.append(face)
else:
face = [vertIdx2[0], vertIdx1[0]]
if not fan:
face.append(vertIdx1[total - 1])
face.append(vertIdx2[total - 1])
faces.append(face)
# Bridge the rest of the faces
for num in range(total - 1):
if flipped:
if fan:
face = [vertIdx2[num], vertIdx1[0], vertIdx2[num + 1]]
else:
face = [vertIdx2[num], vertIdx1[num],
vertIdx1[num + 1], vertIdx2[num + 1]]
faces.append(face)
else:
if fan:
face = [vertIdx1[0], vertIdx2[num], vertIdx2[num + 1]]
else:
face = [vertIdx1[num], vertIdx2[num],
vertIdx2[num + 1], vertIdx1[num + 1]]
faces.append(face)
return faces
# @todo Clean up vertex&face creation process a bit.
def add_gem(r1, r2, seg, h1, h2):
"""
r1 = pavilion radius
r2 = crown radius
seg = number of segments
h1 = pavilion height
h2 = crown height
Generates the vertices and faces of the gem
"""
verts = []
a = 2.0 * pi / seg # Angle between segments
offset = a / 2.0 # Middle between segments
r3 = ((r1 + r2) / 2.0) / cos(offset) # Middle of crown
r4 = (r1 / 2.0) / cos(offset) # Middle of pavilion
h3 = h2 / 2.0 # Middle of crown height
h4 = -h1 / 2.0 # Middle of pavilion height
# Tip
vert_tip = len(verts)
verts.append(Vector((0.0, 0.0, -h1)))
# Middle vertex of the flat side (crown)
vert_flat = len(verts)
verts.append(Vector((0.0, 0.0, h2)))
edgeloop_flat = []
for i in range(seg):
s1 = sin(i * a)
s2 = sin(offset + i * a)
c1 = cos(i * a)
c2 = cos(offset + i * a)
verts.append((r4 * s1, r4 * c1, h4)) # Middle of pavilion
verts.append((r1 * s2, r1 * c2, 0.0)) # Pavilion
verts.append((r3 * s1, r3 * c1, h3)) # Middle crown
edgeloop_flat.append(len(verts))
verts.append((r2 * s2, r2 * c2, h2)) # Crown
faces = []
for index in range(seg):
i = index * 4
j = ((index + 1) % seg) * 4
faces.append([j + 2, vert_tip, i + 2, i + 3]) # Tip -> Middle of pav
faces.append([j + 2, i + 3, j + 3]) # Middle of pav -> pav
faces.append([j + 3, i + 3, j + 4]) # Pav -> Middle crown
faces.append([j + 4, i + 3, i + 4, i + 5]) # Crown quads
faces.append([j + 4, i + 5, j + 5]) # Middle crown -> crown
faces_flat = createFaces([vert_flat], edgeloop_flat, closed=True, flipped=True)
faces.extend(faces_flat)
return verts, faces
def add_diamond(segments, girdle_radius, table_radius,
crown_height, pavilion_height):
PI_2 = pi * 2.0
z_axis = (0.0, 0.0, -1.0)
verts = []
faces = []
height_flat = crown_height
height_middle = 0.0
height_tip = -pavilion_height
# Middle vertex of the flat side (crown)
vert_flat = len(verts)
verts.append(Vector((0.0, 0.0, height_flat)))
# Tip
vert_tip = len(verts)
verts.append(Vector((0.0, 0.0, height_tip)))
verts_flat = []
verts_girdle = []
for index in range(segments):
quat = Quaternion(z_axis, (index / segments) * PI_2)
# angle = PI_2 * index / segments # UNUSED
# Row for flat side
verts_flat.append(len(verts))
vec = quat @ Vector((table_radius, 0.0, height_flat))
verts.append(vec)
# Row for the middle/girdle
verts_girdle.append(len(verts))
vec = quat @ Vector((girdle_radius, 0.0, height_middle))
verts.append(vec)
# Flat face
faces_flat = createFaces([vert_flat], verts_flat, closed=True,
flipped=True)
# Side face
faces_side = createFaces(verts_girdle, verts_flat, closed=True)
# Tip faces
faces_tip = createFaces([vert_tip], verts_girdle, closed=True)
faces.extend(faces_tip)
faces.extend(faces_side)
faces.extend(faces_flat)
return verts, faces
class AddDiamond(Operator, object_utils.AddObjectHelper):
bl_idname = "mesh.primitive_diamond_add"
bl_label = "Add Diamond"
bl_description = "Construct a diamond mesh"
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
Diamond : BoolProperty(name = "Diamond",
default = True,
description = "Diamond")
#### change properties
name : StringProperty(name = "Name",
description = "Name")
change : BoolProperty(name = "Change",
default = False,
description = "change Diamond")
segments: IntProperty(
name="Segments",
description="Number of segments for the diamond",
min=3,
max=256,
default=32
)
girdle_radius: FloatProperty(
name="Girdle Radius",
description="Girdle radius of the diamond",
min=0.01,
max=9999.0,
default=1.0
)
table_radius: FloatProperty(
name="Table Radius",
description="Girdle radius of the diamond",
min=0.01,
max=9999.0,
default=0.6
)
crown_height: FloatProperty(
name="Crown Height",
description="Crown height of the diamond",
min=0.01,
max=9999.0,
default=0.35
)
pavilion_height: FloatProperty(
name="Pavilion Height",
description="Pavilion height of the diamond",
min=0.01,
max=9999.0,
default=0.8
)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
layout.separator()
layout.prop(self, "segments")
col = layout.column(align=True)
col.prop(self, "girdle_radius", text='Radius Girdle')
col.prop(self, "table_radius", text='Table')
col = layout.column(align=True)
col.prop(self, "crown_height", text='Height Crown')
col.prop(self, "pavilion_height", text='Pavilion')
if self.change == False:
layout.separator()
draw_transform_props(self, layout)
def execute(self, context):
# turn off 'Enter Edit Mode'
use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
bpy.context.preferences.edit.use_enter_edit_mode = False
if bpy.context.mode == "OBJECT":
if context.selected_objects != [] and context.active_object and \
(context.active_object.data is not None) and ('Diamond' in context.active_object.data.keys()) and \
(self.change == True):
obj = context.active_object
oldmesh = obj.data
oldmeshname = obj.data.name
verts, faces = add_diamond(self.segments,
self.girdle_radius,
self.table_radius,
self.crown_height,
self.pavilion_height)
mesh = bpy.data.meshes.new("TMP")
mesh.from_pydata(verts, [], faces)
mesh.update()
obj.data = mesh
for material in oldmesh.materials:
obj.data.materials.append(material)
bpy.data.meshes.remove(oldmesh)
obj.data.name = oldmeshname
else:
verts, faces = add_diamond(self.segments,
self.girdle_radius,
self.table_radius,
self.crown_height,
self.pavilion_height)
obj = create_mesh_object(context, self, verts, [], faces, "Diamond")
obj.data["Diamond"] = True
obj.data["change"] = False
for prm in DiamondParameters():
obj.data[prm] = getattr(self, prm)
if bpy.context.mode == "EDIT_MESH":
active_object = context.active_object
name_active_object = active_object.name
bpy.ops.object.mode_set(mode='OBJECT')
verts, faces = add_diamond(self.segments,
self.girdle_radius,
self.table_radius,
self.crown_height,
self.pavilion_height)
obj = create_mesh_object(context, self, verts, [], faces, "TMP")
obj.select_set(True)
active_object.select_set(True)
bpy.context.view_layer.objects.active = active_object
bpy.ops.object.join()
context.active_object.name = name_active_object
bpy.ops.object.mode_set(mode='EDIT')
if use_enter_edit_mode:
bpy.ops.object.mode_set(mode = 'EDIT')
# restore pre operator state
bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
return {'FINISHED'}
def DiamondParameters():
DiamondParameters = [
"segments",
"girdle_radius",
"table_radius",
"crown_height",
"pavilion_height",
]
return DiamondParameters
class AddGem(Operator, object_utils.AddObjectHelper):
bl_idname = "mesh.primitive_gem_add"
bl_label = "Add Gem"
bl_description = "Construct an offset faceted gem mesh"
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
Gem : BoolProperty(name = "Gem",
default = True,
description = "Gem")
#### change properties
name : StringProperty(name = "Name",
description = "Name")
change : BoolProperty(name = "Change",
default = False,
description = "change Gem")
segments: IntProperty(
name="Segments",
description="Longitudial segmentation",
min=3,
max=265,
default=8
)
pavilion_radius: FloatProperty(
name="Radius",
description="Radius of the gem",
min=0.01,
max=9999.0,
default=1.0
)
crown_radius: FloatProperty(
name="Table Radius",
description="Radius of the table(top)",
min=0.01,
max=9999.0,
default=0.6
)
crown_height: FloatProperty(
name="Table height",
description="Height of the top half",
min=0.01,
max=9999.0,
default=0.35
)
pavilion_height: FloatProperty(
name="Pavilion height",
description="Height of bottom half",
min=0.01,
max=9999.0,
default=0.8
)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
layout.separator()
layout.prop(self, "segments")
col = layout.column(align=True)
col.prop(self, "crown_radius", text='Radius Crown')
col.prop(self, "pavilion_radius", text='Pavilion')
col = layout.column(align=True)
col.prop(self, "crown_height", text='Height Crown')
col.prop(self, "pavilion_height", text='Pavilion')
if self.change == False:
layout.separator()
draw_transform_props(self, layout)
def execute(self, context):
# turn off 'Enter Edit Mode'
use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
bpy.context.preferences.edit.use_enter_edit_mode = False
if bpy.context.mode == "OBJECT":
if context.selected_objects != [] and context.active_object and \
(context.active_object.data is not None) and ('Gem' in context.active_object.data.keys()) and \
(self.change == True):
obj = context.active_object
oldmesh = obj.data
oldmeshname = obj.data.name
verts, faces = add_gem(
self.pavilion_radius,
self.crown_radius,
self.segments,
self.pavilion_height,
self.crown_height)
mesh = bpy.data.meshes.new("TMP")
mesh.from_pydata(verts, [], faces)
mesh.update()
obj.data = mesh
for material in oldmesh.materials:
obj.data.materials.append(material)
bpy.data.meshes.remove(oldmesh)
obj.data.name = oldmeshname
else:
verts, faces = add_gem(
self.pavilion_radius,
self.crown_radius,
self.segments,
self.pavilion_height,
self.crown_height)
obj = create_mesh_object(context, self, verts, [], faces, "Gem")
obj.data["Gem"] = True
obj.data["change"] = False
for prm in GemParameters():
obj.data[prm] = getattr(self, prm)
if bpy.context.mode == "EDIT_MESH":
active_object = context.active_object
name_active_object = active_object.name
bpy.ops.object.mode_set(mode='OBJECT')
verts, faces = add_gem(
self.pavilion_radius,
self.crown_radius,
self.segments,
self.pavilion_height,
self.crown_height)
obj = create_mesh_object(context, self, verts, [], faces, "TMP")
obj.select_set(True)
active_object.select_set(True)
bpy.context.view_layer.objects.active = active_object
bpy.ops.object.join()
context.active_object.name = name_active_object
bpy.ops.object.mode_set(mode='EDIT')
if use_enter_edit_mode:
bpy.ops.object.mode_set(mode = 'EDIT')
# restore pre operator state
bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
return {'FINISHED'}
def GemParameters():
GemParameters = [
"segments",
"pavilion_radius",
"crown_radius",
"crown_height",
"pavilion_height",
]
return GemParameters
@@ -0,0 +1,341 @@
# SPDX-FileCopyrightText: 2012-2022 Blender Foundation
#
# SPDX-License-Identifier: GPL-2.0-or-later
# Author: Kayo Phoenix
import bpy
from bpy_extras import object_utils
from math import (
pi, sin,
cos,
)
from bpy.props import (
IntProperty,
BoolProperty,
BoolVectorProperty,
FloatProperty,
FloatVectorProperty,
StringProperty,
)
from .interface import draw_transform_props
class honeycomb_geometry():
def __init__(self, rows, cols, D, E):
self.rows = rows
self.cols = cols
self.D = D
self.E = E
self.hE = 0.5 * self.E
self.R = 0.5 * self.D
self.a = sin(pi / 3)
self.d = self.a * self.D
self.hd = 0.5 * self.d
self.e = self.hE / self.a
self.he = 0.5 * self.e
self.r = self.R - self.e
self.hr = 0.5 * self.r
self.H = self.R * (1.5 * self.rows + 0.5) + self.e
if self.rows > 1:
self.W = self.d * (self.cols + 0.5) + self.E
else:
self.W = self.d * self.cols + self.E
self.hH = 0.5 * self.H
self.hW = 0.5 * self.W
self.sy = -self.hH + self.he + self.R
self.sx = -self.hW + self.hE + self.hd
self.gx = self.hd
self.dy = 1.5 * self.R
self.dx = self.d
def vert(self, row, col):
# full cell
if row >= 0 and row < self.rows and col >= 0 and col < self.cols:
return [0, 1, 2, 3, 4, 5]
# right down corner
if row == -1 and col == self.cols - 1:
return [1, 2]
if row == 0 and self.rows > 1 and col == self.cols:
return [1, 2, 3]
# left down corner
if row == -1 and col == -1:
return [0, 1]
if self.rows % 2:
# left up corner
if row == self.rows and col == -1:
return [4, 5]
# right up corner
if row == self.rows and col == self.cols - 1:
return [3, 4]
if row == self.rows - 1 and self.rows > 1 and col == self.cols:
return [2, 3, 4]
else:
# left up corner
if row == self.rows and col == 0:
return [4, 5]
if row == self.rows - 1 and self.rows > 1 and col == -1:
return [0, 4, 5]
# right up corner
if row == self.rows and col == self.cols:
return [3, 4]
# horizontal lines
if col >= 0 and col < self.cols:
if row == -1:
return [0, 1, 2]
if row == self.rows:
return [3, 4, 5]
# vertical lines
if row >= 0 and row < self.rows:
if col == -1:
if row % 2:
return [0, 1, 4, 5]
else:
return [0, 5]
if col == self.cols:
if row % 2 or self.rows == 1:
return [2, 3]
else:
return [1, 2, 3, 4]
return []
def cell(self, row, col, idx):
cp = [self.sx + self.dx * col, self.sy + self.dy * row, 0] # central point
if row % 2:
cp[0] += self.gx
co = [] # vertices coords
vi = self.vert(row, col)
ap = {}
for i in vi:
a = pi / 6 + i * pi / 3 # angle
ap[i] = idx + len(co)
co.append((cp[0] + cos(a) * self.r, cp[1] + sin(a) * self.r, cp[2]))
return co, ap
def generate(self):
ar = 1
ac = 1
cells = []
verts = []
faces = []
for row in range(-ar, self.rows + ar):
level = []
for col in range(-ac, self.cols + ac):
co, ap = self.cell(row, col, len(verts))
verts += co
level.append(ap)
cells.append(level)
# bottom row
row = 0
for col in range(1, len(cells[row]) - 1):
s = cells[row][col]
l = cells[row][col - 1]
u = cells[row + 1][col]
faces.append((s[1], u[5], u[4], s[2]))
faces.append((s[2], u[4], l[0]))
# top row
row = len(cells) - 1
cs = 0
if row % 2:
cs += 1
for col in range(1 + cs, len(cells[row]) - 1):
s = cells[row][col]
l = cells[row][col - 1]
d = cells[row - 1][col - cs]
faces.append((s[3], l[5], d[1]))
faces.append([s[3], d[1], d[0], s[4]])
# middle rows
for row in range(1, len(cells) - 1):
cs = 0
if row % 2:
cs += 1
for col in range(1, len(cells[row]) - 1):
s = cells[row][col]
l = cells[row][col - 1]
u = cells[row + 1][col - cs]
d = cells[row - 1][col - cs]
faces.append((s[1], u[5], u[4], s[2]))
faces.append((s[2], u[4], l[0]))
faces.append([s[2], l[0], l[5], s[3]])
faces.append((s[3], l[5], d[1]))
faces.append([s[3], d[1], d[0], s[4]])
# right column
row = 0
col = len(cells[row]) - 1
for row in range(1, len(cells) - 1):
cs = 0
if row % 2:
cs += 1
s = cells[row][col]
l = cells[row][col - 1]
u = cells[row + 1][col - cs]
d = cells[row - 1][col - cs]
if row % 2 and row < len(cells) - 2:
faces.append((s[1], u[5], u[4], s[2]))
faces.append((s[2], u[4], l[0]))
faces.append([s[2], l[0], l[5], s[3]])
faces.append((s[3], l[5], d[1]))
if row % 2 and row > 1:
faces.append([s[3], d[1], d[0], s[4]])
# final fix
if not self.rows % 2:
row = len(cells) - 1
s = cells[row][col]
l = cells[row][col - 1]
d = cells[row - 1][col - 1]
faces.append((s[3], l[5], d[1]))
faces.append([s[3], d[1], d[0], s[4]])
return verts, faces
def edge_max(diam):
return diam * sin(pi / 3)
class add_mesh_honeycomb(bpy.types.Operator, object_utils.AddObjectHelper):
bl_idname = "mesh.honeycomb_add"
bl_label = "Add Honeycomb"
bl_description = "Simple honeycomb mesh generator"
bl_options = {'REGISTER', 'UNDO'}
def fix_edge(self, context):
m = edge_max(self.diam)
if self.edge > m:
self.edge = m
HoneyComb : BoolProperty(name = "HoneyComb",
default = True,
description = "HoneyComb")
change : BoolProperty(name = "Change",
default = False,
description = "change HoneyComb")
rows: IntProperty(
name="Rows",
default=2,
min=1, max=100,
description='Number of the rows'
)
cols: IntProperty(
name='Columns',
default=2,
min=1, max=100,
description='Number of the columns'
)
diam: FloatProperty(
name='Cell Diameter',
default=1.0,
min=0.0, update=fix_edge,
description='Diameter of the cell'
)
edge: FloatProperty(
name='Edge Width',
default=0.1,
min=0.0, update=fix_edge,
description='Width of the edge'
)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
layout.prop(self, 'rows', expand=True)
layout.prop(self, 'cols', expand=True)
layout.prop(self, 'diam', expand=True)
layout.prop(self, 'edge', expand=True)
if self.change == False:
layout.separator()
draw_transform_props(self, layout)
@classmethod
def poll(cls, context):
return context.scene is not None
def execute(self, context):
# turn off 'Enter Edit Mode'
use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
bpy.context.preferences.edit.use_enter_edit_mode = False
if bpy.context.mode == "OBJECT":
if context.selected_objects != [] and context.active_object and \
(context.active_object.data is not None) and ('HoneyComb' in context.active_object.data.keys()) and \
(self.change == True):
obj = context.active_object
oldmesh = obj.data
oldmeshname = obj.data.name
comb = honeycomb_geometry(self.rows, self.cols, self.diam, self.edge)
verts, faces = comb.generate()
mesh = bpy.data.meshes.new('HoneyComb')
mesh.from_pydata(verts, [], faces)
obj.data = mesh
for material in oldmesh.materials:
obj.data.materials.append(material)
bpy.data.meshes.remove(oldmesh)
obj.data.name = oldmeshname
else:
comb = honeycomb_geometry(self.rows, self.cols, self.diam, self.edge)
verts, faces = comb.generate()
mesh = bpy.data.meshes.new('HoneyComb')
mesh.from_pydata(verts, [], faces)
obj = object_utils.object_data_add(context, mesh, operator=self)
obj.data["HoneyComb"] = True
obj.data["change"] = False
for prm in HoneyCombParameters():
obj.data[prm] = getattr(self, prm)
if bpy.context.mode == "EDIT_MESH":
active_object = context.active_object
name_active_object = active_object.name
bpy.ops.object.mode_set(mode='OBJECT')
comb = honeycomb_geometry(self.rows, self.cols, self.diam, self.edge)
verts, faces = comb.generate()
mesh = bpy.data.meshes.new('HoneyComb')
mesh.from_pydata(verts, [], faces)
obj = object_utils.object_data_add(context, mesh, operator=self)
obj.select_set(True)
active_object.select_set(True)
bpy.context.view_layer.objects.active = active_object
bpy.ops.object.join()
context.active_object.name = name_active_object
bpy.ops.object.mode_set(mode='EDIT')
if use_enter_edit_mode:
bpy.ops.object.mode_set(mode = 'EDIT')
# restore pre operator state
bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
return {'FINISHED'}
def HoneyCombParameters():
HoneyCombParameters = [
"rows",
"cols",
"diam",
"edge",
]
return HoneyCombParameters
@@ -0,0 +1,198 @@
# SPDX-FileCopyrightText: 2015 Sugiany
#
# SPDX-License-Identifier: MIT
import bpy
from bpy_extras.object_utils import (
AddObjectHelper,
object_data_add,
)
from bpy.props import (
IntProperty,
BoolProperty,
BoolVectorProperty,
FloatVectorProperty,
FloatProperty,
)
import mathutils
import copy
from .interface import draw_transform_props
class MengerSponge(object):
FACE_INDICES = [
[3, 7, 4, 0],
[5, 6, 2, 1],
[1, 2, 3, 0],
[7, 6, 5, 4],
[4, 5, 1, 0],
[2, 6, 7, 3],
]
def __init__(self, level):
self.__level = level
self.__max_point_number = 3 ** level
self.__vertices_map = {}
self.__indices = []
self.__face_visibility = {}
self.__faces = []
for x in range(3):
for y in range(3):
for z in range(3):
self.__face_visibility[(x, y, z)] = [
x == 0 or x == 2 and (y == 1 or z == 1),
x == 2 or x == 0 and (y == 1 or z == 1),
y == 0 or y == 2 and (x == 1 or z == 1),
y == 2 or y == 0 and (x == 1 or z == 1),
z == 0 or z == 2 and (y == 1 or x == 1),
z == 2 or z == 0 and (y == 1 or x == 1),
]
def create(self, width, height):
m = self.__max_point_number
points = [
(0, 0, 0),
(m, 0, 0),
(m, 0, m),
(0, 0, m),
(0, m, 0),
(m, m, 0),
(m, m, m),
(0, m, m),
]
self.__make_sub_sponge(points, None, self.__level)
vertices = self.__make_vertices(width, height)
return vertices, self.__faces
def __get_vindex(self, p):
if p in self.__vertices_map:
return self.__vertices_map[p]
index = len(self.__vertices_map)
self.__vertices_map[p] = index
return index
def __make_vertices(self, width, height):
vertices = [None] * len(self.__vertices_map)
w2 = width / 2
h2 = height / 2
w_step = width / self.__max_point_number
h_step = height / self.__max_point_number
for p, i in sorted(self.__vertices_map.items(), key=lambda x: x[1]):
vertices[i] = mathutils.Vector([
p[0] * w_step - w2,
p[1] * w_step - w2,
p[2] * h_step - h2,
])
return vertices
def __make_sub_sponge(self, cur_points, face_vis, depth):
if depth <= 0:
if not face_vis:
face_vis = [True] * 6
cur_point_indices = []
for p in cur_points:
cur_point_indices.append(self.__get_vindex(p))
for i, vis in enumerate(face_vis):
if vis:
f = []
for vi in self.FACE_INDICES[i]:
f.append(cur_point_indices[vi])
self.__faces.append(f)
return
base = cur_points[0]
width = (cur_points[1][0] - base[0]) / 3
local_vert_map = {}
for z in range(4):
for y in range(4):
for x in range(4):
local_vert_map[(x, y, z)] = (
width * x + base[0],
width * y + base[1],
width * z + base[2],
)
for x in range(3):
for y in range(3):
for z in range(3):
if [x, y, z].count(1) > 1:
continue
next_points = [
local_vert_map[(x, y, z)],
local_vert_map[(x + 1, y, z)],
local_vert_map[(x + 1, y, z + 1)],
local_vert_map[(x, y, z + 1)],
local_vert_map[(x, y + 1, z)],
local_vert_map[(x + 1, y + 1, z)],
local_vert_map[(x + 1, y + 1, z + 1)],
local_vert_map[(x, y + 1, z + 1)],
]
visibility = copy.copy(self.__face_visibility[(x, y, z)])
if face_vis:
visibility[0] = visibility[0] and (face_vis[0] or x != 0)
visibility[1] = visibility[1] and (face_vis[1] or x != 2)
visibility[2] = visibility[2] and (face_vis[2] or y != 0)
visibility[3] = visibility[3] and (face_vis[3] or y != 2)
visibility[4] = visibility[4] and (face_vis[4] or z != 0)
visibility[5] = visibility[5] and (face_vis[5] or z != 2)
self.__make_sub_sponge(
next_points,
visibility,
depth - 1)
class AddMengerSponge(bpy.types.Operator, AddObjectHelper):
bl_idname = "mesh.menger_sponge_add"
bl_label = "Menger Sponge"
bl_description = "Construct a menger sponge mesh"
bl_options = {'REGISTER', 'UNDO'}
level: IntProperty(
name="Level",
description="Sponge Level",
min=0, max=4,
default=1,
)
radius: FloatProperty(
name="Width",
description="Sponge Radius",
min=0.01, max=100.0,
default=1.0,
)
layers: BoolVectorProperty(
name="Layers",
size=20,
subtype='LAYER',
options={'HIDDEN', 'SKIP_SAVE'},
)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
layout.prop(self, 'level')
layout.prop(self, 'radius')
layout.separator()
draw_transform_props(self, layout)
def execute(self, context):
sponger = MengerSponge(self.level)
vertices, faces = sponger.create(self.radius * 2, self.radius * 2)
del sponger
mesh = bpy.data.meshes.new(name='Sponge')
mesh.from_pydata(vertices, [], faces)
uvs = [(0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0)]
mesh.uv_layers.new()
for i, uvloop in enumerate(mesh.uv_layers.active.data):
uvloop.uv = uvs[i % 4]
object_data_add(context, mesh, operator=self)
return {'FINISHED'}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,223 @@
# SPDX-FileCopyrightText: 2011-2022 Blender Foundation
#
# SPDX-License-Identifier: GPL-2.0-or-later
# Author: Phil Cote, cotejrp1, (http://www.blenderaddons.com)
import bpy
import bmesh
from bpy.props import (
FloatProperty,
IntProperty,
StringProperty,
BoolProperty,
)
from math import pi
from mathutils import (
Quaternion,
Vector,
)
from bpy_extras import object_utils
from .interface import draw_transform_props
def create_step(width, base_level, step_height, num_sides):
axis = [0, 0, -1]
PI2 = pi * 2
rad = width / 2
quat_angles = [(cur_side / num_sides) * PI2
for cur_side in range(num_sides)]
quaternions = [Quaternion(axis, quat_angle)
for quat_angle in quat_angles]
init_vectors = [Vector([rad, 0, base_level])] * len(quaternions)
quat_vector_pairs = list(zip(quaternions, init_vectors))
vectors = [quaternion @ vec for quaternion, vec in quat_vector_pairs]
bottom_list = [(vec.x, vec.y, vec.z) for vec in vectors]
top_list = [(vec.x, vec.y, vec.z + step_height) for vec in vectors]
full_list = bottom_list + top_list
return full_list
def split_list(l, n):
"""
split the blocks up. Credit to oremj for this one.
http://stackoverflow.com/questions/312443/how-do-you-split-a-list-into-evenly-sized-chunks-in-python
"""
n *= 2
returned_list = [l[i: i + n] for i in range(0, len(l), n)]
return returned_list
def get_connector_pairs(lst, n_sides):
# chop off the verts that get used for the base and top
lst = lst[n_sides:]
lst = lst[:-n_sides]
lst = split_list(lst, n_sides)
return lst
def pyramid_mesh(self, context):
all_verts = []
height_offset = 0
cur_width = self.width
for i in range(self.num_steps):
verts_loc = create_step(cur_width, height_offset, self.height,
self.num_sides)
height_offset += self.height
cur_width -= self.reduce_by
all_verts.extend(verts_loc)
mesh = bpy.data.meshes.new("Pyramid")
bm = bmesh.new()
for v_co in all_verts:
bm.verts.new(v_co)
def add_faces(n, block_vert_sets):
for bvs in block_vert_sets:
for i in range(self.num_sides - 1):
bm.faces.new([bvs[i], bvs[i + n], bvs[i + n + 1], bvs[i + 1]])
bm.faces.new([bvs[n - 1], bvs[(n * 2) - 1], bvs[n], bvs[0]])
# get the base and cap faces done.
bm.faces.new(bm.verts[0:self.num_sides])
bm.faces.new(reversed(bm.verts[-self.num_sides:])) # otherwise normal faces intern... T44619.
# side faces
block_vert_sets = split_list(bm.verts, self.num_sides)
add_faces(self.num_sides, block_vert_sets)
# connector faces between faces and faces of the block above it.
connector_pairs = get_connector_pairs(bm.verts, self.num_sides)
add_faces(self.num_sides, connector_pairs)
bm.to_mesh(mesh)
mesh.update()
return mesh
class AddPyramid(bpy.types.Operator, object_utils.AddObjectHelper):
bl_idname = "mesh.primitive_steppyramid_add"
bl_label = "Pyramid"
bl_description = "Construct a step pyramid mesh"
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
Pyramid : BoolProperty(name = "Pyramid",
default = True,
description = "Pyramid")
change : BoolProperty(name = "Change",
default = False,
description = "change Pyramid")
num_sides: IntProperty(
name="Sides",
description="How many sides each step will have",
min=3,
default=4
)
num_steps: IntProperty(
name="Steps",
description="How many steps for the overall pyramid",
min=1,
default=10
)
width: FloatProperty(
name="Width",
description="Initial base step width",
min=0.01,
default=2
)
height: FloatProperty(
name="Height",
description="How tall each step will be",
min=0.01,
default=0.1
)
reduce_by: FloatProperty(
name="Taper",
description="How much to reduce each succeeding step by",
min=.01,
default=.20
)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
layout.separator()
layout.prop(self, 'num_sides')
layout.prop(self, 'num_steps')
layout.prop(self, 'width')
layout.prop(self, 'height')
layout.prop(self, 'reduce_by')
if self.change == False:
layout.separator()
draw_transform_props(self, layout)
def execute(self, context):
# turn off 'Enter Edit Mode'
use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
bpy.context.preferences.edit.use_enter_edit_mode = False
if bpy.context.mode == "OBJECT":
if context.selected_objects != [] and context.active_object and \
(context.active_object.data is not None) and ('Pyramid' in context.active_object.data.keys()) and \
(self.change == True):
obj = context.active_object
oldmesh = obj.data
oldmeshname = obj.data.name
obj.data = pyramid_mesh(self, context)
for material in oldmesh.materials:
obj.data.materials.append(material)
bpy.data.meshes.remove(oldmesh)
obj.data.name = oldmeshname
else:
mesh = pyramid_mesh(self, context)
obj = object_utils.object_data_add(context, mesh, operator=self)
obj.data["Pyramid"] = True
obj.data["change"] = False
for prm in PyramidParameters():
obj.data[prm] = getattr(self, prm)
if bpy.context.mode == "EDIT_MESH":
active_object = context.active_object
name_active_object = active_object.name
bpy.ops.object.mode_set(mode='OBJECT')
mesh = pyramid_mesh(self, context)
obj = object_utils.object_data_add(context, mesh, operator=self)
obj.select_set(True)
active_object.select_set(True)
bpy.context.view_layer.objects.active = active_object
bpy.ops.object.join()
context.active_object.name = name_active_object
bpy.ops.object.mode_set(mode='EDIT')
if use_enter_edit_mode:
bpy.ops.object.mode_set(mode = 'EDIT')
# restore pre operator state
bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
return {'FINISHED'}
def PyramidParameters():
PyramidParameters = [
"num_sides",
"num_steps",
"width",
"height",
"reduce_by",
]
return PyramidParameters
@@ -0,0 +1,36 @@
# SPDX-FileCopyrightText: 2019-2022 Blender Foundation
#
# SPDX-License-Identifier: GPL-2.0-or-later
# Paul "BrikBot" Marshall
# Created: July 1, 2011
# Last Modified: September 26, 2013
# Homepage (blog): http://post.darkarsenic.com/
# //blog.darkarsenic.com/
# Thanks to Meta-Androco, RickyBlender, Ace Dragon, and PKHG for ideas
# and testing.
#
# Coded in IDLE, tested in Blender 2.68a. NumPy Recommended.
# Search for "@todo" to quickly find sections that need work.
if "bpy" in locals():
import importlib
importlib.reload(rockgen)
else:
from . import rockgen
import bpy
# Register:
def register():
rockgen.register()
def unregister():
rockgen.unregister()
if __name__ == "__main__":
register()
@@ -0,0 +1,403 @@
<?xml version="1.0" ?>
<!DOCTYPE settings [
<!ELEMENT settings (default,preset*)>
<!ELEMENT default (title,size,shape,material,random)>
<!ELEMENT preset (title,size,shape,material,random)>
<!ELEMENT title (#PCDATA)>
<!ELEMENT size (scale+,skew+,use_scale_dis,scale_fac)>
<!ELEMENT scale (axis,lower,upper)>
<!ELEMENT axis (#PCDATA)>
<!ELEMENT lower (#PCDATA)>
<!ELEMENT upper (#PCDATA)>
<!ELEMENT skew (axis,value)>
<!ELEMENT value (#PCDATA)>
<!ELEMENT use_scale_dis (#PCDATA)>
<!ELEMENT scale_fac (#PCDATA)>
<!ELEMENT shape (deform,rough,detail,display_detail,smooth_fac,smooth_it)>
<!ELEMENT deform (#PCDATA)>
<!ELEMENT rough (#PCDATA)>
<!ELEMENT detail (#PCDATA)>
<!ELEMENT display_detail (#PCDATA)>
<!ELEMENT smooth_fac (#PCDATA)>
<!ELEMENT smooth_it (#PCDATA)>
<!ELEMENT material (mat_enable,mat_color,mat_bright,mat_rough,mat_spec,mat_hard,mat_use_trans,mat_alpha,mat_cloudy,mat_IOR,mat_mossy)>
<!ELEMENT mat_enable (#PCDATA)>
<!ELEMENT mat_color (#PCDATA)>
<!ELEMENT mat_bright (#PCDATA)>
<!ELEMENT mat_rough (#PCDATA)>
<!ELEMENT mat_spec (#PCDATA)>
<!ELEMENT mat_hard (#PCDATA)>
<!ELEMENT mat_use_trans (#PCDATA)>
<!ELEMENT mat_alpha (#PCDATA)>
<!ELEMENT mat_cloudy (#PCDATA)>
<!ELEMENT mat_IOR (#PCDATA)>
<!ELEMENT mat_mossy (#PCDATA)>
<!ELEMENT random (use_random_seed,user_seed)>
<!ELEMENT use_generate (#PCDATA)>
<!ELEMENT use_random_seed (#PCDATA)>
<!ELEMENT user_seed (#PCDATA)>
<!ATTLIST preset id ID #REQUIRED>
]>
<settings>
<default>
<title>Default</title>
<size>
<scale>
<axis>X</axis>
<lower>1.0</lower>
<upper>1.0</upper>
</scale>
<scale>
<axis>Y</axis>
<lower>1.0</lower>
<upper>1.0</upper>
</scale>
<scale>
<axis>Z</axis>
<lower>1.0</lower>
<upper>1.0</upper>
</scale>
<skew>
<axis>X</axis>
<value>0.0</value>
</skew>
<skew>
<axis>Y</axis>
<value>0.0</value>
</skew>
<skew>
<axis>Z</axis>
<value>0.0</value>
</skew>
<use_scale_dis>False</use_scale_dis>
<scale_fac>[1.0, 1.0, 1.0]</scale_fac>
</size>
<shape>
<deform>5.0</deform>
<rough>2.5</rough>
<detail>3</detail>
<display_detail>2</display_detail>
<smooth_fac>0.0</smooth_fac>
<smooth_it>0</smooth_it>
</shape>
<material>
<mat_enable>False</mat_enable>
<mat_color>[0.5, 0.5, 0.5]</mat_color>
<mat_bright>0.85</mat_bright>
<mat_rough>1.0</mat_rough>
<mat_spec>0.2</mat_spec>
<mat_hard>50</mat_hard>
<mat_use_trans>False</mat_use_trans>
<mat_alpha>0.0</mat_alpha>
<mat_cloudy>0.0</mat_cloudy>
<mat_IOR>1.0</mat_IOR>
<mat_mossy>0.0</mat_mossy>
</material>
<random>
<use_generate>True</use_generate>
<use_random_seed>True</use_random_seed>
<user_seed>1</user_seed>
</random>
</default>
<preset id="1">
<title>River Rock</title>
<size>
<scale>
<axis>X</axis>
<lower>0.5</lower>
<upper>1.25</upper>
</scale>
<scale>
<axis>Y</axis>
<lower>0.5</lower>
<upper>1.25</upper>
</scale>
<scale>
<axis>Z</axis>
<lower>0.5</lower>
<upper>1.25</upper>
</scale>
<skew>
<axis>X</axis>
<value>-0.5</value>
</skew>
<skew>
<axis>Y</axis>
<value>-0.5</value>
</skew>
<skew>
<axis>Z</axis>
<value>-0.5</value>
</skew>
<use_scale_dis>False</use_scale_dis>
<scale_fac>[1.0, 1.0, 1.0]</scale_fac>
</size>
<shape>
<deform>3.0</deform>
<rough>2.0</rough>
<detail>2</detail>
<display_detail>2</display_detail>
<smooth_fac>2.0</smooth_fac>
<smooth_it>2</smooth_it>
</shape>
<material>
<mat_enable>True</mat_enable>
<mat_color>[0.5, 0.5, 0.5]</mat_color>
<mat_bright>0.85</mat_bright>
<mat_rough>0.125</mat_rough>
<mat_spec>0.5</mat_spec>
<mat_hard>50</mat_hard>
<mat_use_trans>False</mat_use_trans>
<mat_alpha>0.0</mat_alpha>
<mat_cloudy>0.0</mat_cloudy>
<mat_IOR>1.0</mat_IOR>
<mat_mossy>0.0</mat_mossy>
</material>
<random>
<use_generate>True</use_generate>
<use_random_seed>True</use_random_seed>
<user_seed>1</user_seed>
</random>
</preset>
<preset id="2">
<title>Asteroid</title>
<size>
<scale>
<axis>X</axis>
<lower>1.0</lower>
<upper>5.0</upper>
</scale>
<scale>
<axis>Y</axis>
<lower>1.0</lower>
<upper>5.0</upper>
</scale>
<scale>
<axis>Z</axis>
<lower>1.0</lower>
<upper>5.0</upper>
</scale>
<skew>
<axis>X</axis>
<value>0.0</value>
</skew>
<skew>
<axis>Y</axis>
<value>0.0</value>
</skew>
<skew>
<axis>Z</axis>
<value>0.0</value>
</skew>
<use_scale_dis>False</use_scale_dis>
<scale_fac>[1.0, 1.0, 1.0]</scale_fac>
</size>
<shape>
<deform>7.5</deform>
<rough>3.0</rough>
<detail>4</detail>
<display_detail>3</display_detail>
<smooth_fac>0.0</smooth_fac>
<smooth_it>0</smooth_it>
</shape>
<material>
<mat_enable>True</mat_enable>
<mat_color>[0.3, 0.25, 0.2]</mat_color>
<mat_bright>0.85</mat_bright>
<mat_rough>1.5</mat_rough>
<mat_spec>0.25</mat_spec>
<mat_hard>30</mat_hard>
<mat_use_trans>False</mat_use_trans>
<mat_alpha>0.0</mat_alpha>
<mat_cloudy>0.0</mat_cloudy>
<mat_IOR>1.0</mat_IOR>
<mat_mossy>0.0</mat_mossy>
</material>
<random>
<use_generate>True</use_generate>
<use_random_seed>True</use_random_seed>
<user_seed>1</user_seed>
</random>
</preset>
<preset id="3">
<title>Sandstone</title>
<size>
<scale>
<axis>X</axis>
<lower>1.0</lower>
<upper>1.0</upper>
</scale>
<scale>
<axis>Y</axis>
<lower>1.0</lower>
<upper>1.0</upper>
</scale>
<scale>
<axis>Z</axis>
<lower>1.0</lower>
<upper>1.0</upper>
</scale>
<skew>
<axis>X</axis>
<value>0.0</value>
</skew>
<skew>
<axis>Y</axis>
<value>0.0</value>
</skew>
<skew>
<axis>Z</axis>
<value>0.0</value>
</skew>
<use_scale_dis>True</use_scale_dis>
<scale_fac>[5.0, 5.0, 0.1]</scale_fac>
</size>
<shape>
<deform>0.5</deform>
<rough>1.0</rough>
<detail>3</detail>
<display_detail>3</display_detail>
<smooth_fac>2.0</smooth_fac>
<smooth_it>2</smooth_it>
</shape>
<material>
<mat_enable>True</mat_enable>
<mat_color>[0.5, 0.4, 0.35]</mat_color>
<mat_bright>0.85</mat_bright>
<mat_rough>0.1</mat_rough>
<mat_spec>0.2</mat_spec>
<mat_hard>50</mat_hard>
<mat_use_trans>False</mat_use_trans>
<mat_alpha>0.0</mat_alpha>
<mat_cloudy>0.0</mat_cloudy>
<mat_IOR>1.0</mat_IOR>
<mat_mossy>0.0</mat_mossy>
</material>
<random>
<use_generate>True</use_generate>
<use_random_seed>True</use_random_seed>
<user_seed>1</user_seed>
</random>
</preset>
<preset id="4">
<title>Ice</title>
<size>
<scale>
<axis>X</axis>
<lower>0.0</lower>
<upper>2.0</upper>
</scale>
<scale>
<axis>Y</axis>
<lower>0.0</lower>
<upper>2.0</upper>
</scale>
<scale>
<axis>Z</axis>
<lower>0.0</lower>
<upper>2.0</upper>
</scale>
<skew>
<axis>X</axis>
<value>0.0</value>
</skew>
<skew>
<axis>Y</axis>
<value>0.0</value>
</skew>
<skew>
<axis>Z</axis>
<value>0.0</value>
</skew>
<use_scale_dis>False</use_scale_dis>
<scale_fac>[1.0, 1.0, 1.0]</scale_fac>
</size>
<shape>
<deform>5.0</deform>
<rough>1.0</rough>
<detail>3</detail>
<display_detail>2</display_detail>
<smooth_fac>2.0</smooth_fac>
<smooth_it>1</smooth_it>
</shape>
<material>
<mat_enable>True</mat_enable>
<mat_color>[0.9, 0.95, 1.0]</mat_color>
<mat_bright>0.85</mat_bright>
<mat_rough>0.25</mat_rough>
<mat_spec>0.2</mat_spec>
<mat_hard>50</mat_hard>
<mat_use_trans>True</mat_use_trans>
<mat_alpha>0.9</mat_alpha>
<mat_cloudy>0.1</mat_cloudy>
<mat_IOR>1.31</mat_IOR>
<mat_mossy>0.0</mat_mossy>
</material>
<random>
<use_generate>True</use_generate>
<use_random_seed>True</use_random_seed>
<user_seed>1</user_seed>
</random>
</preset>
<preset id="5">
<title>Fake Ocean</title>
<size>
<scale>
<axis>X</axis>
<lower>10.0</lower>
<upper>10.0</upper>
</scale>
<scale>
<axis>Y</axis>
<lower>10.0</lower>
<upper>10.0</upper>
</scale>
<scale>
<axis>Z</axis>
<lower>0.0</lower>
<upper>0.0</upper>
</scale>
<skew>
<axis>X</axis>
<value>0.0</value>
</skew>
<skew>
<axis>Y</axis>
<value>0.0</value>
</skew>
<skew>
<axis>Z</axis>
<value>0.0</value>
</skew>
<use_scale_dis>False</use_scale_dis>
<scale_fac>[1.0, 1.0, 1.0]</scale_fac>
</size>
<shape>
<deform>7.5</deform>
<rough>3.0</rough>
<detail>4</detail>
<display_detail>3</display_detail>
<smooth_fac>0.0</smooth_fac>
<smooth_it>0</smooth_it>
</shape>
<material>
<mat_enable>True</mat_enable>
<mat_color>[0.1, 0.12, 0.125]</mat_color>
<mat_bright>0.85</mat_bright>
<mat_rough>1.5</mat_rough>
<mat_spec>0.25</mat_spec>
<mat_hard>30</mat_hard>
<mat_use_trans>True</mat_use_trans>
<mat_alpha>0.5</mat_alpha>
<mat_cloudy>0.5</mat_cloudy>
<mat_IOR>1.333</mat_IOR>
<mat_mossy>0.0</mat_mossy>
</material>
<random>
<use_generate>True</use_generate>
<use_random_seed>True</use_random_seed>
<user_seed>1</user_seed>
</random>
</preset>
</settings>
@@ -0,0 +1,403 @@
<?xml version="1.0" ?>
<!DOCTYPE settings [
<!ELEMENT settings (default,preset*)>
<!ELEMENT default (title,size,shape,material,random)>
<!ELEMENT preset (title,size,shape,material,random)>
<!ELEMENT title (#PCDATA)>
<!ELEMENT size (scale+,skew+,use_scale_dis,scale_fac)>
<!ELEMENT scale (axis,lower,upper)>
<!ELEMENT axis (#PCDATA)>
<!ELEMENT lower (#PCDATA)>
<!ELEMENT upper (#PCDATA)>
<!ELEMENT skew (axis,value)>
<!ELEMENT value (#PCDATA)>
<!ELEMENT use_scale_dis (#PCDATA)>
<!ELEMENT scale_fac (#PCDATA)>
<!ELEMENT shape (deform,rough,detail,display_detail,smooth_fac,smooth_it)>
<!ELEMENT deform (#PCDATA)>
<!ELEMENT rough (#PCDATA)>
<!ELEMENT detail (#PCDATA)>
<!ELEMENT display_detail (#PCDATA)>
<!ELEMENT smooth_fac (#PCDATA)>
<!ELEMENT smooth_it (#PCDATA)>
<!ELEMENT material (mat_enable,mat_color,mat_bright,mat_rough,mat_spec,mat_hard,mat_use_trans,mat_alpha,mat_cloudy,mat_IOR,mat_mossy)>
<!ELEMENT mat_enable (#PCDATA)>
<!ELEMENT mat_color (#PCDATA)>
<!ELEMENT mat_bright (#PCDATA)>
<!ELEMENT mat_rough (#PCDATA)>
<!ELEMENT mat_spec (#PCDATA)>
<!ELEMENT mat_hard (#PCDATA)>
<!ELEMENT mat_use_trans (#PCDATA)>
<!ELEMENT mat_alpha (#PCDATA)>
<!ELEMENT mat_cloudy (#PCDATA)>
<!ELEMENT mat_IOR (#PCDATA)>
<!ELEMENT mat_mossy (#PCDATA)>
<!ELEMENT random (use_random_seed,user_seed)>
<!ELEMENT use_generate (#PCDATA)>
<!ELEMENT use_random_seed (#PCDATA)>
<!ELEMENT user_seed (#PCDATA)>
<!ATTLIST preset id ID #REQUIRED>
]>
<settings>
<default>
<title>Default</title>
<size>
<scale>
<axis>X</axis>
<lower>1.0</lower>
<upper>1.0</upper>
</scale>
<scale>
<axis>Y</axis>
<lower>1.0</lower>
<upper>1.0</upper>
</scale>
<scale>
<axis>Z</axis>
<lower>1.0</lower>
<upper>1.0</upper>
</scale>
<skew>
<axis>X</axis>
<value>0.0</value>
</skew>
<skew>
<axis>Y</axis>
<value>0.0</value>
</skew>
<skew>
<axis>Z</axis>
<value>0.0</value>
</skew>
<use_scale_dis>False</use_scale_dis>
<scale_fac>[1.0, 1.0, 1.0]</scale_fac>
</size>
<shape>
<deform>5.0</deform>
<rough>2.5</rough>
<detail>3</detail>
<display_detail>2</display_detail>
<smooth_fac>0.0</smooth_fac>
<smooth_it>0</smooth_it>
</shape>
<material>
<mat_enable>False</mat_enable>
<mat_color>[0.5, 0.5, 0.5]</mat_color>
<mat_bright>0.85</mat_bright>
<mat_rough>1.0</mat_rough>
<mat_spec>0.2</mat_spec>
<mat_hard>50</mat_hard>
<mat_use_trans>False</mat_use_trans>
<mat_alpha>0.0</mat_alpha>
<mat_cloudy>0.0</mat_cloudy>
<mat_IOR>1.0</mat_IOR>
<mat_mossy>0.0</mat_mossy>
</material>
<random>
<use_generate>True</use_generate>
<use_random_seed>True</use_random_seed>
<user_seed>1</user_seed>
</random>
</default>
<preset id="1">
<title>River Rock</title>
<size>
<scale>
<axis>X</axis>
<lower>0.5</lower>
<upper>1.25</upper>
</scale>
<scale>
<axis>Y</axis>
<lower>0.5</lower>
<upper>1.25</upper>
</scale>
<scale>
<axis>Z</axis>
<lower>0.5</lower>
<upper>1.25</upper>
</scale>
<skew>
<axis>X</axis>
<value>-0.5</value>
</skew>
<skew>
<axis>Y</axis>
<value>-0.5</value>
</skew>
<skew>
<axis>Z</axis>
<value>-0.5</value>
</skew>
<use_scale_dis>False</use_scale_dis>
<scale_fac>[1.0, 1.0, 1.0]</scale_fac>
</size>
<shape>
<deform>3.0</deform>
<rough>2.0</rough>
<detail>2</detail>
<display_detail>2</display_detail>
<smooth_fac>2.0</smooth_fac>
<smooth_it>2</smooth_it>
</shape>
<material>
<mat_enable>True</mat_enable>
<mat_color>[0.5, 0.5, 0.5]</mat_color>
<mat_bright>0.85</mat_bright>
<mat_rough>0.125</mat_rough>
<mat_spec>0.5</mat_spec>
<mat_hard>50</mat_hard>
<mat_use_trans>False</mat_use_trans>
<mat_alpha>0.0</mat_alpha>
<mat_cloudy>0.0</mat_cloudy>
<mat_IOR>1.0</mat_IOR>
<mat_mossy>0.0</mat_mossy>
</material>
<random>
<use_generate>True</use_generate>
<use_random_seed>True</use_random_seed>
<user_seed>1</user_seed>
</random>
</preset>
<preset id="2">
<title>Asteroid</title>
<size>
<scale>
<axis>X</axis>
<lower>1.0</lower>
<upper>5.0</upper>
</scale>
<scale>
<axis>Y</axis>
<lower>1.0</lower>
<upper>5.0</upper>
</scale>
<scale>
<axis>Z</axis>
<lower>1.0</lower>
<upper>5.0</upper>
</scale>
<skew>
<axis>X</axis>
<value>0.0</value>
</skew>
<skew>
<axis>Y</axis>
<value>0.0</value>
</skew>
<skew>
<axis>Z</axis>
<value>0.0</value>
</skew>
<use_scale_dis>False</use_scale_dis>
<scale_fac>[1.0, 1.0, 1.0]</scale_fac>
</size>
<shape>
<deform>7.5</deform>
<rough>3.0</rough>
<detail>4</detail>
<display_detail>3</display_detail>
<smooth_fac>0.0</smooth_fac>
<smooth_it>0</smooth_it>
</shape>
<material>
<mat_enable>True</mat_enable>
<mat_color>[0.3, 0.25, 0.2]</mat_color>
<mat_bright>0.85</mat_bright>
<mat_rough>1.5</mat_rough>
<mat_spec>0.25</mat_spec>
<mat_hard>30</mat_hard>
<mat_use_trans>False</mat_use_trans>
<mat_alpha>0.0</mat_alpha>
<mat_cloudy>0.0</mat_cloudy>
<mat_IOR>1.0</mat_IOR>
<mat_mossy>0.0</mat_mossy>
</material>
<random>
<use_generate>True</use_generate>
<use_random_seed>True</use_random_seed>
<user_seed>1</user_seed>
</random>
</preset>
<preset id="3">
<title>Sandstone</title>
<size>
<scale>
<axis>X</axis>
<lower>1.0</lower>
<upper>1.0</upper>
</scale>
<scale>
<axis>Y</axis>
<lower>1.0</lower>
<upper>1.0</upper>
</scale>
<scale>
<axis>Z</axis>
<lower>1.0</lower>
<upper>1.0</upper>
</scale>
<skew>
<axis>X</axis>
<value>0.0</value>
</skew>
<skew>
<axis>Y</axis>
<value>0.0</value>
</skew>
<skew>
<axis>Z</axis>
<value>0.0</value>
</skew>
<use_scale_dis>True</use_scale_dis>
<scale_fac>[5.0, 5.0, 0.1]</scale_fac>
</size>
<shape>
<deform>0.5</deform>
<rough>1.0</rough>
<detail>3</detail>
<display_detail>3</display_detail>
<smooth_fac>2.0</smooth_fac>
<smooth_it>2</smooth_it>
</shape>
<material>
<mat_enable>True</mat_enable>
<mat_color>[0.5, 0.4, 0.35]</mat_color>
<mat_bright>0.85</mat_bright>
<mat_rough>0.1</mat_rough>
<mat_spec>0.2</mat_spec>
<mat_hard>50</mat_hard>
<mat_use_trans>False</mat_use_trans>
<mat_alpha>0.0</mat_alpha>
<mat_cloudy>0.0</mat_cloudy>
<mat_IOR>1.0</mat_IOR>
<mat_mossy>0.0</mat_mossy>
</material>
<random>
<use_generate>True</use_generate>
<use_random_seed>True</use_random_seed>
<user_seed>1</user_seed>
</random>
</preset>
<preset id="4">
<title>Ice</title>
<size>
<scale>
<axis>X</axis>
<lower>0.0</lower>
<upper>2.0</upper>
</scale>
<scale>
<axis>Y</axis>
<lower>0.0</lower>
<upper>2.0</upper>
</scale>
<scale>
<axis>Z</axis>
<lower>0.0</lower>
<upper>2.0</upper>
</scale>
<skew>
<axis>X</axis>
<value>0.0</value>
</skew>
<skew>
<axis>Y</axis>
<value>0.0</value>
</skew>
<skew>
<axis>Z</axis>
<value>0.0</value>
</skew>
<use_scale_dis>False</use_scale_dis>
<scale_fac>[1.0, 1.0, 1.0]</scale_fac>
</size>
<shape>
<deform>5.0</deform>
<rough>1.0</rough>
<detail>3</detail>
<display_detail>2</display_detail>
<smooth_fac>2.0</smooth_fac>
<smooth_it>1</smooth_it>
</shape>
<material>
<mat_enable>True</mat_enable>
<mat_color>[0.9, 0.95, 1.0]</mat_color>
<mat_bright>0.85</mat_bright>
<mat_rough>0.25</mat_rough>
<mat_spec>0.2</mat_spec>
<mat_hard>50</mat_hard>
<mat_use_trans>True</mat_use_trans>
<mat_alpha>0.9</mat_alpha>
<mat_cloudy>0.1</mat_cloudy>
<mat_IOR>1.31</mat_IOR>
<mat_mossy>0.0</mat_mossy>
</material>
<random>
<use_generate>True</use_generate>
<use_random_seed>True</use_random_seed>
<user_seed>1</user_seed>
</random>
</preset>
<preset id="5">
<title>Fake Ocean</title>
<size>
<scale>
<axis>X</axis>
<lower>10.0</lower>
<upper>10.0</upper>
</scale>
<scale>
<axis>Y</axis>
<lower>10.0</lower>
<upper>10.0</upper>
</scale>
<scale>
<axis>Z</axis>
<lower>0.0</lower>
<upper>0.0</upper>
</scale>
<skew>
<axis>X</axis>
<value>0.0</value>
</skew>
<skew>
<axis>Y</axis>
<value>0.0</value>
</skew>
<skew>
<axis>Z</axis>
<value>0.0</value>
</skew>
<use_scale_dis>False</use_scale_dis>
<scale_fac>[1.0, 1.0, 1.0]</scale_fac>
</size>
<shape>
<deform>7.5</deform>
<rough>3.0</rough>
<detail>4</detail>
<display_detail>3</display_detail>
<smooth_fac>0.0</smooth_fac>
<smooth_it>0</smooth_it>
</shape>
<material>
<mat_enable>True</mat_enable>
<mat_color>[0.1, 0.12, 0.125]</mat_color>
<mat_bright>0.85</mat_bright>
<mat_rough>1.5</mat_rough>
<mat_spec>0.25</mat_spec>
<mat_hard>30</mat_hard>
<mat_use_trans>True</mat_use_trans>
<mat_alpha>0.5</mat_alpha>
<mat_cloudy>0.5</mat_cloudy>
<mat_IOR>1.333</mat_IOR>
<mat_mossy>0.0</mat_mossy>
</material>
<random>
<use_generate>True</use_generate>
<use_random_seed>True</use_random_seed>
<user_seed>1</user_seed>
</random>
</preset>
</settings>
@@ -0,0 +1,161 @@
# SPDX-FileCopyrightText: 2019-2022 Blender Foundation
#
# SPDX-License-Identifier: GPL-2.0-or-later
# This try block allows for the script to psudo-intelligently select the
# appropriate random to use. If Numpy's random is present it will use that.
# If Numpy's random is not present, it will through a "module not found"
# exception and instead use the slower built-in random that Python has.
try:
from numpy.random import random_integers as randint
from numpy.random import normal as gauss
from numpy.random import (
beta,
uniform,
)
except:
from random import (
randint,
gauss,
uniform,
)
from random import betavariate as beta
from .utils import skewedGauss
def randomizeTexture(texture, level=1):
'''
Set the values for a texture from parameters.
param: texture - bpy.data.texture to modify.
level - designated tweaked settings to use
-> Below 10 is a displacement texture
-> Between 10 and 20 is a base material texture
'''
noises = ['BLENDER_ORIGINAL', 'ORIGINAL_PERLIN', 'IMPROVED_PERLIN',
'VORONOI_F1', 'VORONOI_F2', 'VORONOI_F3', 'VORONOI_F4',
'VORONOI_F2_F1', 'VORONOI_CRACKLE']
if texture.type == 'CLOUDS':
if randint(0, 1) == 0:
texture.noise_type = 'SOFT_NOISE'
else:
texture.noise_type = 'HARD_NOISE'
if level != 11:
tempInt = randint(0, 6)
else:
tempInt = randint(0, 8)
texture.noise_basis = noises[tempInt]
texture.noise_depth = 8
if level == 0:
texture.noise_scale = gauss(0.625, 1 / 24)
elif level == 2:
texture.noise_scale = 0.15
elif level == 11:
texture.noise_scale = gauss(0.5, 1 / 24)
if texture.noise_basis in ['BLENDER_ORIGINAL', 'ORIGINAL_PERLIN',
'IMPROVED_PERLIN', 'VORONOI_F1']:
texture.intensity = gauss(1, 1 / 6)
texture.contrast = gauss(4, 1 / 3)
elif texture.noise_basis in ['VORONOI_F2', 'VORONOI_F3', 'VORONOI_F4']:
texture.intensity = gauss(0.25, 1 / 12)
texture.contrast = gauss(2, 1 / 6)
elif texture.noise_basis == 'VORONOI_F2_F1':
texture.intensity = gauss(0.5, 1 / 6)
texture.contrast = gauss(2, 1 / 6)
elif texture.noise_basis == 'VORONOI_CRACKLE':
texture.intensity = gauss(0.5, 1 / 6)
texture.contrast = gauss(2, 1 / 6)
elif texture.type == 'MUSGRAVE':
# musgraveType = ['MULTIFRACTAL', 'RIDGED_MULTIFRACTAL',
# 'HYBRID_MULTIFRACTAL', 'FBM', 'HETERO_TERRAIN']
texture.musgrave_type = 'MULTIFRACTAL'
texture.dimension_max = abs(gauss(0, 0.6)) + 0.2
texture.lacunarity = beta(3, 8) * 8.2 + 1.8
if level == 0:
texture.noise_scale = gauss(0.625, 1 / 24)
texture.noise_intensity = 0.2
texture.octaves = 1.0
elif level == 2:
texture.intensity = gauss(1, 1 / 6)
texture.contrast = 0.2
texture.noise_scale = 0.15
texture.octaves = 8.0
elif level == 10:
texture.intensity = gauss(0.25, 1 / 12)
texture.contrast = gauss(1.5, 1 / 6)
texture.noise_scale = 0.5
texture.octaves = 8.0
elif level == 12:
texture.octaves = uniform(1, 3)
elif level > 12:
texture.octaves = uniform(2, 8)
else:
texture.intensity = gauss(1, 1 / 6)
texture.contrast = 0.2
texture.octaves = 8.0
elif texture.type == 'DISTORTED_NOISE':
tempInt = randint(0, 8)
texture.noise_distortion = noises[tempInt]
tempInt = randint(0, 8)
texture.noise_basis = noises[tempInt]
texture.distortion = skewedGauss(2.0, 2.6666, (0.0, 10.0), False)
if level == 0:
texture.noise_scale = gauss(0.625, 1 / 24)
elif level == 2:
texture.noise_scale = 0.15
elif level >= 12:
texture.noise_scale = gauss(0.2, 1 / 48)
elif texture.type == 'STUCCI':
stucciTypes = ['PLASTIC', 'WALL_IN', 'WALL_OUT']
if randint(0, 1) == 0:
texture.noise_type = 'SOFT_NOISE'
else:
texture.noise_type = 'HARD_NOISE'
tempInt = randint(0, 2)
texture.stucci_type = stucciTypes[tempInt]
if level == 0:
tempInt = randint(0, 6)
texture.noise_basis = noises[tempInt]
texture.noise_scale = gauss(0.625, 1 / 24)
elif level == 2:
tempInt = randint(0, 6)
texture.noise_basis = noises[tempInt]
texture.noise_scale = 0.15
elif level >= 12:
tempInt = randint(0, 6)
texture.noise_basis = noises[tempInt]
texture.noise_scale = gauss(0.2, 1 / 30)
else:
tempInt = randint(0, 6)
texture.noise_basis = noises[tempInt]
elif texture.type == 'VORONOI':
metrics = ['DISTANCE', 'DISTANCE_SQUARED', 'MANHATTAN', 'CHEBYCHEV',
'MINKOVSKY_HALF', 'MINKOVSKY_FOUR', 'MINKOVSKY']
# Settings for first displacement level:
if level == 0:
tempInt = randint(0, 1)
texture.distance_metric = metrics[tempInt]
texture.noise_scale = gauss(0.625, 1 / 24)
texture.contrast = 0.5
texture.intensity = 0.7
elif level == 2:
texture.noise_scale = 0.15
tempInt = randint(0, 6)
texture.distance_metric = metrics[tempInt]
elif level >= 12:
tempInt = randint(0, 1)
texture.distance_metric = metrics[tempInt]
texture.noise_scale = gauss(0.125, 1 / 48)
texture.contrast = 0.5
texture.intensity = 0.7
else:
tempInt = randint(0, 6)
texture.distance_metric = metrics[tempInt]
return
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,144 @@
# SPDX-FileCopyrightText: 2019-2022 Blender Foundation
#
# SPDX-License-Identifier: GPL-2.0-or-later
# Paul "BrikBot" Marshall
# Created: July 1, 2011
# Last Modified: November 17, 2011
# Homepage (blog): http://post.darkarsenic.com/
# //blog.darkarsenic.com/
# Thanks to Meta-Androco, RickyBlender, Ace Dragon, and PKHG for ideas
# and testing.
#
# Coded in IDLE, tested in Blender 2.59. NumPy Recommended.
# Search for "@todo" to quickly find sections that need work.
import inspect
import shutil
from . import utils
from xml.dom import minidom
basePath = inspect.getfile(inspect.currentframe())[0:-len("settings.py")]
path = basePath + "add_mesh_rocks.xml"
try:
source = minidom.parse(path)
# print("Rock generator settings file found:\n" + path)
except:
print("Rock generator settings file not found. Creating settings file.")
shutil.copy(basePath + "factory.xml", path)
source = minidom.parse(path)
xmlDefault = source.getElementsByTagName('default')[0]
xmlPresets = source.getElementsByTagName('preset')
default = []
presets = []
# ----- Gets and Sets -----#
def getDefault():
global default
return default
def getPresetLists():
global presets
return presets
def getPreset(ID=0):
global presets
return presets[ID]
# ---------- Core ----------#
def parse():
global xmlDefault
global xmlPresets
global default
global presets
# Parse default values
default = parseNode(xmlDefault)
# Parse preset values
for setting in xmlPresets:
presets.append(parseNode(setting))
return '{FINISHED}'
# Takes a node and parses it for data. Relies on that setting.xml has
# a valid format as specified by the DTD.
# For some reason minidom places an empty child node for every other node.
def parseNode(setting, title=True):
loc = 1
if title:
# Preset name (xmlPreset.childNodes[1]):
title = setting.childNodes[loc].childNodes[0].data
loc += 2
# Preset size values (xmlPreset.childNodes[3]):
scaleX = [float(setting.childNodes[loc].childNodes[1].childNodes[3].childNodes[0].data),
float(setting.childNodes[loc].childNodes[1].childNodes[5].childNodes[0].data)]
scaleY = [float(setting.childNodes[loc].childNodes[3].childNodes[3].childNodes[0].data),
float(setting.childNodes[loc].childNodes[3].childNodes[5].childNodes[0].data)]
scaleZ = [float(setting.childNodes[loc].childNodes[5].childNodes[3].childNodes[0].data),
float(setting.childNodes[loc].childNodes[5].childNodes[5].childNodes[0].data)]
skewX = float(setting.childNodes[loc].childNodes[7].childNodes[3].childNodes[0].data)
skewY = float(setting.childNodes[loc].childNodes[9].childNodes[3].childNodes[0].data)
skewZ = float(setting.childNodes[loc].childNodes[11].childNodes[3].childNodes[0].data)
if setting.childNodes[loc].childNodes[13].childNodes[0].data == 'False':
use_scale_dis = False
else:
use_scale_dis = True
scale_fac = utils.toList(setting.childNodes[loc].childNodes[15].childNodes[0].data)
loc += 2
# Presst shape values (xmlPreset.childNodes[5]):
deform = float(setting.childNodes[loc].childNodes[1].childNodes[0].data)
rough = float(setting.childNodes[loc].childNodes[3].childNodes[0].data)
detail = int(setting.childNodes[loc].childNodes[5].childNodes[0].data)
display_detail = int(setting.childNodes[loc].childNodes[7].childNodes[0].data)
smooth_fac = float(setting.childNodes[loc].childNodes[9].childNodes[0].data)
smooth_it = int(setting.childNodes[loc].childNodes[11].childNodes[0].data)
loc += 2
# Preset material values (xmlPreset.childNodes[7]):
loc += 2
# Preset random values (xmlPreset.childNodes[9]):
if setting.childNodes[loc].childNodes[1].childNodes[0].data == 'True':
use_generate = True
else:
use_generate = False
if setting.childNodes[loc].childNodes[3].childNodes[0].data == 'False':
use_random_seed = False
else:
use_random_seed = True
user_seed = int(setting.childNodes[loc].childNodes[5].childNodes[0].data)
if title:
parsed = [title, scaleX, scaleY, scaleZ, skewX, skewY, skewZ,
use_scale_dis, scale_fac, deform, rough, detail,
display_detail, smooth_fac, smooth_it,
use_generate, use_random_seed, user_seed]
else:
parsed = [scaleX, scaleY, scaleZ, skewX, skewY, skewZ, use_scale_dis,
scale_fac, deform, rough, detail, display_detail, smooth_fac,
smooth_it, use_generate, use_random_seed, user_seed]
return parsed
def save():
return '{FINISHED}'
def _print():
for i in presets:
print(i)
return '{FINISHED}'
@@ -0,0 +1,141 @@
# SPDX-FileCopyrightText: 2019-2022 Blender Foundation
#
# SPDX-License-Identifier: GPL-2.0-or-later
# Converts a formatted string to a float tuple:
# IN - '(0.5, 0.2)' -> CONVERT -> OUT - (0.5, 0.2)
def toTuple(stringIn):
sTemp = str(stringIn)[1:len(str(stringIn)) - 1].split(', ')
fTemp = []
for i in sTemp:
fTemp.append(float(i))
return tuple(fTemp)
# Converts a formatted string to a float tuple:
# IN - '[0.5, 0.2]' -> CONVERT -> OUT - [0.5, 0.2]
def toList(stringIn):
sTemp = str(stringIn)[1:len(str(stringIn)) - 1].split(', ')
fTemp = []
for i in sTemp:
fTemp.append(float(i))
return fTemp
# Converts each item of a list into a float:
def toFloats(inList):
outList = []
for i in inList:
outList.append(float(i))
return outList
# Converts each item of a list into an integer:
def toInts(inList):
outList = []
for i in inList:
outList.append(int(i))
return outList
# Sets all faces smooth. Done this way since I can't
# find a simple way without using bpy.ops:
def smooth(mesh):
import bmesh
bm = bmesh.new()
bm.from_mesh(mesh)
for f in bm.faces:
f.smooth = True
bm.to_mesh(mesh)
return mesh
# This try block allows for the script to psudo-intelligently select the
# appropriate random to use. If Numpy's random is present it will use that.
# If Numpy's random is not present, it will through a "module not found"
# exception and instead use the slower built-in random that Python has.
try:
# from numpy.random import random_integers as randint
from numpy.random import normal as gauss
# from numpy.random import (beta,
# uniform,
# seed,
# weibull)
# print("Rock Generator: Numpy found.")
numpy = True
except:
from random import (
# randint,
gauss,
# uniform,
# seed
)
# from random import betavariate as beta
# from random import weibullvariate as weibull
print("Rock Generator: Numpy not found. Using Python's random.")
numpy = False
# Artificially skews a normal (gaussian) distribution. This will not create
# a continuous distribution curve but instead acts as a piecewise finction.
# This linearly scales the output on one side to fit the bounds.
#
# Example output histograms:
#
# Upper skewed: Lower skewed:
# | ▄ | _
# | █ | █
# | █_ | █
# | ██ | _█
# | _██ | ██
# | _▄███_ | ██ _
# | ▄██████ | ▄██▄█▄_
# | _█▄███████ | ███████
# | _██████████_ | ████████▄▄█_ _
# | _▄▄████████████ | ████████████▄█_
# | _▄_ ▄███████████████▄_ | _▄███████████████▄▄_
# ------------------------- -----------------------
# |mu |mu
# Histograms were generated in R (http://www.r-project.org/) based on the
# calculations below and manually duplicated here.
#
# param: mu - mu is the mean of the distribution.
# sigma - sigma is the standard deviation of the distribution.
# bounds - bounds[0] is the lower bound and bounds[1]
# is the upper bound.
# upperSkewed - if the distribution is upper skewed.
# return: out - Rondomly generated value from the skewed distribution.
#
# @todo: Because NumPy's random value generators are faster when called
# a bunch of times at once, maybe allow this to generate and return
# multiple values at once?
def skewedGauss(mu, sigma, bounds, upperSkewed=True):
raw = gauss(mu, sigma)
# Quicker to check an extra condition than do unnecessary math. . . .
if raw < mu and not upperSkewed:
out = ((mu - bounds[0]) / (3 * sigma)) * raw + ((mu * (bounds[0] - (mu - 3 * sigma))) / (3 * sigma))
elif raw > mu and upperSkewed:
out = ((mu - bounds[1]) / (3 * -sigma)) * raw + ((mu * (bounds[1] - (mu + 3 * sigma))) / (3 * -sigma))
else:
out = raw
return out
# @todo create a def for generating an alpha and beta for a beta distribution
# given a mu, sigma, and an upper and lower bound. This proved faster in
# profiling in addition to providing a much better distribution curve
# provided multiple iterations happen within this function; otherwise it was
# slower.
# This might be a scratch because of the bounds placed on mu and sigma:
#
# For alpha > 1 and beta > 1:
# mu^2 - mu^3 mu^3 - mu^2 + mu
# ----------- < sigma < ----------------
# 1 + mu 2 - mu
#
# def generateBeta(mu, sigma, scale, repitions=1):
# results = []
#
# return results
@@ -0,0 +1,500 @@
# SPDX-FileCopyrightText: 2015-2023 Blender Foundation
#
# SPDX-License-Identifier: GPL-2.0-or-later
import bpy
from math import (
pi, sin,
cos, tan,
)
from bpy.types import Operator
from mathutils import (
Vector,
Euler,
)
from bpy.props import (
IntProperty,
FloatProperty,
BoolProperty,
StringProperty,
)
from bpy_extras import object_utils
from .interface import draw_transform_props
# mesh generating function, returns mesh
def add_mesh_Brilliant(context, s, table_w, crown_h, girdle_t, pavi_d, bezel_f,
pavi_f, culet, girdle_real, keep_lga, g_real_smooth):
# # possible user inputs ( output 100% = 2 blender units )
# s # no. of girdle facets (steps) default: 16
# table_w # table width default: 0.530
# crown_h # crown height default: 0.162
# girdle_t # girdle thickness default: 0.017
# pavi_d # pavilion depth default: 0.431
# bezel_f # bezel factor default: 0.250
# pavi_f # pavilion factor default: 0.400
# culet # culet size default: 0.000
# girdle_real # type of girdle flat/real default: True
# g_real_smooth # smooth or flat shading default: False
# keep_lga # when culet > 0, keep lga default: False
# variables / shortcuts
if s % 2: # prevent odd number of steps (messes up mesh)
s = s - 1
if not girdle_real:
g_real_smooth = False
ang = 2 * pi / s # angle step size
Verts = [] # collect all vertices
Faces = [] # collect all faces
ca = cos(ang)
ca2 = cos(ang / 2)
sa4 = sin(ang / 4)
ta4 = tan(ang / 4)
ta8 = tan(ang / 8)
def fa(*vs): # shortcut Faces.append
v = []
for u in vs:
v.append(u)
Faces.append(v)
def va(vx, vz, iang, sang, n): # shortcut Verts.append
for i in range(n):
v = Vector((vx, 0, vz))
ai = sang + iang * i
E_rot = Euler((0, 0, ai), 'XYZ')
v.rotate(E_rot)
Verts.append((v.x, v.y, v.z))
# upper girdle angle
uga = (1 - bezel_f) * crown_h * 2 / (ca2 -
(table_w + (1 - table_w) * bezel_f) * ca2 / ca)
# lower girdle angle
if keep_lga:
if pavi_f > 0 and pavi_f < 1:
lga = (1 - pavi_f) * pavi_d * 2 / (ca2 - pavi_f * ca2 / ca)
elif pavi_f == 1:
lga = 0
else:
lga = 2 * pavi_d * ca
else:
lga = (1 - pavi_f) * pavi_d * 2 / (ca2 -
(culet + (1 - culet) * pavi_f) * ca2 / ca)
# append girdle vertices
va(1, 0, ang, 0, s)
va(1, 2 * girdle_t, ang, 0, s)
# append real girdle vertices
if girdle_real:
dnu = uga * (1 - ca2)
dfu = uga * (ta8 + ta4) * sa4
dnl = lga * (1 - ca2)
dfl = lga * (ta8 + ta4) * sa4
if abs(dnu) + abs(dnl) > 2 * girdle_t or dnu < 0 or dnl < 0:
girdle_real = False
else:
va(1, dnl, ang, ang / 2, s)
va(1, 2 * girdle_t - dnu, ang, ang / 2, s)
va(1, dfl, ang / 2, ang / 4, 2 * s)
va(1, 2 * girdle_t - dfu, ang / 2, ang / 4, 2 * s)
# make girdle faces
l1 = len(Verts) # 2*s / 8*s
for i in range(l1):
if girdle_real:
if i < s:
fa(i, i + s, 2 * i + 6 * s, 2 * i + 4 * s)
if i == 0:
fa(i, s, l1 - 1, 6 * s - 1)
else:
fa(i, i + s, 2 * i + 6 * s - 1, 2 * i + 4 * s - 1)
elif i > 2 * s - 1 and i < 3 * s:
fa(i, i + s, 2 * (i + s), 2 * i)
fa(i, i + s, 2 * (i + s) + 1, 2 * i + 1)
else:
if i < s - 1:
fa(i, i + s, i + s + 1, i + 1)
elif i == s - 1:
fa(i, i + s, s, 0)
# append upper girdle facet vertices
va((table_w + (1 - table_w) * bezel_f) / ca, (1 - bezel_f) * 2 * crown_h +
2 * girdle_t, 2 * ang, ang, int(s / 2))
# make upper girdle facet faces
l2 = len(Verts) # 2.5*s / 8.5*s
for i in range(l2):
if i > s and i < 2 * s - 1 and i % 2 != 0:
if girdle_real:
fa(i, 2 * (i + 2 * s), i + 2 * s, 2 * (i + 2 * s) + 1, i + 1,
int(7.5 * s) + int((i - 1) / 2))
fa(i, 2 * (i + 2 * s) - 1, i + 2 * s - 1, 2 * (i + 2 * s - 1),
i - 1, int(7.5 * s) + int((i - 1) / 2))
else:
fa(i, i + 1, int((i + 3 * s) / 2))
fa(i, i - 1, int((i + 3 * s) / 2))
elif i == s:
if girdle_real:
fa(i, l1 - 1, 4 * s - 1, l1 - 2, 2 * i - 1, l2 - 1)
fa(2 * i - 2, l1 - 4, 4 * s - 2, l1 - 3, 2 * i - 1, l2 - 1)
else:
fa(i, 2 * i - 1, l2 - 1)
fa(2 * i - 1, 2 * i - 2, l2 - 1)
# append table vertices
va(table_w, (crown_h + girdle_t) * 2, 2 * ang, 0, int(s / 2))
# make bezel facet faces and star facet faces
l3 = len(Verts) # 3*s / 9*s
for i in range(l3):
if i > l2 - 1 and i < l3 - 1:
fa(i, i + 1, i - int(s / 2))
fa(i + 1, i - int(s / 2), 2 * (i - l2) + 2 + s, i - int(s / 2) + 1)
elif i == l3 - 1:
fa(i, l2, l2 - 1)
fa(s, l2 - 1, l2, l2 - int(s / 2))
# make table facet face
tf = []
for i in range(l3):
if i > l2 - 1:
tf.append(i)
fa(*tf)
# append lower girdle facet vertices
if keep_lga:
va(pavi_f / ca, (pavi_f - 1) * pavi_d * 2, 2 * ang, ang, int(s / 2))
else:
va((pavi_f * (1 - culet) + culet) / ca, (pavi_f - 1) * pavi_d * 2, 2 * ang,
ang, int(s / 2))
# make lower girdle facet faces
l4 = len(Verts) # 3.5*s / 9.5*s
for i in range(l4):
if i > 0 and i < s - 1 and i % 2 == 0:
if girdle_real:
fa(i, 2 * (i + 2 * s), i + 2 * s, 2 * (i + 2 * s) + 1, i + 1,
int(i / 2) + 9 * s)
fa(i, 2 * (i + 2 * s) - 1, i + 2 * s - 1, 2 * (i + 2 * s - 1),
i - 1, int(i / 2) + 9 * s - 1)
else:
fa(i, i + 1, int(i / 2) + l4 - int(s / 2))
fa(i, i - 1, int(i / 2) + l4 - int(s / 2) - 1)
elif i == 0:
if girdle_real:
fa(0, 4 * s, 2 * s, 4 * s + 1, 1, 9 * s)
fa(0, 6 * s - 1, 3 * s - 1, 6 * s - 2, s - 1, l4 - 1)
else:
fa(0, 1, l4 - int(s / 2))
fa(0, s - 1, l4 - 1)
# append culet vertice(s)
if culet == 0:
va(0, pavi_d * (-2), 0, 0, 1)
else:
if keep_lga:
va(culet * pavi_f / ca, pavi_d * (-2) + culet * pavi_f * 2 * pavi_d,
2 * ang, ang, int(s / 2))
else:
va(culet / ca, pavi_d * (-2), 2 * ang, ang, int(s / 2))
# make pavilion facet face
l5 = len(Verts) # 4*s / 10*s //if !culet: 3.5*s+1 / 9.5*s+1
for i in range(l5):
if i > 0 and i < s - 1 and i % 2 == 0:
if culet:
fa(i, l3 + int(i / 2), l3 + int((s + i) / 2),
l3 + int((s + i) / 2) - 1, l3 + int(i / 2) - 1)
else:
fa(i, l3 + int(i / 2), l5 - 1, l3 + int(i / 2) - 1)
elif i == 0:
if culet:
fa(i, l3, l4, l5 - 1, l4 - 1)
else:
fa(i, l3, l5 - 1, l4 - 1)
# make culet facet face
if culet:
cf = []
for i in range(l5):
if i > l4 - 1:
cf.append(i)
fa(*cf)
# create actual mesh and object based on Verts and Faces given
dmesh = bpy.data.meshes.new("dmesh")
dmesh.from_pydata(Verts, [], Faces)
dmesh.update()
return dmesh
# object generating function, returns final object
def addBrilliant(context, self, s, table_w, crown_h, girdle_t, pavi_d, bezel_f,
pavi_f, culet, girdle_real, keep_lga, g_real_smooth):
# deactivate possible active Objects
bpy.context.view_layer.objects.active = None
# create actual mesh and object based on Verts and Faces given
dmesh = add_mesh_Brilliant(context, s, table_w, crown_h, girdle_t, pavi_d, bezel_f,
pavi_f, culet, girdle_real, keep_lga, g_real_smooth)
# Create object and link it into scene.
dobj = object_utils.object_data_add(context, dmesh, operator=self, name="dobj")
# activate and select object
bpy.context.view_layer.objects.active = dobj
dobj.select_set(True)
obj = bpy.context.active_object
# flip all face normals outside
bpy.ops.object.mode_set(mode='EDIT', toggle=False)
sel_mode = bpy.context.tool_settings.mesh_select_mode
bpy.context.tool_settings.mesh_select_mode = [False, False, True]
bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
for i, face in enumerate(obj.data.polygons):
face.select = True
bpy.ops.object.mode_set(mode='EDIT', toggle=False)
bpy.ops.mesh.normals_make_consistent(inside=False)
bpy.context.tool_settings.mesh_select_mode = sel_mode
bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
# make girdle smooth for complex girdle
if girdle_real and g_real_smooth:
bpy.ops.object.mode_set(mode='EDIT', toggle=False)
bpy.ops.mesh.select_all(action='DESELECT') # deselect all mesh data
bpy.ops.object.mode_set(mode='OBJECT')
pls = []
dp = obj.data.polygons[:4 * s] # only consider faces of girdle
ov = obj.data.vertices
for i, p in enumerate(dp):
pls.extend(p.vertices) # list all verts of girdle
for i, e in enumerate(obj.data.edges): # select edges to mark sharp
if e.vertices[0] in pls and e.vertices[1] in pls and abs(
ov[e.vertices[0]].co.x - ov[e.vertices[1]].co.x):
obj.data.edges[i].select = True
continue
obj.data.edges[i].select = False
bpy.ops.object.mode_set(mode='EDIT', toggle=False)
bpy.ops.mesh.mark_sharp()
bpy.context.tool_settings.mesh_select_mode = [False, False, True]
bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
bpy.ops.object.select_all(action='DESELECT')
for i, face in enumerate(obj.data.polygons):
if i < 4 * s:
face.select = True
continue
face.select = False
bpy.ops.object.mode_set(mode='EDIT', toggle=False)
bpy.ops.mesh.faces_shade_smooth()
edge_split_modifier = context.object.modifiers.new("", 'EDGE_SPLIT')
bpy.context.tool_settings.mesh_select_mode = sel_mode
bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
bpy.ops.object.modifier_apply(modifier=edge_split_modifier.name)
return dobj
# add new operator for object
class MESH_OT_primitive_brilliant_add(Operator, object_utils.AddObjectHelper):
bl_idname = "mesh.primitive_brilliant_add"
bl_label = "Brilliant"
bl_description = "Construct a custom brilliant mesh"
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
Brilliant : BoolProperty(name = "Brilliant",
default = True,
description = "Brilliant")
change : BoolProperty(name = "Change",
default = False,
description = "change Brilliant")
s: IntProperty(
name="Segments",
description="Longitudial segmentation",
step=1,
min=6,
max=128,
default=16
)
table_w: FloatProperty(
name="Table Width",
description="Width of table",
min=0.001,
max=1.0,
default=0.53,
subtype='FACTOR'
)
crown_h: FloatProperty(
name="Crown Height",
description="Height of crown",
min=0.0,
max=1.0,
default=0.162,
subtype='FACTOR'
)
girdle_t: FloatProperty(
name="Girdle Height",
description="Height of girdle",
min=0.0,
max=0.5,
default=0.017,
subtype='FACTOR'
)
girdle_real: BoolProperty(
name="Real Girdle",
description="More beautiful girdle; has more polygons",
default=True
)
g_real_smooth: BoolProperty(
name="Smooth Girdle",
description="smooth shading for girdle, only available for real girdle",
default=False
)
pavi_d: FloatProperty(
name="Pavilion Depth",
description="Height of pavilion",
min=0.0,
max=1.0,
default=0.431,
subtype='FACTOR'
)
bezel_f: FloatProperty(
name="Upper Facet Factor",
description="Determines the form of bezel and upper girdle facets",
min=0.0,
max=1.0,
default=0.250,
subtype='FACTOR'
)
pavi_f: FloatProperty(
name="Lower Facet Factor",
description="Determines the form of pavilion and lower girdle facets",
min=0.001,
max=1.0,
default=0.400,
subtype='FACTOR'
)
culet: FloatProperty(
name="Culet Size",
description="0: no culet (default)",
min=0.0,
max=0.999,
default=0.0,
subtype='FACTOR'
)
keep_lga: BoolProperty(
name="Retain Lower Angle",
description="If culet > 0, retains angle of pavilion facets",
default=False
)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
layout.separator()
layout.prop(self, "s")
layout.prop(self, "table_w")
col = layout.column(align=True)
col.prop(self, "crown_h", text='Height Crown')
col.prop(self, "girdle_t", text='Girdle')
col.prop(self, "pavi_d", text='Pavilion')
layout.prop(self, "girdle_real")
layout.prop(self, "g_real_smooth")
col = layout.column(align=True)
col.prop(self, "bezel_f", text='Facet Upper')
col.prop(self, "pavi_f", text='Lower')
layout.prop(self, "culet")
layout.prop(self, "keep_lga")
if self.change == False:
layout.separator()
draw_transform_props(self, layout)
# call mesh/object generator function with user inputs
def execute(self, context):
# turn off 'Enter Edit Mode'
use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
bpy.context.preferences.edit.use_enter_edit_mode = False
if bpy.context.mode == "OBJECT":
if context.selected_objects != [] and context.active_object and \
(context.active_object.data is not None) and ('Brilliant' in context.active_object.data.keys()) and \
(self.change == True):
obj = context.active_object
oldmesh = obj.data
oldmeshname = obj.data.name
mesh = add_mesh_Brilliant(context, self.s, self.table_w, self.crown_h,
self.girdle_t, self.pavi_d, self.bezel_f,
self.pavi_f, self.culet, self.girdle_real,
self.keep_lga, self.g_real_smooth
)
obj.data = mesh
for material in oldmesh.materials:
obj.data.materials.append(material)
bpy.data.meshes.remove(oldmesh)
obj.data.name = oldmeshname
else:
obj = addBrilliant(context, self, self.s, self.table_w, self.crown_h,
self.girdle_t, self.pavi_d, self.bezel_f,
self.pavi_f, self.culet, self.girdle_real,
self.keep_lga, self.g_real_smooth
)
obj.data["Brilliant"] = True
obj.data["change"] = False
for prm in BrilliantParameters():
obj.data[prm] = getattr(self, prm)
if bpy.context.mode == "EDIT_MESH":
active_object = context.active_object
name_active_object = active_object.name
bpy.ops.object.mode_set(mode='OBJECT')
obj = addBrilliant(context, self, self.s, self.table_w, self.crown_h,
self.girdle_t, self.pavi_d, self.bezel_f,
self.pavi_f, self.culet, self.girdle_real,
self.keep_lga, self.g_real_smooth
)
obj.select_set(True)
active_object.select_set(True)
bpy.context.view_layer.objects.active = active_object
bpy.ops.object.join()
context.active_object.name = name_active_object
bpy.ops.object.mode_set(mode='EDIT')
if use_enter_edit_mode:
bpy.ops.object.mode_set(mode = 'EDIT')
# restore pre operator state
bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
return {'FINISHED'}
def BrilliantParameters():
BrilliantParameters = [
"s",
"table_w",
"crown_h",
"girdle_t",
"girdle_real",
"g_real_smooth",
"pavi_d",
"bezel_f",
"pavi_f",
"culet",
"keep_lga",
]
return BrilliantParameters
@@ -0,0 +1,521 @@
# SPDX-FileCopyrightText: 2015-2022 Blender Foundation
#
# SPDX-License-Identifier: GPL-2.0-or-later
# Author: Alain Ducharme (phymec)
import bpy
from bpy_extras import object_utils
from itertools import permutations
from math import (
copysign, pi,
sqrt,
)
from bpy.types import Operator
from bpy.props import (
BoolProperty,
EnumProperty,
FloatProperty,
FloatVectorProperty,
IntProperty,
StringProperty,
)
from .interface import draw_transform_props
def round_cube(radius=1.0, arcdiv=4, lindiv=0., size=(0., 0., 0.),
div_type='CORNERS', odd_axis_align=False, info_only=False):
# subdiv bitmasks
CORNERS, EDGES, ALL = 0, 1, 2
try:
subdiv = ('CORNERS', 'EDGES', 'ALL').index(div_type)
except ValueError:
subdiv = CORNERS # fallback
radius = max(radius, 0.)
if not radius:
# No sphere
arcdiv = 1
odd_axis_align = False
if arcdiv <= 0:
arcdiv = max(round(pi * radius * lindiv * 0.5), 1)
arcdiv = max(round(arcdiv), 1)
if lindiv <= 0. and radius:
lindiv = 1. / (pi / (arcdiv * 2.) * radius)
lindiv = max(lindiv, 0.)
if not lindiv:
subdiv = CORNERS
odd = arcdiv % 2 # even = arcdiv % 2 ^ 1
step_size = 2. / arcdiv
odd_aligned = 0
vi = -1.
steps = arcdiv + 1
if odd_axis_align and odd:
odd_aligned = 1
vi += 0.5 * step_size
steps = arcdiv
axis_aligned = not odd or odd_aligned
if arcdiv == 1 and not odd_aligned and subdiv == EDGES:
subdiv = CORNERS
half_chord = 0. # ~ spherical cap base radius
sagitta = 0. # ~ spherical cap height
if not axis_aligned:
half_chord = sqrt(3.) * radius / (3. * arcdiv)
id2 = 1. / (arcdiv * arcdiv)
sagitta = radius - radius * sqrt(id2 * id2 / 3. - id2 + 1.)
# Extrusion per axis
exyz = [0. if s < 2. * (radius - sagitta) else (s - 2. * (radius - sagitta)) * 0.5 for s in size]
ex, ey, ez = exyz
dxyz = [0, 0, 0] # extrusion divisions per axis
dssxyz = [0., 0., 0.] # extrusion division step sizes per axis
for i in range(3):
sc = 2. * (exyz[i] + half_chord)
dxyz[i] = round(sc * lindiv) if subdiv else 0
if dxyz[i]:
dssxyz[i] = sc / dxyz[i]
dxyz[i] -= 1
else:
dssxyz[i] = sc
if info_only:
ec = sum(1 for n in exyz if n)
if subdiv:
fxyz = [d + (e and axis_aligned) for d, e in zip(dxyz, exyz)]
dvc = arcdiv * 4 * sum(fxyz)
if subdiv == ALL:
dvc += sum(p1 * p2 for p1, p2 in permutations(fxyz, 2))
elif subdiv == EDGES and axis_aligned:
# (0, 0, 2, 4) * sum(dxyz) + (0, 0, 2, 6)
dvc += ec * ec // 2 * sum(dxyz) + ec * (ec - 1)
else:
dvc = (arcdiv * 4) * ec + ec * (ec - 1) if axis_aligned else 0
vert_count = int(6 * arcdiv * arcdiv + (0 if odd_aligned else 2) + dvc)
if not radius and not max(size) > 0:
vert_count = 1
return arcdiv, lindiv, vert_count
if not radius and not max(size) > 0:
# Single vertex
return [(0, 0, 0)], []
# uv lookup table
uvlt = []
v = vi
for j in range(1, steps + 1):
v2 = v * v
uvlt.append((v, v2, radius * sqrt(18. - 6. * v2) / 6.))
v = vi + j * step_size # v += step_size # instead of accumulating errors
# clear fp errors / signs at axis
if abs(v) < 1e-10:
v = 0.0
# Sides built left to right bottom up
# xp yp zp xd yd zd
sides = ((0, 2, 1, (-1, 1, 1)), # Y+ Front
(1, 2, 0, (-1, -1, 1)), # X- Left
(0, 2, 1, (1, -1, 1)), # Y- Back
(1, 2, 0, (1, 1, 1)), # X+ Right
(0, 1, 2, (-1, 1, -1)), # Z- Bottom
(0, 1, 2, (-1, -1, 1))) # Z+ Top
# side vertex index table (for sphere)
svit = [[[] for i in range(steps)] for i in range(6)]
# Extend svit rows for extrusion
yer = zer = 0
if ey:
yer = axis_aligned + (dxyz[1] if subdiv else 0)
svit[4].extend([[] for i in range(yer)])
svit[5].extend([[] for i in range(yer)])
if ez:
zer = axis_aligned + (dxyz[2] if subdiv else 0)
for side in range(4):
svit[side].extend([[] for i in range(zer)])
# Extend svit rows for odd_aligned
if odd_aligned:
for side in range(4):
svit[side].append([])
hemi = steps // 2
# Create vertices and svit without dups
vert = [0., 0., 0.]
verts = []
if arcdiv == 1 and not odd_aligned and subdiv == ALL:
# Special case: Grid Cuboid
for side, (xp, yp, zp, dir) in enumerate(sides):
svitc = svit[side]
rows = len(svitc)
if rows < dxyz[yp] + 2:
svitc.extend([[] for i in range(dxyz[yp] + 2 - rows)])
vert[zp] = (half_chord + exyz[zp]) * dir[zp]
for j in range(dxyz[yp] + 2):
vert[yp] = (j * dssxyz[yp] - half_chord - exyz[yp]) * dir[yp]
for i in range(dxyz[xp] + 2):
vert[xp] = (i * dssxyz[xp] - half_chord - exyz[xp]) * dir[xp]
if (side == 5) or ((i < dxyz[xp] + 1 and j < dxyz[yp] + 1) and (side < 4 or (i and j))):
svitc[j].append(len(verts))
verts.append(tuple(vert))
else:
for side, (xp, yp, zp, dir) in enumerate(sides):
svitc = svit[side]
exr = exyz[xp]
eyr = exyz[yp]
ri = 0 # row index
rij = zer if side < 4 else yer
if side == 5:
span = range(steps)
elif side < 4 or odd_aligned:
span = range(arcdiv)
else:
span = range(1, arcdiv)
ri = 1
for j in span: # rows
v, v2, mv2 = uvlt[j]
tv2mh = 1. / 3. * v2 - 0.5
hv2 = 0.5 * v2
if j == hemi and rij:
# Jump over non-edge row indices
ri += rij
for i in span: # columns
u, u2, mu2 = uvlt[i]
vert[xp] = u * mv2
vert[yp] = v * mu2
vert[zp] = radius * sqrt(u2 * tv2mh - hv2 + 1.)
vert[0] = (vert[0] + copysign(ex, vert[0])) * dir[0]
vert[1] = (vert[1] + copysign(ey, vert[1])) * dir[1]
vert[2] = (vert[2] + copysign(ez, vert[2])) * dir[2]
rv = tuple(vert)
if exr and i == hemi:
rx = vert[xp] # save rotated x
vert[xp] = rxi = (-exr - half_chord) * dir[xp]
if axis_aligned:
svitc[ri].append(len(verts))
verts.append(tuple(vert))
if subdiv:
offsetx = dssxyz[xp] * dir[xp]
for k in range(dxyz[xp]):
vert[xp] += offsetx
svitc[ri].append(len(verts))
verts.append(tuple(vert))
if eyr and j == hemi and axis_aligned:
vert[xp] = rxi
vert[yp] = -eyr * dir[yp]
svitc[hemi].append(len(verts))
verts.append(tuple(vert))
if subdiv:
offsety = dssxyz[yp] * dir[yp]
ry = vert[yp]
for k in range(dxyz[yp]):
vert[yp] += offsety
svitc[hemi + axis_aligned + k].append(len(verts))
verts.append(tuple(vert))
vert[yp] = ry
for k in range(dxyz[xp]):
vert[xp] += offsetx
svitc[hemi].append(len(verts))
verts.append(tuple(vert))
if subdiv & ALL:
for l in range(dxyz[yp]):
vert[yp] += offsety
svitc[hemi + axis_aligned + l].append(len(verts))
verts.append(tuple(vert))
vert[yp] = ry
vert[xp] = rx # restore
if eyr and j == hemi:
vert[yp] = (-eyr - half_chord) * dir[yp]
if axis_aligned:
svitc[hemi].append(len(verts))
verts.append(tuple(vert))
if subdiv:
offsety = dssxyz[yp] * dir[yp]
for k in range(dxyz[yp]):
vert[yp] += offsety
if exr and i == hemi and not axis_aligned and subdiv & ALL:
vert[xp] = rxi
for l in range(dxyz[xp]):
vert[xp] += offsetx
svitc[hemi + k].append(len(verts))
verts.append(tuple(vert))
vert[xp] = rx
svitc[hemi + axis_aligned + k].append(len(verts))
verts.append(tuple(vert))
svitc[ri].append(len(verts))
verts.append(rv)
ri += 1
# Complete svit edges (shared vertices)
# Sides' right edge
for side, rows in enumerate(svit[:4]):
for j, row in enumerate(rows[:-1]):
svit[3 if not side else side - 1][j].append(row[0])
# Sides' top edge
svit[0][-1].extend(svit[5][0])
svit[2][-1].extend(svit[5][-1][::-1])
for row in svit[5]:
svit[3][-1].insert(0, row[0])
svit[1][-1].append(row[-1])
if odd_aligned:
for side in svit[:4]:
side[-1].append(-1)
# Bottom edges
if odd_aligned:
svit[4].insert(0, [-1] + svit[2][0][-2::-1] + [-1])
for i, col in enumerate(svit[3][0][:-1]):
svit[4][i + 1].insert(0, col)
svit[4][i + 1].append(svit[1][0][-i - 2])
svit[4].append([-1] + svit[0][0][:-1] + [-1])
else:
svit[4][0].extend(svit[2][0][::-1])
for i, col in enumerate(svit[3][0][1:-1]):
svit[4][i + 1].insert(0, col)
svit[4][i + 1].append(svit[1][0][-i - 2])
svit[4][-1].extend(svit[0][0])
# Build faces
faces = []
if not axis_aligned:
hemi -= 1
for side, rows in enumerate(svit):
xp, yp = sides[side][:2]
oa4 = odd_aligned and side == 4
if oa4: # special case
hemi += 1
for j, row in enumerate(rows[:-1]):
tri = odd_aligned and (oa4 and not j or rows[j + 1][-1] < 0)
for i, vi in enumerate(row[:-1]):
# odd_aligned triangle corners
if vi < 0:
if not j and not i:
faces.append((row[i + 1], rows[j + 1][i + 1], rows[j + 1][i]))
elif oa4 and not i and j == len(rows) - 2:
faces.append((vi, row[i + 1], rows[j + 1][i + 1]))
elif tri and i == len(row) - 2:
if j:
faces.append((vi, row[i + 1], rows[j + 1][i]))
else:
if oa4 or arcdiv > 1:
faces.append((vi, rows[j + 1][i + 1], rows[j + 1][i]))
else:
faces.append((vi, row[i + 1], rows[j + 1][i]))
# subdiv = EDGES (not ALL)
elif subdiv and len(rows[j + 1]) < len(row) and (i >= hemi):
if (i == hemi):
faces.append((vi, row[i + 1 + dxyz[xp]], rows[j + 1 + dxyz[yp]][i + 1 + dxyz[xp]],
rows[j + 1 + dxyz[yp]][i]))
elif i > hemi + dxyz[xp]:
faces.append((vi, row[i + 1], rows[j + 1][i + 1 - dxyz[xp]], rows[j + 1][i - dxyz[xp]]))
elif subdiv and len(rows[j + 1]) > len(row) and (i >= hemi):
if (i > hemi):
faces.append((vi, row[i + 1], rows[j + 1][i + 1 + dxyz[xp]], rows[j + 1][i + dxyz[xp]]))
elif subdiv and len(row) < len(rows[0]) and i == hemi:
pass
else:
# Most faces...
faces.append((vi, row[i + 1], rows[j + 1][i + 1], rows[j + 1][i]))
if oa4:
hemi -= 1
return verts, faces
class AddRoundCube(Operator, object_utils.AddObjectHelper):
bl_idname = "mesh.primitive_round_cube_add"
bl_label = "Add Round Cube"
bl_description = ("Create mesh primitives: Quadspheres, "
"Capsules, Rounded Cuboids, 3D Grids etc")
bl_options = {"REGISTER", "UNDO", "PRESET"}
sanity_check_verts = 200000
vert_count = 0
Roundcube : BoolProperty(name = "Roundcube",
default = True,
description = "Roundcube")
change : BoolProperty(name = "Change",
default = False,
description = "change Roundcube")
radius: FloatProperty(
name="Radius",
description="Radius of vertices for sphere, capsule or cuboid bevel",
default=1, min=0.0, soft_min=0.01, step=10
)
size: FloatVectorProperty(
name="Size",
description="Size",
subtype='XYZ',
default=(0.0, 0.0, 0.0),
)
arc_div: IntProperty(
name="Arc Divisions",
description="Arc curve divisions, per quadrant, 0=derive from Linear",
default=8, min=1
)
lin_div: FloatProperty(
name="Linear Divisions",
description="Linear unit divisions (Edges/Faces), 0=derive from Arc",
default=0.0, min=0.0, step=100, precision=1
)
no_limit: BoolProperty(
name='No Vertex Limit',
description='Do not limit to ' + str(sanity_check_verts) + ' vertices (sanity check)',
options={'HIDDEN'},
default=False
)
div_type: EnumProperty(
name='Type',
description='Division type',
items=(
('CORNERS', 'Corners', 'Sphere / Corners'),
('EDGES', 'Edges', 'Sphere / Corners and extruded edges (size)'),
('ALL', 'All', 'Sphere / Corners, extruded edges and faces (size)')),
default='CORNERS',
)
odd_axis_align: BoolProperty(
name='Odd Axis Align',
description='Align odd arc divisions with axes (Note: triangle corners!)',
)
def execute(self, context):
# turn off 'Enter Edit Mode'
use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
bpy.context.preferences.edit.use_enter_edit_mode = False
if self.arc_div <= 0 and self.lin_div <= 0:
self.report({'ERROR'},
"Either Arc Divisions or Linear Divisions must be greater than zero")
return {'CANCELLED'}
if not self.no_limit and self.vert_count > self.sanity_check_verts:
self.report({'ERROR'}, 'More than ' + str(self.sanity_check_verts) +
' vertices! Check "No Limit" to proceed')
return {'CANCELLED'}
if bpy.context.mode == "OBJECT":
if context.selected_objects != [] and context.active_object and \
(context.active_object.data is not None) and ('Roundcube' in context.active_object.data.keys()) and \
(self.change == True):
obj = context.active_object
oldmesh = obj.data
oldmeshname = obj.data.name
verts, faces = round_cube(self.radius, self.arc_div, self.lin_div,
self.size, self.div_type, self.odd_axis_align)
mesh = bpy.data.meshes.new('Roundcube')
mesh.from_pydata(verts, [], faces)
obj.data = mesh
for material in oldmesh.materials:
obj.data.materials.append(material)
bpy.data.meshes.remove(oldmesh)
obj.data.name = oldmeshname
else:
verts, faces = round_cube(self.radius, self.arc_div, self.lin_div,
self.size, self.div_type, self.odd_axis_align)
mesh = bpy.data.meshes.new('Roundcube')
mesh.from_pydata(verts, [], faces)
obj = object_utils.object_data_add(context, mesh, operator=self)
obj.data["Roundcube"] = True
obj.data["change"] = False
for prm in RoundCubeParameters():
obj.data[prm] = getattr(self, prm)
if bpy.context.mode == "EDIT_MESH":
active_object = context.active_object
name_active_object = active_object.name
bpy.ops.object.mode_set(mode='OBJECT')
verts, faces = round_cube(self.radius, self.arc_div, self.lin_div,
self.size, self.div_type, self.odd_axis_align)
mesh = bpy.data.meshes.new('Roundcube')
mesh.from_pydata(verts, [], faces)
obj = object_utils.object_data_add(context, mesh, operator=self)
obj.select_set(True)
active_object.select_set(True)
bpy.context.view_layer.objects.active = active_object
bpy.ops.object.join()
context.active_object.name = name_active_object
bpy.ops.object.mode_set(mode='EDIT')
if use_enter_edit_mode:
bpy.ops.object.mode_set(mode = 'EDIT')
# restore pre operator state
bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
return {'FINISHED'}
def check(self, context):
self.arcdiv, self.lindiv, self.vert_count = round_cube(
self.radius, self.arc_div, self.lin_div,
self.size, self.div_type, self.odd_axis_align,
True
)
return True
def invoke(self, context, event):
self.check(context)
return self.execute(context)
def draw(self, context):
self.check(context)
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
layout.separator()
layout.prop(self, 'radius')
layout.column().prop(self, 'size', expand=True)
layout.separator()
layout.prop(self, 'div_type', text='Division Method')
layout.prop(self, 'arc_div', text='Arc')
row = layout.row()
row.enabled = (
self.div_type != 'CORNERS' and
(
self.size[0] > self.radius*2 or
self.size[1] > self.radius*2 or
self.size[2] > self.radius*2
)
)
row.prop(self, 'lin_div', text='Linear')
row = layout.row()
row.alert = self.vert_count > self.sanity_check_verts
row.prop(self, 'no_limit')
row = layout.row()
row.active = self.arcdiv % 2
row.prop(self, 'odd_axis_align', text='Triangle Corners')
if self.change == False:
layout.separator()
draw_transform_props(self, layout)
def RoundCubeParameters():
RoundCubeParameters = [
"radius",
"size",
"arc_div",
"lin_div",
"div_type",
"odd_axis_align",
"no_limit",
]
return RoundCubeParameters
@@ -0,0 +1,405 @@
# SPDX-FileCopyrightText: 2010-2022 Blender Foundation
#
# SPDX-License-Identifier: GPL-2.0-or-later
# Author: DreamPainter
import bpy
from math import sqrt
from mathutils import Vector
from functools import reduce
from bpy.props import (
FloatProperty,
EnumProperty,
BoolProperty,
)
from bpy_extras.object_utils import object_data_add
# function to make the reduce function work as a workaround to sum a list of vectors
def vSum(list):
return reduce(lambda a, b: a + b, list)
# Get a copy of the input faces, but with the normals flipped by reversing the order of the vertex indices of each face.
def flippedFaceNormals(faces):
return [list(reversed(vertexIndices)) for vertexIndices in faces]
# creates the 5 platonic solids as a base for the rest
# plato: should be one of {"4","6","8","12","20"}. decides what solid the
# outcome will be.
# returns a list of vertices and faces
def source(plato):
verts = []
faces = []
# Tetrahedron
if plato == "4":
# Calculate the necessary constants
s = sqrt(2) / 3.0
t = -1 / 3
u = sqrt(6) / 3
# create the vertices and faces
v = [(0, 0, 1), (2 * s, 0, t), (-s, u, t), (-s, -u, t)]
faces = [[0, 1, 2], [0, 2, 3], [0, 3, 1], [1, 3, 2]]
# Hexahedron (cube)
elif plato == "6":
# Calculate the necessary constants
s = 1 / sqrt(3)
# create the vertices and faces
v = [(-s, -s, -s), (s, -s, -s), (s, s, -s), (-s, s, -s), (-s, -s, s), (s, -s, s), (s, s, s), (-s, s, s)]
faces = [[0, 3, 2, 1], [0, 1, 5, 4], [0, 4, 7, 3], [6, 5, 1, 2], [6, 2, 3, 7], [6, 7, 4, 5]]
# Octahedron
elif plato == "8":
# create the vertices and faces
v = [(1, 0, 0), (-1, 0, 0), (0, 1, 0), (0, -1, 0), (0, 0, 1), (0, 0, -1)]
faces = [[4, 0, 2], [4, 2, 1], [4, 1, 3], [4, 3, 0], [5, 2, 0], [5, 1, 2], [5, 3, 1], [5, 0, 3]]
# Dodecahedron
elif plato == "12":
# Calculate the necessary constants
s = 1 / sqrt(3)
t = sqrt((3 - sqrt(5)) / 6)
u = sqrt((3 + sqrt(5)) / 6)
# create the vertices and faces
v = [(s, s, s), (s, s, -s), (s, -s, s), (s, -s, -s), (-s, s, s), (-s, s, -s), (-s, -s, s), (-s, -s, -s),
(t, u, 0), (-t, u, 0), (t, -u, 0), (-t, -u, 0), (u, 0, t), (u, 0, -t), (-u, 0, t), (-u, 0, -t), (0, t, u),
(0, -t, u), (0, t, -u), (0, -t, -u)]
faces = [[0, 8, 9, 4, 16], [0, 12, 13, 1, 8], [0, 16, 17, 2, 12], [8, 1, 18, 5, 9], [12, 2, 10, 3, 13],
[16, 4, 14, 6, 17], [9, 5, 15, 14, 4], [6, 11, 10, 2, 17], [3, 19, 18, 1, 13], [7, 15, 5, 18, 19],
[7, 11, 6, 14, 15], [7, 19, 3, 10, 11]]
# Icosahedron
elif plato == "20":
# Calculate the necessary constants
s = (1 + sqrt(5)) / 2
t = sqrt(1 + s * s)
s = s / t
t = 1 / t
# create the vertices and faces
v = [(s, t, 0), (-s, t, 0), (s, -t, 0), (-s, -t, 0), (t, 0, s), (t, 0, -s), (-t, 0, s), (-t, 0, -s),
(0, s, t), (0, -s, t), (0, s, -t), (0, -s, -t)]
faces = [[0, 8, 4], [0, 5, 10], [2, 4, 9], [2, 11, 5], [1, 6, 8], [1, 10, 7], [3, 9, 6], [3, 7, 11],
[0, 10, 8], [1, 8, 10], [2, 9, 11], [3, 11, 9], [4, 2, 0], [5, 0, 2], [6, 1, 3], [7, 3, 1],
[8, 6, 4], [9, 4, 6], [10, 5, 7], [11, 7, 5]]
# convert the tuples to Vectors
verts = [Vector(i) for i in v]
return verts, faces
# processes the raw data from source
def createSolid(plato, vtrunc, etrunc, dual, snub):
# the duals from each platonic solid
dualSource = {"4": "4",
"6": "8",
"8": "6",
"12": "20",
"20": "12"}
# constants saving space and readability
vtrunc *= 0.5
etrunc *= 0.5
supposedSize = 0
noSnub = (snub == "None") or (etrunc == 0.5) or (etrunc == 0)
lSnub = (snub == "Left") and (0 < etrunc < 0.5)
rSnub = (snub == "Right") and (0 < etrunc < 0.5)
# no truncation
if vtrunc == 0:
if dual: # dual is as simple as another, but mirrored platonic solid
vInput, fInput = source(dualSource[plato])
supposedSize = vSum(vInput[i] for i in fInput[0]).length / len(fInput[0])
vInput = [-i * supposedSize for i in vInput] # mirror it
# Inverting vInput turns the mesh inside-out, so normals need to be flipped.
return vInput, flippedFaceNormals(fInput)
return source(plato)
elif 0 < vtrunc <= 0.5: # simple truncation of the source
vInput, fInput = source(plato)
else:
# truncation is now equal to simple truncation of the dual of the source
vInput, fInput = source(dualSource[plato])
supposedSize = vSum(vInput[i] for i in fInput[0]).length / len(fInput[0])
vtrunc = 1 - vtrunc # account for the source being a dual
if vtrunc == 0: # no truncation needed
if dual:
vInput, fInput = source(plato)
vInput = [-i * supposedSize for i in vInput]
# Inverting vInput turns the mesh inside-out, so normals need to be flipped.
return vInput, flippedFaceNormals(fInput)
# generate connection database
vDict = [{} for i in vInput]
# for every face, store what vertex comes after and before the current vertex
for x in range(len(fInput)):
i = fInput[x]
for j in range(len(i)):
vDict[i[j - 1]][i[j]] = [i[j - 2], x]
if len(vDict[i[j - 1]]) == 1:
vDict[i[j - 1]][-1] = i[j]
# the actual connection database: exists out of:
# [vtrunc pos, etrunc pos, connected vert IDs, connected face IDs]
vData = [[[], [], [], []] for i in vInput]
fvOutput = [] # faces created from truncated vertices
feOutput = [] # faces created from truncated edges
vOutput = [] # newly created vertices
for x in range(len(vInput)):
i = vDict[x] # lookup the current vertex
current = i[-1]
while True: # follow the chain to get a ccw order of connected verts and faces
vData[x][2].append(i[current][0])
vData[x][3].append(i[current][1])
# create truncated vertices
vData[x][0].append((1 - vtrunc) * vInput[x] + vtrunc * vInput[vData[x][2][-1]])
current = i[current][0]
if current == i[-1]:
break # if we're back at the first: stop the loop
fvOutput.append([]) # new face from truncated vert
fOffset = x * (len(i) - 1) # where to start off counting faceVerts
# only create one vert where one is needed (v1 todo: done)
if etrunc == 0.5:
for j in range(len(i) - 1):
vOutput.append((vData[x][0][j] + vData[x][0][j - 1]) * etrunc) # create vert
fvOutput[x].append(fOffset + j) # add to face
fvOutput[x] = fvOutput[x][1:] + [fvOutput[x][0]] # rotate face for ease later on
# create faces from truncated edges.
for j in range(len(i) - 1):
if x > vData[x][2][j]: # only create when other vertex has been added
index = vData[vData[x][2][j]][2].index(x)
feOutput.append([fvOutput[x][j], fvOutput[x][j - 1],
fvOutput[vData[x][2][j]][index],
fvOutput[vData[x][2][j]][index - 1]])
# edge truncation between none and full
elif etrunc > 0:
for j in range(len(i) - 1):
# create snubs from selecting verts from rectified meshes
if rSnub:
vOutput.append(etrunc * vData[x][0][j] + (1 - etrunc) * vData[x][0][j - 1])
fvOutput[x].append(fOffset + j)
elif lSnub:
vOutput.append((1 - etrunc) * vData[x][0][j] + etrunc * vData[x][0][j - 1])
fvOutput[x].append(fOffset + j)
else: # noSnub, select both verts from rectified mesh
vOutput.append(etrunc * vData[x][0][j] + (1 - etrunc) * vData[x][0][j - 1])
vOutput.append((1 - etrunc) * vData[x][0][j] + etrunc * vData[x][0][j - 1])
fvOutput[x].append(2 * fOffset + 2 * j)
fvOutput[x].append(2 * fOffset + 2 * j + 1)
# rotate face for ease later on
if noSnub:
fvOutput[x] = fvOutput[x][2:] + fvOutput[x][:2]
else:
fvOutput[x] = fvOutput[x][1:] + [fvOutput[x][0]]
# create single face for each edge
if noSnub:
for j in range(len(i) - 1):
if x > vData[x][2][j]:
index = vData[vData[x][2][j]][2].index(x)
feOutput.append([fvOutput[x][j * 2], fvOutput[x][2 * j - 1],
fvOutput[vData[x][2][j]][2 * index],
fvOutput[vData[x][2][j]][2 * index - 1]])
# create 2 tri's for each edge for the snubs
elif rSnub:
for j in range(len(i) - 1):
if x > vData[x][2][j]:
index = vData[vData[x][2][j]][2].index(x)
feOutput.append([fvOutput[x][j], fvOutput[x][j - 1],
fvOutput[vData[x][2][j]][index]])
feOutput.append([fvOutput[x][j], fvOutput[vData[x][2][j]][index],
fvOutput[vData[x][2][j]][index - 1]])
elif lSnub:
for j in range(len(i) - 1):
if x > vData[x][2][j]:
index = vData[vData[x][2][j]][2].index(x)
feOutput.append([fvOutput[x][j], fvOutput[x][j - 1],
fvOutput[vData[x][2][j]][index - 1]])
feOutput.append([fvOutput[x][j - 1], fvOutput[vData[x][2][j]][index],
fvOutput[vData[x][2][j]][index - 1]])
# special rules for birectified mesh (v1 todo: done)
elif vtrunc == 0.5:
for j in range(len(i) - 1):
if x < vData[x][2][j]: # use current vert, since other one has not passed yet
vOutput.append(vData[x][0][j])
fvOutput[x].append(len(vOutput) - 1)
else:
# search for other edge to avoid duplicity
connectee = vData[x][2][j]
fvOutput[x].append(fvOutput[connectee][vData[connectee][2].index(x)])
else: # vert truncation only
vOutput.extend(vData[x][0]) # use generated verts from way above
for j in range(len(i) - 1): # create face from them
fvOutput[x].append(fOffset + j)
# calculate supposed vertex length to ensure continuity
if supposedSize and not dual: # this to make the vtrunc > 1 work
supposedSize *= len(fvOutput[0]) / vSum(vOutput[i] for i in fvOutput[0]).length
vOutput = [-i * supposedSize for i in vOutput]
# Inverting vOutput turns the mesh inside-out, so normals need to be flipped.
flipNormals = True
else:
flipNormals = False
# create new faces by replacing old vert IDs by newly generated verts
ffOutput = [[] for i in fInput]
for x in range(len(fInput)):
# only one generated vert per vertex, so choose accordingly
if etrunc == 0.5 or (etrunc == 0 and vtrunc == 0.5) or lSnub or rSnub:
ffOutput[x] = [fvOutput[i][vData[i][3].index(x) - 1] for i in fInput[x]]
# two generated verts per vertex
elif etrunc > 0:
for i in fInput[x]:
ffOutput[x].append(fvOutput[i][2 * vData[i][3].index(x) - 1])
ffOutput[x].append(fvOutput[i][2 * vData[i][3].index(x) - 2])
else: # cutting off corners also makes 2 verts
for i in fInput[x]:
ffOutput[x].append(fvOutput[i][vData[i][3].index(x)])
ffOutput[x].append(fvOutput[i][vData[i][3].index(x) - 1])
if not dual:
fOutput = fvOutput + feOutput + ffOutput
if flipNormals:
fOutput = flippedFaceNormals(fOutput)
return vOutput, fOutput
else:
# do the same procedure as above, only now on the generated mesh
# generate connection database
vDict = [{} for i in vOutput]
dvOutput = [0 for i in fvOutput + feOutput + ffOutput]
dfOutput = []
for x in range(len(dvOutput)): # for every face
i = (fvOutput + feOutput + ffOutput)[x] # choose face to work with
# find vertex from face
normal = (vOutput[i[0]] - vOutput[i[1]]).cross(vOutput[i[2]] - vOutput[i[1]]).normalized()
dvOutput[x] = normal / (normal.dot(vOutput[i[0]]))
for j in range(len(i)): # create vert chain
vDict[i[j - 1]][i[j]] = [i[j - 2], x]
if len(vDict[i[j - 1]]) == 1:
vDict[i[j - 1]][-1] = i[j]
# calculate supposed size for continuity
supposedSize = vSum([vInput[i] for i in fInput[0]]).length / len(fInput[0])
supposedSize /= dvOutput[-1].length
dvOutput = [i * supposedSize for i in dvOutput]
# use chains to create faces
for x in range(len(vOutput)):
i = vDict[x]
current = i[-1]
face = []
while True:
face.append(i[current][1])
current = i[current][0]
if current == i[-1]:
break
dfOutput.append(face)
return dvOutput, dfOutput
class Solids(bpy.types.Operator):
"""Add one of the (regular) solids (mesh)"""
bl_idname = "mesh.primitive_solid_add"
bl_label = "Add Regular Solid"
bl_description = "Add one of the Platonic, Archimedean or Catalan solids"
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
source: EnumProperty(
items=(("4", "Tetrahedron", ""),
("6", "Hexahedron", ""),
("8", "Octahedron", ""),
("12", "Dodecahedron", ""),
("20", "Icosahedron", "")),
name="Source",
description="Starting point of your solid"
)
size: FloatProperty(
name="Size",
description="Radius of the sphere through the vertices",
min=0.01,
soft_min=0.01,
max=100,
soft_max=100,
default=1.0
)
vTrunc: FloatProperty(
name="Vertex Truncation",
description="Amount of vertex truncation",
min=0.0,
soft_min=0.0,
max=2.0,
soft_max=2.0,
default=0.0,
precision=3,
step=0.5
)
eTrunc: FloatProperty(
name="Edge Truncation",
description="Amount of edge truncation",
min=0.0,
soft_min=0.0,
max=1.0,
soft_max=1.0,
default=0.0,
precision=3,
step=0.2
)
snub: EnumProperty(
items=(("None", "No Snub", ""),
("Left", "Left Snub", ""),
("Right", "Right Snub", "")),
name="Snub",
description="Create the snub version"
)
dual: BoolProperty(
name="Dual",
description="Create the dual of the current solid",
default=False
)
keepSize: BoolProperty(
name="Keep Size",
description="Keep the whole solid at a constant size",
default=False
)
def execute(self, context):
# generate mesh
verts, faces = createSolid(self.source,
self.vTrunc,
self.eTrunc,
self.dual,
self.snub
)
# resize to normal size, or if keepSize, make sure all verts are of length 'size'
if self.keepSize:
rad = self.size / verts[-1 if self.dual else 0].length
else:
rad = self.size
verts = [i * rad for i in verts]
# generate object
# Create new mesh
mesh = bpy.data.meshes.new("Solid")
# Make a mesh from a list of verts/edges/faces.
mesh.from_pydata(verts, [], faces)
# Update mesh geometry after adding stuff.
mesh.update()
object_data_add(context, mesh, operator=None)
# object generation done
return {'FINISHED'}
@@ -0,0 +1,285 @@
# SPDX-FileCopyrightText: 2015-2022 Blender Foundation
#
# SPDX-License-Identifier: GPL-2.0-or-later
# Original by Fourmadmen
import bpy
from mathutils import (
Vector,
Quaternion,
)
from math import pi
from bpy.props import (
IntProperty,
FloatProperty,
StringProperty,
BoolProperty,
)
from bpy_extras import object_utils
from .interface import draw_transform_props
# Create a new mesh (object) from verts/edges/faces.
# verts/edges/faces ... List of vertices/edges/faces for the
# new mesh (as used in from_pydata)
# name ... Name of the new mesh (& object)
def create_mesh_object(context, verts, edges, faces, name):
# Create new mesh
mesh = bpy.data.meshes.new(name)
# Make a mesh from a list of verts/edges/faces.
mesh.from_pydata(verts, edges, faces)
# Update mesh geometry after adding stuff.
mesh.update()
from bpy_extras import object_utils
return object_utils.object_data_add(context, mesh, operator=None)
# A very simple "bridge" tool.
def createFaces(vertIdx1, vertIdx2, closed=False, flipped=False):
faces = []
if not vertIdx1 or not vertIdx2:
return None
if len(vertIdx1) < 2 and len(vertIdx2) < 2:
return None
fan = False
if (len(vertIdx1) != len(vertIdx2)):
if (len(vertIdx1) == 1 and len(vertIdx2) > 1):
fan = True
else:
return None
total = len(vertIdx2)
if closed:
# Bridge the start with the end.
if flipped:
face = [
vertIdx1[0],
vertIdx2[0],
vertIdx2[total - 1]]
if not fan:
face.append(vertIdx1[total - 1])
faces.append(face)
else:
face = [vertIdx2[0], vertIdx1[0]]
if not fan:
face.append(vertIdx1[total - 1])
face.append(vertIdx2[total - 1])
faces.append(face)
# Bridge the rest of the faces.
for num in range(total - 1):
if flipped:
if fan:
face = [vertIdx2[num], vertIdx1[0], vertIdx2[num + 1]]
else:
face = [vertIdx2[num], vertIdx1[num],
vertIdx1[num + 1], vertIdx2[num + 1]]
faces.append(face)
else:
if fan:
face = [vertIdx1[0], vertIdx2[num], vertIdx2[num + 1]]
else:
face = [vertIdx1[num], vertIdx2[num],
vertIdx2[num + 1], vertIdx1[num + 1]]
faces.append(face)
return faces
# @todo Clean up vertex&face creation process a bit.
def add_star(points, outer_radius, inner_radius, height):
PI_2 = pi * 2
z_axis = (0, 0, 1)
verts = []
faces = []
segments = points * 2
half_height = height / 2.0
vert_idx_top = len(verts)
verts.append(Vector((0.0, 0.0, half_height)))
vert_idx_bottom = len(verts)
verts.append(Vector((0.0, 0.0, -half_height)))
edgeloop_top = []
edgeloop_bottom = []
for index in range(segments):
quat = Quaternion(z_axis, (index / segments) * PI_2)
if index % 2:
# Uneven
radius = outer_radius
else:
# Even
radius = inner_radius
edgeloop_top.append(len(verts))
vec = quat @ Vector((radius, 0, half_height))
verts.append(vec)
edgeloop_bottom.append(len(verts))
vec = quat @ Vector((radius, 0, -half_height))
verts.append(vec)
faces_top = createFaces([vert_idx_top], edgeloop_top, closed=True)
faces_outside = createFaces(edgeloop_top, edgeloop_bottom, closed=True)
faces_bottom = createFaces([vert_idx_bottom], edgeloop_bottom,
flipped=True, closed=True)
faces.extend(faces_top)
faces.extend(faces_outside)
faces.extend(faces_bottom)
return verts, faces
class AddStar(bpy.types.Operator, object_utils.AddObjectHelper):
bl_idname = "mesh.primitive_star_add"
bl_label = "Simple Star"
bl_description = "Construct a star mesh"
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
Star : BoolProperty(name = "Star",
default = True,
description = "Star")
change : BoolProperty(name = "Change",
default = False,
description = "change Star")
points: IntProperty(
name="Points",
description="Number of points for the star",
min=2,
max=256,
default=5
)
outer_radius: FloatProperty(
name="Outer Radius",
description="Outer radius of the star",
min=0.01,
max=9999.0,
default=1.0
)
innter_radius: FloatProperty(
name="Inner Radius",
description="Inner radius of the star",
min=0.01,
max=9999.0,
default=0.5
)
height: FloatProperty(name="Height",
description="Height of the star",
min=0.01,
max=9999.0,
default=0.5
)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
layout.separator()
layout.prop(self, 'points')
layout.prop(self, 'height')
col = layout.column(align=True)
col.prop(self, 'outer_radius', text='Radius Outer')
col.prop(self, 'innter_radius', text='Inner')
if self.change == False:
layout.separator()
draw_transform_props(self, layout)
def execute(self, context):
# turn off 'Enter Edit Mode'
use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
bpy.context.preferences.edit.use_enter_edit_mode = False
if bpy.context.mode == "OBJECT":
if context.selected_objects != [] and context.active_object and \
(context.active_object.data is not None) and ('Star' in context.active_object.data.keys()) and \
(self.change == True):
obj = context.active_object
oldmesh = obj.data
oldmeshname = obj.data.name
verts, faces = add_star(
self.points,
self.outer_radius,
self.innter_radius,
self.height
)
mesh = bpy.data.meshes.new('Star')
mesh.from_pydata(verts, [], faces)
obj.data = mesh
for material in oldmesh.materials:
obj.data.materials.append(material)
bpy.data.meshes.remove(oldmesh)
obj.data.name = oldmeshname
else:
verts, faces = add_star(
self.points,
self.outer_radius,
self.innter_radius,
self.height
)
mesh = bpy.data.meshes.new('Star')
mesh.from_pydata(verts, [], faces)
obj = object_utils.object_data_add(context, mesh, operator=self)
obj.data["Star"] = True
obj.data["change"] = False
for prm in StarParameters():
obj.data[prm] = getattr(self, prm)
if bpy.context.mode == "EDIT_MESH":
active_object = context.active_object
name_active_object = active_object.name
bpy.ops.object.mode_set(mode='OBJECT')
verts, faces = add_star(
self.points,
self.outer_radius,
self.innter_radius,
self.height
)
mesh = bpy.data.meshes.new('Star')
mesh.from_pydata(verts, [], faces)
obj = object_utils.object_data_add(context, mesh, operator=self)
obj.select_set(True)
active_object.select_set(True)
bpy.context.view_layer.objects.active = active_object
bpy.ops.object.join()
context.active_object.name = name_active_object
bpy.ops.object.mode_set(mode='EDIT')
if use_enter_edit_mode:
bpy.ops.object.mode_set(mode = 'EDIT')
# restore pre operator state
bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
return {'FINISHED'}
def StarParameters():
StarParameters = [
"points",
"outer_radius",
"innter_radius",
"height",
]
return StarParameters
@@ -0,0 +1,331 @@
# SPDX-FileCopyrightText: 2011-2022 Blender Foundation
#
# SPDX-License-Identifier: GPL-2.0-or-later
# Author: DreamPainter
import bpy
from bpy.props import (
FloatProperty,
BoolProperty,
IntProperty,
EnumProperty,
)
from math import pi, cos, sin
from mathutils import Vector
from bpy_extras import object_utils
from .interface import draw_transform_props
# A very simple "bridge" tool
def createFaces(vertIdx1, vertIdx2, closed=False, flipped=False):
faces = []
if not vertIdx1 or not vertIdx2:
return None
if len(vertIdx1) < 2 and len(vertIdx2) < 2:
return None
fan = False
if (len(vertIdx1) != len(vertIdx2)):
if (len(vertIdx1) == 1 and len(vertIdx2) > 1):
fan = True
else:
return None
total = len(vertIdx2)
if closed:
# Bridge the start with the end.
if flipped:
face = [
vertIdx1[0],
vertIdx2[0],
vertIdx2[total - 1]]
if not fan:
face.append(vertIdx1[total - 1])
faces.append(face)
else:
face = [vertIdx2[0], vertIdx1[0]]
if not fan:
face.append(vertIdx1[total - 1])
face.append(vertIdx2[total - 1])
faces.append(face)
# Bridge the rest of the faces.
for num in range(total - 1):
if flipped:
if fan:
face = [vertIdx2[num], vertIdx1[0], vertIdx2[num + 1]]
else:
face = [vertIdx2[num], vertIdx1[num],
vertIdx1[num + 1], vertIdx2[num + 1]]
faces.append(face)
else:
if fan:
face = [vertIdx1[0], vertIdx2[num], vertIdx2[num + 1]]
else:
face = [vertIdx1[num], vertIdx2[num],
vertIdx2[num + 1], vertIdx1[num + 1]]
faces.append(face)
return faces
def power(a, b):
if a < 0:
return -((-a) ** b)
return a ** b
def supertoroid(R, r, u, v, n1, n2):
"""
R = big radius
r = small radius
u = lateral segmentation
v = radial segmentation
n1 = value determines the shape of the torus
n2 = value determines the shape of the cross-section
"""
# create the necessary constants
a = 2 * pi / u
b = 2 * pi / v
verts = []
faces = []
# create each cross-section by calculating each vector on the
# the wannabe circle
# x = (cos(theta) ** n1)*(R + r * (cos(phi) ** n2))
# y = (sin(theta) ** n1)*(R + r * (cos(phi) ** n2))
# z = (r * sin(phi) ** n2)
# with theta and phi ranging from 0 to 2pi
for i in range(u):
s = power(sin(i * a), n1)
c = power(cos(i * a), n1)
for j in range(v):
c2 = R + r * power(cos(j * b), n2)
s2 = r * power(sin(j * b), n2)
verts.append(Vector((c * c2, s * c2, s2)))
# bridge the last circle with the previous circle
if i > 0: # but not for the first circle, 'cus there's no previous before the first
f = createFaces(range((i - 1) * v, i * v), range(i * v, (i + 1) * v), closed=True)
faces.extend(f)
# bridge the last circle with the first
f = createFaces(range((u - 1) * v, u * v), range(v), closed=True)
faces.extend(f)
return verts, faces
class add_supertoroid(bpy.types.Operator, object_utils.AddObjectHelper):
bl_idname = "mesh.primitive_supertoroid_add"
bl_label = "Add SuperToroid"
bl_description = "Construct a supertoroid mesh"
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
SuperToroid : BoolProperty(name = "SuperToroid",
default = True,
description = "SuperToroid")
change : BoolProperty(name = "Change",
default = False,
description = "change SuperToroid")
method: EnumProperty(
name='Method',
description='Method for determining the size and thickness of the torus',
items=(
('MAJOR-MINOR', 'Major / Minor', 'Uses the major radius for the overall size and the minor for the thickness'),
('INT-EXT', 'Interior / Exterior', 'Uses the absolute size of the inner and outer circles to determine the size and thickness'),
),
default='MAJOR-MINOR',
)
R: FloatProperty(
name="Big radius",
description="The radius inside the tube",
default=1.0,
min=0.01, max=100.0
)
r: FloatProperty(
name="Small radius",
description="The radius of the tube",
default=0.3,
min=0.01, max=100.0
)
outer_r: FloatProperty(
name="Exterior Radius",
description="Total Exterior Radius of the torus",
min=0.01,
max=100.0,
default=1.3
)
inner_r: FloatProperty(
name="Inside Radius",
description="Total Interior Radius of the torus",
min=0.01,
max=100.0,
default=0.7
)
u: IntProperty(
name="U-segments",
description="Radial segmentation",
default=16,
min=3, max=265
)
v: IntProperty(
name="V-segments",
description="Lateral segmentation",
default=8,
min=3, max=265
)
n1: FloatProperty(
name="Ring manipulator",
description="Manipulates the shape of the Ring",
default=1.0,
min=0.01, max=100.0
)
n2: FloatProperty(
name="Cross manipulator",
description="Manipulates the shape of the cross-section",
default=1.0,
min=0.01, max=100.0
)
edit: BoolProperty(
name="",
description="",
default=False,
options={'HIDDEN'}
)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
layout.separator()
layout.prop(self, 'method', text='Dimensions Mode')
col = layout.column(align=True)
if self.method == 'MAJOR-MINOR':
col.prop(self, 'R', text='Radius Major')
col.prop(self, 'r', text='Minor')
else:
col.prop(self, 'outer_r', text='Radius Exterior')
col.prop(self, 'inner_r', text='Interior')
col = layout.column(align=True)
col.prop(self, 'u', text='Segments Major')
col.prop(self, 'v', text='Minor')
layout.prop(self, 'n1', text='Ring')
layout.prop(self, 'n2', text='Cross')
if self.change == False:
draw_transform_props(self, layout)
def execute(self, context):
# turn off 'Enter Edit Mode'
use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
bpy.context.preferences.edit.use_enter_edit_mode = False
props = self.properties
# check how the radii properties must be used
if props.method == 'INT-EXT':
rad1 = (props.outer_r + props.inner_r) / 2
rad2 = (props.outer_r - props.inner_r) / 2
# for consistency in the mesh, ie no crossing faces, make the largest of the two
# the outer radius
if rad2 > rad1:
[rad1, rad2] = [rad2, rad1]
else:
rad1 = props.R
rad2 = props.r
# again for consistency, make the radius in the tube,
# at least as big as the radius of the tube
if rad2 > rad1:
rad1 = rad2
if bpy.context.mode == "OBJECT":
if context.selected_objects != [] and context.active_object and \
(context.active_object.data is not None) and ('SuperToroid' in context.active_object.data.keys()) and \
(self.change == True):
obj = context.active_object
oldmesh = obj.data
oldmeshname = obj.data.name
verts, faces = supertoroid(rad1,
rad2,
props.u,
props.v,
props.n1,
props.n2
)
mesh = bpy.data.meshes.new('SuperToroid')
mesh.from_pydata(verts, [], faces)
obj.data = mesh
for material in oldmesh.materials:
obj.data.materials.append(material)
bpy.data.meshes.remove(oldmesh)
obj.data.name = oldmeshname
else:
verts, faces = supertoroid(rad1,
rad2,
props.u,
props.v,
props.n1,
props.n2
)
mesh = bpy.data.meshes.new('SuperToroid')
mesh.from_pydata(verts, [], faces)
obj = object_utils.object_data_add(context, mesh, operator=self)
obj.data["SuperToroid"] = True
obj.data["change"] = False
for prm in SuperToroidParameters():
obj.data[prm] = getattr(self, prm)
if bpy.context.mode == "EDIT_MESH":
active_object = context.active_object
name_active_object = active_object.name
bpy.ops.object.mode_set(mode='OBJECT')
verts, faces = supertoroid(rad1,
rad2,
props.u,
props.v,
props.n1,
props.n2
)
mesh = bpy.data.meshes.new('SuperToroid')
mesh.from_pydata(verts, [], faces)
obj = object_utils.object_data_add(context, mesh, operator=self)
obj.select_set(True)
active_object.select_set(True)
bpy.context.view_layer.objects.active = active_object
bpy.ops.object.join()
context.active_object.name = name_active_object
bpy.ops.object.mode_set(mode='EDIT')
if use_enter_edit_mode:
bpy.ops.object.mode_set(mode = 'EDIT')
# restore pre operator state
bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
return {'FINISHED'}
def SuperToroidParameters():
SuperToroidParameters = [
"R",
"r",
"u",
"v",
"n1",
"n2",
"method",
"edit",
"inner_r",
"outer_r",
]
return SuperToroidParameters
@@ -0,0 +1,865 @@
# SPDX-FileCopyrightText: 2012-2022 Blender Foundation
#
# SPDX-License-Identifier: GPL-2.0-or-later
# Author, Anthony D'Agostino
import bpy
from bpy.props import (
IntProperty,
EnumProperty,
)
import mathutils
import io
import operator
import functools
from bpy_extras import object_utils
from .interface import draw_transform_props
class AddTeapot(bpy.types.Operator, object_utils.AddObjectHelper):
bl_idname = "mesh.primitive_teapot_add"
bl_label = "Add Teapot"
bl_description = "Construct a teapot or teaspoon mesh"
bl_options = {"REGISTER", "UNDO"}
resolution: IntProperty(
name="Resolution",
description="Resolution of the Teapot",
default=5,
min=2, max=15,
)
objecttype: EnumProperty(
name="Type",
description="Type of Bezier Object",
items=(('1', "Teapot", "Construct a teapot mesh"),
('2', "Tea Spoon", "Construct a teaspoon mesh")),
default='1',
)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
layout.prop(self, 'objecttype')
layout.prop(self, 'resolution')
layout.separator()
draw_transform_props(self, layout)
def execute(self, context):
# turn off 'Enter Edit Mode'
use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
bpy.context.preferences.edit.use_enter_edit_mode = False
cmode = bpy.context.mode
verts, faces = make_teapot(self.objecttype,
self.resolution)
# Actually create the mesh object from this geometry data.
obj = create_mesh_object(self, context, verts, [], faces, "Teapot")
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.remove_doubles()
if cmode != "EDIT_MESH":
bpy.ops.object.mode_set(mode=cmode)
if use_enter_edit_mode:
bpy.ops.object.mode_set(mode = 'EDIT')
# restore pre operator state
bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
return {'FINISHED'}
def create_mesh_face_hack(faces):
# FIXME, faces with duplicate vertices shouldn't be created in the first place.
faces_copy = []
for f in faces:
f_copy = []
for i in f:
if i not in f_copy:
f_copy.append(i)
faces_copy.append(f_copy)
faces[:] = faces_copy
def create_mesh_object(self, context, verts, edges, faces, name):
create_mesh_face_hack(faces)
# Create new mesh
mesh = bpy.data.meshes.new(name)
# Make a mesh from a list of verts/edges/faces.
mesh.from_pydata(verts, edges, faces)
# Update mesh geometry after adding stuff.
mesh.update()
return object_utils.object_data_add(context, mesh, operator=self)
# ==========================
# === Bezier patch Block ===
# ==========================
def read_indexed_patch_file(filename):
file = io.StringIO(filename)
rawpatches = []
patches = []
numpatches = int(file.readline())
for i in range(numpatches):
line = file.readline()
(a, b, c, d,
e, f, g, h,
i, j, k, l,
m, n, o, p,
) = map(int, line.split(","))
patches.append([[a, b, c, d], [e, f, g, h], [i, j, k, l], [m, n, o, p]])
rawpatches.append([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]])
verts = []
numverts = int(file.readline())
for i in range(numverts):
line = file.readline()
v1, v2, v3 = map(float, line.split(","))
verts.append((v1, v2, v3))
for i in range(len(patches)):
for j in range(4): # len(patches[i])):
for k in range(4): # len(patches[i][j])):
index = patches[i][j][k] - 1
rawpatches[i][j][k] = verts[index]
return rawpatches
def patches_to_raw(patches, resolution):
raw = []
for patch in patches:
verts = make_verts(patch, resolution)
faces = make_faces(resolution)
rawquads = indexed_to_rawquads(verts, faces)
raw.append(rawquads)
raw = functools.reduce(operator.add, raw) # flatten the list
return raw
def make_bezier(ctrlpnts, resolution):
def b1(t):
return t * t * t
def b2(t):
return 3.0 * t * t * (1.0 - t)
def b3(t):
return 3.0 * t * (1.0 - t) * (1.0 - t)
def b4(t):
return (1.0 - t) * (1.0 - t) * (1.0 - t)
p1, p2, p3, p4 = map(mathutils.Vector, ctrlpnts)
def makevert(t):
x, y, z = b1(t) * p1 + b2(t) * p2 + b3(t) * p3 + b4(t) * p4
return (x, y, z)
curveverts = [makevert(i / resolution) for i in range(resolution + 1)]
return curveverts
def make_verts(a, resolution):
s = []
for i in a:
c = make_bezier(i, resolution)
s.append(c)
b = transpose(s)
s = []
for i in b:
c = make_bezier(i, resolution)
s.append(c)
verts = s
verts = functools.reduce(operator.add, verts) # flatten the list
return verts
def make_faces(resolution):
n = resolution + 1
faces = []
for i in range(resolution):
for j in range(resolution):
v1 = (i + 1) * n + j
v2 = (i + 1) * n + j + 1
v3 = i * n + j + 1
v4 = i * n + j
faces.append([v1, v2, v3, v4])
return faces
def indexed_to_rawquads(verts, faces):
rows = len(faces)
cols = len(faces[0]) # or 4
rawquads = [[None] * cols for i in range(rows)]
for i in range(rows):
for j in range(cols):
index = faces[i][j]
rawquads[i][j] = verts[index]
return rawquads
def raw_to_indexed(rawfaces):
# Generate verts and faces lists, without dups
verts = []
coords = {}
index = 0
for i in range(len(rawfaces)):
for j in range(len(rawfaces[i])):
vertex = rawfaces[i][j]
if vertex not in coords:
coords[vertex] = index
index += 1
verts.append(vertex)
rawfaces[i][j] = coords[vertex]
return verts, rawfaces
def transpose(rowsbycols):
rows = len(rowsbycols)
cols = len(rowsbycols[0])
colsbyrows = [[None] * rows for i in range(cols)]
for i in range(cols):
for j in range(rows):
colsbyrows[i][j] = rowsbycols[j][i]
return colsbyrows
def make_teapot(enumname, resolution):
filenames = [None, teapot, teaspoon]
try:
indexes = int(enumname)
filename = filenames[indexes]
except:
print("Add Teapot Error: EnumProperty could not be set")
filename = filenames[1]
patches = read_indexed_patch_file(filename)
raw = patches_to_raw(patches, resolution)
verts, faces = raw_to_indexed(raw)
return (verts, faces)
# =================================
# === Indexed Bezier Data Block ===
# =================================
teapot = """32
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16
4,17,18,19,8,20,21,22,12,23,24,25,16,26,27,28
19,29,30,31,22,32,33,34,25,35,36,37,28,38,39,40
31,41,42,1,34,43,44,5,37,45,46,9,40,47,48,13
13,14,15,16,49,50,51,52,53,54,55,56,57,58,59,60
16,26,27,28,52,61,62,63,56,64,65,66,60,67,68,69
28,38,39,40,63,70,71,72,66,73,74,75,69,76,77,78
40,47,48,13,72,79,80,49,75,81,82,53,78,83,84,57
57,58,59,60,85,86,87,88,89,90,91,92,93,94,95,96
60,67,68,69,88,97,98,99,92,100,101,102,96,103,104,105
69,76,77,78,99,106,107,108,102,109,110,111,105,112,113,114
78,83,84,57,108,115,116,85,111,117,118,89,114,119,120,93
121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136
124,137,138,121,128,139,140,125,132,141,142,129,136,143,144,133
133,134,135,136,145,146,147,148,149,150,151,152,69,153,154,155
136,143,144,133,148,156,157,145,152,158,159,149,155,160,161,69
162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177
165,178,179,162,169,180,181,166,173,182,183,170,177,184,185,174
174,175,176,177,186,187,188,189,190,191,192,193,194,195,196,197
177,184,185,174,189,198,199,186,193,200,201,190,197,202,203,194
204,204,204,204,207,208,209,210,211,211,211,211,212,213,214,215
204,204,204,204,210,217,218,219,211,211,211,211,215,220,221,222
204,204,204,204,219,224,225,226,211,211,211,211,222,227,228,229
204,204,204,204,226,230,231,207,211,211,211,211,229,232,233,212
212,213,214,215,234,235,236,237,238,239,240,241,242,243,244,245
215,220,221,222,237,246,247,248,241,249,250,251,245,252,253,254
222,227,228,229,248,255,256,257,251,258,259,260,254,261,262,263
229,232,233,212,257,264,265,234,260,266,267,238,263,268,269,242
270,270,270,270,279,280,281,282,275,276,277,278,271,272,273,274
270,270,270,270,282,289,290,291,278,286,287,288,274,283,284,285
270,270,270,270,291,298,299,300,288,295,296,297,285,292,293,294
270,270,270,270,300,305,306,279,297,303,304,275,294,301,302,271
306
1.4,0.0,2.4
1.4,-0.784,2.4
0.784,-1.4,2.4
0.0,-1.4,2.4
1.3375,0.0,2.53125
1.3375,-0.749,2.53125
0.749,-1.3375,2.53125
0.0,-1.3375,2.53125
1.4375,0.0,2.53125
1.4375,-0.805,2.53125
0.805,-1.4375,2.53125
0.0,-1.4375,2.53125
1.5,0.0,2.4
1.5,-0.84,2.4
0.84,-1.5,2.4
0.0,-1.5,2.4
-0.784,-1.4,2.4
-1.4,-0.784,2.4
-1.4,0.0,2.4
-0.749,-1.3375,2.53125
-1.3375,-0.749,2.53125
-1.3375,0.0,2.53125
-0.805,-1.4375,2.53125
-1.4375,-0.805,2.53125
-1.4375,0.0,2.53125
-0.84,-1.5,2.4
-1.5,-0.84,2.4
-1.5,0.0,2.4
-1.4,0.784,2.4
-0.784,1.4,2.4
0.0,1.4,2.4
-1.3375,0.749,2.53125
-0.749,1.3375,2.53125
0.0,1.3375,2.53125
-1.4375,0.805,2.53125
-0.805,1.4375,2.53125
0.0,1.4375,2.53125
-1.5,0.84,2.4
-0.84,1.5,2.4
0.0,1.5,2.4
0.784,1.4,2.4
1.4,0.784,2.4
0.749,1.3375,2.53125
1.3375,0.749,2.53125
0.805,1.4375,2.53125
1.4375,0.805,2.53125
0.84,1.5,2.4
1.5,0.84,2.4
1.75,0.0,1.875
1.75,-0.98,1.875
0.98,-1.75,1.875
0.0,-1.75,1.875
2.0,0.0,1.35
2.0,-1.12,1.35
1.12,-2.0,1.35
0.0,-2.0,1.35
2.0,0.0,0.9
2.0,-1.12,0.9
1.12,-2.0,0.9
0.0,-2.0,0.9
-0.98,-1.75,1.875
-1.75,-0.98,1.875
-1.75,0.0,1.875
-1.12,-2.0,1.35
-2.0,-1.12,1.35
-2.0,0.0,1.35
-1.12,-2.0,0.9
-2.0,-1.12,0.9
-2.0,0.0,0.9
-1.75,0.98,1.875
-0.98,1.75,1.875
0.0,1.75,1.875
-2.0,1.12,1.35
-1.12,2.0,1.35
0.0,2.0,1.35
-2.0,1.12,0.9
-1.12,2.0,0.9
0.0,2.0,0.9
0.98,1.75,1.875
1.75,0.98,1.875
1.12,2.0,1.35
2.0,1.12,1.35
1.12,2.0,0.9
2.0,1.12,0.9
2.0,0.0,0.45
2.0,-1.12,0.45
1.12,-2.0,0.45
0.0,-2.0,0.45
1.5,0.0,0.225
1.5,-0.84,0.225
0.84,-1.5,0.225
0.0,-1.5,0.225
1.5,0.0,0.15
1.5,-0.84,0.15
0.84,-1.5,0.15
0.0,-1.5,0.15
-1.12,-2.0,0.45
-2.0,-1.12,0.45
-2.0,0.0,0.45
-0.84,-1.5,0.225
-1.5,-0.84,0.225
-1.5,0.0,0.225
-0.84,-1.5,0.15
-1.5,-0.84,0.15
-1.5,0.0,0.15
-2.0,1.12,0.45
-1.12,2.0,0.45
0.0,2.0,0.45
-1.5,0.84,0.225
-0.84,1.5,0.225
0.0,1.5,0.225
-1.5,0.84,0.15
-0.84,1.5,0.15
0.0,1.5,0.15
1.12,2.0,0.45
2.0,1.12,0.45
0.84,1.5,0.225
1.5,0.84,0.225
0.84,1.5,0.15
1.5,0.84,0.15
-1.6,0.0,2.025
-1.6,-0.3,2.025
-1.5,-0.3,2.25
-1.5,0.0,2.25
-2.3,0.0,2.025
-2.3,-0.3,2.025
-2.5,-0.3,2.25
-2.5,0.0,2.25
-2.7,0.0,2.025
-2.7,-0.3,2.025
-3.0,-0.3,2.25
-3.0,0.0,2.25
-2.7,0.0,1.8
-2.7,-0.3,1.8
-3.0,-0.3,1.8
-3.0,0.0,1.8
-1.5,0.3,2.25
-1.6,0.3,2.025
-2.5,0.3,2.25
-2.3,0.3,2.025
-3.0,0.3,2.25
-2.7,0.3,2.025
-3.0,0.3,1.8
-2.7,0.3,1.8
-2.7,0.0,1.575
-2.7,-0.3,1.575
-3.0,-0.3,1.35
-3.0,0.0,1.35
-2.5,0.0,1.125
-2.5,-0.3,1.125
-2.65,-0.3,0.9375
-2.65,0.0,0.9375
-2.0,-0.3,0.9
-1.9,-0.3,0.6
-1.9,0.0,0.6
-3.0,0.3,1.35
-2.7,0.3,1.575
-2.65,0.3,0.9375
-2.5,0.3,1.125
-1.9,0.3,0.6
-2.0,0.3,0.9
1.7,0.0,1.425
1.7,-0.66,1.425
1.7,-0.66,0.6
1.7,0.0,0.6
2.6,0.0,1.425
2.6,-0.66,1.425
3.1,-0.66,0.825
3.1,0.0,0.825
2.3,0.0,2.1
2.3,-0.25,2.1
2.4,-0.25,2.025
2.4,0.0,2.025
2.7,0.0,2.4
2.7,-0.25,2.4
3.3,-0.25,2.4
3.3,0.0,2.4
1.7,0.66,0.6
1.7,0.66,1.425
3.1,0.66,0.825
2.6,0.66,1.425
2.4,0.25,2.025
2.3,0.25,2.1
3.3,0.25,2.4
2.7,0.25,2.4
2.8,0.0,2.475
2.8,-0.25,2.475
3.525,-0.25,2.49375
3.525,0.0,2.49375
2.9,0.0,2.475
2.9,-0.15,2.475
3.45,-0.15,2.5125
3.45,0.0,2.5125
2.8,0.0,2.4
2.8,-0.15,2.4
3.2,-0.15,2.4
3.2,0.0,2.4
3.525,0.25,2.49375
2.8,0.25,2.475
3.45,0.15,2.5125
2.9,0.15,2.475
3.2,0.15,2.4
2.8,0.15,2.4
0.0,0.0,3.15
0.0,-0.002,3.15
0.002,0.0,3.15
0.8,0.0,3.15
0.8,-0.45,3.15
0.45,-0.8,3.15
0.0,-0.8,3.15
0.0,0.0,2.85
0.2,0.0,2.7
0.2,-0.112,2.7
0.112,-0.2,2.7
0.0,-0.2,2.7
-0.002,0.0,3.15
-0.45,-0.8,3.15
-0.8,-0.45,3.15
-0.8,0.0,3.15
-0.112,-0.2,2.7
-0.2,-0.112,2.7
-0.2,0.0,2.7
0.0,0.002,3.15
-0.8,0.45,3.15
-0.45,0.8,3.15
0.0,0.8,3.15
-0.2,0.112,2.7
-0.112,0.2,2.7
0.0,0.2,2.7
0.45,0.8,3.15
0.8,0.45,3.15
0.112,0.2,2.7
0.2,0.112,2.7
0.4,0.0,2.55
0.4,-0.224,2.55
0.224,-0.4,2.55
0.0,-0.4,2.55
1.3,0.0,2.55
1.3,-0.728,2.55
0.728,-1.3,2.55
0.0,-1.3,2.55
1.3,0.0,2.4
1.3,-0.728,2.4
0.728,-1.3,2.4
0.0,-1.3,2.4
-0.224,-0.4,2.55
-0.4,-0.224,2.55
-0.4,0.0,2.55
-0.728,-1.3,2.55
-1.3,-0.728,2.55
-1.3,0.0,2.55
-0.728,-1.3,2.4
-1.3,-0.728,2.4
-1.3,0.0,2.4
-0.4,0.224,2.55
-0.224,0.4,2.55
0.0,0.4,2.55
-1.3,0.728,2.55
-0.728,1.3,2.55
0.0,1.3,2.55
-1.3,0.728,2.4
-0.728,1.3,2.4
0.0,1.3,2.4
0.224,0.4,2.55
0.4,0.224,2.55
0.728,1.3,2.55
1.3,0.728,2.55
0.728,1.3,2.4
1.3,0.728,2.4
0.0,0.0,0.0
1.5,0.0,0.15
1.5,0.84,0.15
0.84,1.5,0.15
0.0,1.5,0.15
1.5,0.0,0.075
1.5,0.84,0.075
0.84,1.5,0.075
0.0,1.5,0.075
1.425,0.0,0.0
1.425,0.798,0.0
0.798,1.425,0.0
0.0,1.425,0.0
-0.84,1.5,0.15
-1.5,0.84,0.15
-1.5,0.0,0.15
-0.84,1.5,0.075
-1.5,0.84,0.075
-1.5,0.0,0.075
-0.798,1.425,0.0
-1.425,0.798,0.0
-1.425,0.0,0.0
-1.5,-0.84,0.15
-0.84,-1.5,0.15
0.0,-1.5,0.15
-1.5,-0.84,0.075
-0.84,-1.5,0.075
0.0,-1.5,0.075
-1.425,-0.798,0.0
-0.798,-1.425,0.0
0.0,-1.425,0.0
0.84,-1.5,0.15
1.5,-0.84,0.15
0.84,-1.5,0.075
1.5,-0.84,0.075
0.798,-1.425,0.0
1.425,-0.798,0.0
"""
teaspoon = """16
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16
17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32
33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48
49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64
65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80
81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96
97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112
113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128
129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144
145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160
161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176
177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192
193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208
209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224
225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240
241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256
256
-0.000107143,0.205357,0.0
0.0,0.196429,-0.0178571
0.0,0.196429,-0.0178571
0.000107143,0.205357,0.0
-0.0535714,0.205357,0.0
-0.0222714,0.178571,-0.0534286
0.0222714,0.178571,-0.0534286
0.0535714,0.205357,0.0
-0.107143,0.0952429,-0.0178571
-0.0446429,0.0952429,-0.0892857
0.0446429,0.0952429,-0.0892857
0.107143,0.0952429,-0.0178571
-0.107143,0.0,-0.0178571
-0.0446429,0.0,-0.0892857
0.0446429,0.0,-0.0892857
0.107143,0.0,-0.0178571
0.000107143,0.205357,0.0
0.000135714,0.207589,0.00446429
0.000157143,0.216518,0.00446429
0.000125,0.214286,0.0
0.0535714,0.205357,0.0
0.0613964,0.212054,0.0133571
0.0714286,0.220982,0.015625
0.0625,0.214286,0.0
0.107143,0.0952429,-0.0178571
0.122768,0.0952429,0.0
0.142857,0.0952429,0.00446429
0.125,0.0952429,-0.0178571
0.107143,0.0,-0.0178571
0.122768,0.0,0.0
0.142857,0.0,0.00446429
0.125,0.0,-0.0178571
0.000125,0.214286,0.0
0.0,0.205357,-0.0178571
0.0,0.205357,-0.0178571
-0.000125,0.214286,0.0
0.0625,0.214286,0.0
0.0267857,0.1875,-0.0625
-0.0267857,0.1875,-0.0625
-0.0625,0.214286,0.0
0.125,0.0952429,-0.0178571
0.0535714,0.0952429,-0.107143
-0.0535714,0.0952429,-0.107143
-0.125,0.0952429,-0.0178571
0.125,0.0,-0.0178571
0.0535714,0.0,-0.107143
-0.0535714,0.0,-0.107143
-0.125,0.0,-0.0178571
-0.000125,0.214286,0.0
-0.000157143,0.216518,0.00446429
-0.000135714,0.207589,0.00446429
-0.000107143,0.205357,0.0
-0.0625,0.214286,0.0
-0.0714286,0.220982,0.015625
-0.0613964,0.212054,0.0133571
-0.0535714,0.205357,0.0
-0.125,0.0952429,-0.0178571
-0.142857,0.0952429,0.00446429
-0.122768,0.0952429,0.0
-0.107143,0.0952429,-0.0178571
-0.125,0.0,-0.0178571
-0.142857,0.0,0.00446429
-0.122768,0.0,0.0
-0.107143,0.0,-0.0178571
-0.107143,0.0,-0.0178571
-0.0446429,0.0,-0.0892857
0.0446429,0.0,-0.0892857
0.107143,0.0,-0.0178571
-0.107143,-0.142857,-0.0178571
-0.0446429,-0.142857,-0.0892857
0.0446429,-0.142857,-0.0892857
0.107143,-0.142857,-0.0178571
-0.0133929,-0.160714,0.0386893
-0.00557857,-0.160714,0.0386893
0.00557857,-0.160714,0.0386893
0.0133929,-0.160714,0.0386893
-0.0133929,-0.25,0.0535714
-0.00557857,-0.25,0.0535714
0.00557857,-0.25,0.0535714
0.0133929,-0.25,0.0535714
0.107143,0.0,-0.0178571
0.122768,0.0,0.0
0.142857,0.0,0.00446429
0.125,0.0,-0.0178571
0.107143,-0.142857,-0.0178571
0.122768,-0.142857,0.0
0.142857,-0.142857,0.00446429
0.125,-0.142857,-0.0178571
0.0133929,-0.160714,0.0386893
0.0153464,-0.160714,0.0386893
0.0178571,-0.160714,0.0314357
0.015625,-0.160714,0.0297607
0.0133929,-0.25,0.0535714
0.0153464,-0.25,0.0535714
0.0178571,-0.25,0.0463179
0.015625,-0.25,0.0446429
0.125,0.0,-0.0178571
0.0535714,0.0,-0.107143
-0.0535714,0.0,-0.107143
-0.125,0.0,-0.0178571
0.125,-0.142857,-0.0178571
0.0535714,-0.142857,-0.107143
-0.0535714,-0.142857,-0.107143
-0.125,-0.142857,-0.0178571
0.015625,-0.160714,0.0297607
0.00669643,-0.160714,0.0230643
-0.00781071,-0.160714,0.0208321
-0.015625,-0.160714,0.0297607
0.015625,-0.25,0.0446429
0.00669643,-0.25,0.0379464
-0.00781071,-0.25,0.0357143
-0.015625,-0.25,0.0446429
-0.125,0.0,-0.0178571
-0.142857,0.0,0.00446429
-0.122768,0.0,0.0
-0.107143,0.0,-0.0178571
-0.125,-0.142857,-0.0178571
-0.142857,-0.142857,0.00446429
-0.122768,-0.142857,0.0
-0.107143,-0.142857,-0.0178571
-0.015625,-0.160714,0.0297607
-0.0175786,-0.160714,0.0319929
-0.0153464,-0.160714,0.0386893
-0.0133929,-0.160714,0.0386893
-0.015625,-0.25,0.0446429
-0.0175786,-0.25,0.046875
-0.0153464,-0.25,0.0535714
-0.0133929,-0.25,0.0535714
-0.0133929,-0.25,0.0535714
-0.00557857,-0.25,0.0535714
0.00557857,-0.25,0.0535714
0.0133929,-0.25,0.0535714
-0.0133929,-0.46425,0.0892857
-0.00557857,-0.46425,0.0892857
0.00557857,-0.46425,0.0892857
0.0133929,-0.46425,0.0892857
-0.0446429,-0.678571,0.0535714
-0.00892857,-0.678571,0.0625
0.00892857,-0.678571,0.0625
0.0446429,-0.678571,0.0535714
-0.0446429,-0.857143,0.0357143
-0.00892857,-0.857143,0.0446429
0.00892857,-0.857143,0.0446429
0.0446429,-0.857143,0.0357143
0.0133929,-0.25,0.0535714
0.0153464,-0.25,0.0535714
0.0178571,-0.25,0.0463179
0.015625,-0.25,0.0446429
0.0133929,-0.46425,0.0892857
0.0153464,-0.464286,0.0892857
0.0178571,-0.46425,0.0820321
0.015625,-0.46425,0.0803571
0.0446429,-0.678571,0.0535714
0.0535714,-0.678571,0.0513393
0.0535714,-0.678571,0.0334821
0.0446429,-0.678571,0.0357143
0.0446429,-0.857143,0.0357143
0.0535714,-0.857143,0.0334821
0.0535714,-0.857143,0.015625
0.0446429,-0.857143,0.0178571
0.015625,-0.25,0.0446429
0.00669643,-0.25,0.0379464
-0.00781071,-0.25,0.0357143
-0.015625,-0.25,0.0446429
0.015625,-0.46425,0.0803571
0.00669643,-0.464286,0.0736607
-0.00781071,-0.46425,0.0714286
-0.015625,-0.46425,0.0803571
0.0446429,-0.678571,0.0357143
0.00892857,-0.678571,0.0446429
-0.00892857,-0.678571,0.0446429
-0.0446429,-0.678571,0.0357143
0.0446429,-0.857143,0.0178571
0.00892857,-0.857143,0.0267857
-0.00892857,-0.857143,0.0267857
-0.0446429,-0.857143,0.0178571
-0.015625,-0.25,0.0446429
-0.0175786,-0.25,0.046875
-0.0153464,-0.25,0.0535714
-0.0133929,-0.25,0.0535714
-0.015625,-0.46425,0.0803571
-0.0175786,-0.464286,0.0825893
-0.0153464,-0.464286,0.0892857
-0.0133929,-0.46425,0.0892857
-0.0446429,-0.678571,0.0357143
-0.0535714,-0.678571,0.0334821
-0.0535714,-0.678571,0.0513393
-0.0446429,-0.678571,0.0535714
-0.0446429,-0.857143,0.0178571
-0.0535714,-0.857143,0.015625
-0.0535714,-0.857143,0.0334821
-0.0446429,-0.857143,0.0357143
-0.0446429,-0.857143,0.0357143
-0.00892857,-0.857143,0.0446429
0.00892857,-0.857143,0.0446429
0.0446429,-0.857143,0.0357143
-0.0446429,-0.928571,0.0285714
-0.00892857,-0.928571,0.0375
0.00892857,-0.928571,0.0375
0.0446429,-0.928571,0.0285714
-0.0539286,-0.999643,0.0178571
0.000357143,-0.999643,0.0178571
0.0,-0.999643,0.0178571
0.0535714,-0.999643,0.0178571
-0.000357143,-1,0.0178571
0.000357143,-1,0.0178571
0.0,-1,0.0178571
0.0,-1,0.0178571
0.0446429,-0.857143,0.0357143
0.0535714,-0.857143,0.0334821
0.0535714,-0.857143,0.015625
0.0446429,-0.857143,0.0178571
0.0446429,-0.928571,0.0285714
0.0535714,-0.928571,0.0263393
0.0535714,-0.928571,0.00848214
0.0446429,-0.928571,0.0107143
0.0535714,-0.999643,0.0178571
0.0669643,-0.999643,0.0178571
0.0673214,-0.999643,0.0
0.0539286,-0.999643,0.0
0.0,-1,0.0178571
0.0,-1,0.0178571
0.000357143,-1,0.0
0.000357143,-1,0.0
0.0446429,-0.857143,0.0178571
0.00892857,-0.857143,0.0267857
-0.00892857,-0.857143,0.0267857
-0.0446429,-0.857143,0.0178571
0.0446429,-0.928571,0.0107143
0.00892857,-0.928571,0.0196429
-0.00892857,-0.928571,0.0196429
-0.0446429,-0.928571,0.0107143
0.0539286,-0.999643,0.0
0.000357143,-0.999643,0.0
-0.000357143,-0.999643,0.0
-0.0539286,-0.999643,0.0
0.000357143,-1,0.0
0.000357143,-1,0.0
-0.000357143,-1,0.0
-0.000357143,-1,0.0
-0.0446429,-0.857143,0.0178571
-0.0535714,-0.857143,0.015625
-0.0535714,-0.857143,0.0334821
-0.0446429,-0.857143,0.0357143
-0.0446429,-0.928571,0.0107143
-0.0535714,-0.928571,0.00848214
-0.0535714,-0.928571,0.0263393
-0.0446429,-0.928571,0.0285714
-0.0539286,-0.999643,0.0
-0.0673214,-0.999643,0.0
-0.0675,-0.999643,0.0178571
-0.0539286,-0.999643,0.0178571
-0.000357143,-1,0.0
-0.000357143,-1,0.0
-0.000535714,-1,0.0178571
-0.000357143,-1,0.0178571
"""
@@ -0,0 +1,201 @@
# SPDX-FileCopyrightText: 2012-2022 Blender Foundation
#
# SPDX-License-Identifier: GPL-2.0-or-later
# Author: Anthony D'Agostino
import bpy
from mathutils import Vector
from math import sin, cos, pi
from bpy.props import (
BoolProperty,
IntProperty,
StringProperty,
)
from bpy_extras import object_utils
from .interface import draw_transform_props
def create_mesh_object(context, verts, edges, faces, name):
# Create new mesh
mesh = bpy.data.meshes.new(name)
# Make a mesh from a list of verts/edges/faces.
mesh.from_pydata(verts, edges, faces)
# Update mesh geometry after adding stuff.
mesh.update()
from bpy_extras import object_utils
return object_utils.object_data_add(context, mesh, operator=None)
# ========================
# === Torus Knot Block ===
# ========================
def k1(t):
x = cos(t) - 2 * cos(2 * t)
y = sin(t) + 2 * sin(2 * t)
z = sin(3 * t)
return Vector([x, y, z])
def k2(t):
x = 10 * (cos(t) + cos(3 * t)) + cos(2 * t) + cos(4 * t)
y = 6 * sin(t) + 10 * sin(3 * t)
z = 4 * sin(3 * t) * sin(5 * t / 2) + 4 * sin(4 * t) - 2 * sin(6 * t)
return Vector([x, y, z]) * 0.2
def k3(t):
x = 2.5 * cos(t + pi) / 3 + 2 * cos(3 * t)
y = 2.5 * sin(t) / 3 + 2 * sin(3 * t)
z = 1.5 * sin(4 * t) + sin(2 * t) / 3
return Vector([x, y, z])
def make_verts(ures, vres, r2, knotfunc):
verts = []
for i in range(ures):
t1 = (i + 0) * 2 * pi / ures
t2 = (i + 1) * 2 * pi / ures
a = knotfunc(t1) # curr point
b = knotfunc(t2) # next point
a, b = map(Vector, (a, b))
e = a - b
f = a + b
g = e.cross(f)
h = e.cross(g)
g.normalize()
h.normalize()
for j in range(vres):
k = j * 2 * pi / vres
l = (cos(k), 0.0, sin(k))
l = Vector(l)
m = l * r2
x, y, z = m
n = h * x
o = g * z
p = n + o
q = a + p
verts.append(q)
return verts
def make_faces(ures, vres):
faces = []
for u in range(0, ures):
for v in range(0, vres):
p1 = v + u * vres
p2 = v + ((u + 1) % ures) * vres
p4 = (v + 1) % vres + u * vres
p3 = (v + 1) % vres + ((u + 1) % ures) * vres
faces.append([p4, p3, p2, p1])
return faces
def make_knot(knotidx, ures):
knots = [k1, k2, k3]
knotfunc = knots[knotidx - 1]
vres = ures // 10
r2 = 0.5
verts = make_verts(ures, vres, r2, knotfunc)
faces = make_faces(ures, vres)
return (verts, faces)
class AddTorusKnot(bpy.types.Operator, object_utils.AddObjectHelper):
bl_idname = "mesh.primitive_torusknot_add"
bl_label = "Add Torus Knot"
bl_description = "Construct a torus knot mesh"
bl_options = {"REGISTER", "UNDO"}
TorusKnot : BoolProperty(name = "TorusKnot",
default = True,
description = "TorusKnot")
change : BoolProperty(name = "Change",
default = False,
description = "change TorusKnot")
resolution: IntProperty(
name="Resolution",
description="Resolution of the Torus Knot",
default=80,
min=30, max=256
)
objecttype: IntProperty(
name="Knot Type",
description="Type of Knot",
default=1,
min=1, max=3
)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
layout.prop(self, 'resolution', expand=True)
layout.prop(self, 'objecttype', expand=True)
if self.change == False:
draw_transform_props(self, layout)
def execute(self, context):
# turn off 'Enter Edit Mode'
use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
bpy.context.preferences.edit.use_enter_edit_mode = False
if bpy.context.mode == "OBJECT":
if context.selected_objects != [] and context.active_object and \
(context.active_object.data is not None) and ('TorusKnot' in context.active_object.data.keys()) and \
(self.change == True):
obj = context.active_object
oldmesh = obj.data
oldmeshname = obj.data.name
verts, faces = make_knot(self.objecttype, self.resolution)
mesh = bpy.data.meshes.new('TorusKnot')
mesh.from_pydata(verts, [], faces)
obj.data = mesh
for material in oldmesh.materials:
obj.data.materials.append(material)
bpy.data.meshes.remove(oldmesh)
obj.data.name = oldmeshname
else:
verts, faces = make_knot(self.objecttype, self.resolution)
mesh = bpy.data.meshes.new('TorusKnot')
mesh.from_pydata(verts, [], faces)
obj = object_utils.object_data_add(context, mesh, operator=self)
obj.data["TorusKnot"] = True
obj.data["change"] = False
for prm in TorusKnotParameters():
obj.data[prm] = getattr(self, prm)
if bpy.context.mode == "EDIT_MESH":
active_object = context.active_object
name_active_object = active_object.name
bpy.ops.object.mode_set(mode='OBJECT')
verts, faces = make_knot(self.objecttype, self.resolution)
mesh = bpy.data.meshes.new('TorusKnot')
mesh.from_pydata(verts, [], faces)
obj = object_utils.object_data_add(context, mesh, operator=self)
obj.select_set(True)
active_object.select_set(True)
bpy.context.view_layer.objects.active = active_object
bpy.ops.object.join()
context.active_object.name = name_active_object
bpy.ops.object.mode_set(mode='EDIT')
if use_enter_edit_mode:
bpy.ops.object.mode_set(mode = 'EDIT')
# restore pre operator state
bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
return {'FINISHED'}
def TorusKnotParameters():
TorusKnotParameters = [
"resolution",
"objecttype",
]
return TorusKnotParameters
@@ -0,0 +1,313 @@
# SPDX-FileCopyrightText: 2017-2023 Blender Foundation
#
# SPDX-License-Identifier: GPL-2.0-or-later
"""
This script provides a triangle mesh primitive
and a toolbar menu to further specify settings
"""
import math
import bpy
from mathutils import Vector
from bpy.types import Operator
from bpy.props import (
BoolProperty,
EnumProperty,
FloatProperty,
)
from bpy_extras import object_utils
from .interface import draw_transform_props
def checkEditMode():
# Check if we are in edit mode
# Returns: 1 if True
# 0 if False
if (bpy.context.active_object.mode == 'EDIT'):
return 1
return 0
def exitEditMode():
# Check if we are in edit mode (cuz we don't want this when creating a new Mesh)
# If we are then toggle back to object mode
# Check if there are active objects
if bpy.context.active_object is not None:
# Only the active object should be in edit mode
if (bpy.context.active_object.mode == 'EDIT'):
bpy.ops.object.editmode_toggle()
class MakeTriangle(Operator, object_utils.AddObjectHelper):
bl_idname = "mesh.make_triangle"
bl_label = "Add Triangle"
bl_description = "Construct different types of Triangle Meshes"
bl_options = {"REGISTER", "UNDO"}
nothing = 0
Ya = 0.0
Xb = 0.0
Xc = 0.0
Vertices = []
Faces = []
triangleTypeList = [
('ISOSCELES', "Isosceles", "Two equal sides", 0),
('EQUILATERAL', "Equilateral", "Three equal sides and angles (60°)", 1),
('ISOSCELESRIGHTANGLE', "Isosceles right angled", "90° angle and two equal sides", 2),
('SCALENERIGHTANGLE', "Scalene right angled", "90° angle, no equal sides", 3)
]
triangleFaceList = [
('DEFAULT', "Triangle", "1 Triangle face", 0),
('TRIANGLES', "3 Triangles", "4 Vertices & 3 Triangle faces", 1),
('QUADS', "3 Quads", "7 Vertices & 3 Quad faces", 2),
('SAFEQUADS', "6 Quads", "12 Vertices & 6 Quad faces", 3)
]
# add definitions for some manipulation buttons
flipX: BoolProperty(
name="Flip X sign",
description="Draw on the other side of the X axis (Mirror on Y axis)",
default=False
)
flipY: BoolProperty(
name="Flip Y sign",
description="Draw on the other side of the Y axis (Mirror on X axis)",
default=False
)
scale: FloatProperty(
name="Scale",
description="Triangle scale",
default=1.0,
min=1.0
)
triangleType: EnumProperty(
items=triangleTypeList,
name="Type",
description="Triangle Type"
)
triangleFace: EnumProperty(
items=triangleFaceList,
name="Face Types",
description="Triangle Face Types"
)
at_3Dcursor: BoolProperty(
name="Use 3D Cursor",
description="Draw the triangle where the 3D cursor is",
default=True
)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
col = layout.column()
col.prop(self, "triangleType", text="Type")
col.prop(self, "triangleFace", text="Fill Type")
col.prop(self, "scale")
col.separator()
row = col.row(heading='At')
row.prop(self, "at_3Dcursor", text="3D Cursor")
col.separator()
row = col.row(heading='Flip')
row.prop(self, "flipX", text='X')
col.prop(self, "flipY", text='Y')
col.separator()
draw_transform_props(self, col)
def drawBasicTriangleShape(self):
# set everything to 0
Xb = Xc = 0.0
Ya = 0.0
scale = self.scale
Xsign = -1 if self.flipX else 1
Ysign = -1 if self.flipY else 1
# Isosceles (2 equal sides)
if (self.triangleType == 'ISOSCELES'):
# below a simple triangle containing 2 triangles with 1:2 side ratio
Ya = (1 * Ysign * scale)
A = Vector([0.0, Ya, 0.0])
Xb = (0.5 * Xsign * scale)
B = Vector([Xb, 0.0, 0.0])
Xc = (-0.5 * Xsign * scale)
C = Vector([Xc, 0.0, 0.0])
self.Ya = Ya
self.Xb = Xb
self.Xc = Xc
self.Vertices = [A, B, C, ]
return True
# Equilateral (all sides equal)
if (self.triangleType == 'EQUILATERAL'):
Ya = (math.sqrt(0.75) * Ysign * scale)
A = Vector([0.0, Ya, 0.0])
Xb = (0.5 * Xsign * scale)
B = Vector([Xb, 0.0, 0.0])
Xc = (-0.5 * Xsign * scale)
C = Vector([Xc, 0.0, 0.0])
self.Ya = Ya
self.Xb = Xb
self.Xc = Xc
self.Vertices = [A, B, C, ]
return True
# Isosceles right angled (1, 1, sqrt(2))
if (self.triangleType == 'ISOSCELESRIGHTANGLE'):
Ya = (1 * Ysign * scale)
A = Vector([0.0, Ya, 0.0])
Xb = 0.0
B = Vector([Xb, 0.0, 0.0])
Xc = (1 * Xsign * scale)
C = Vector([Xc, 0.0, 0.0])
self.Ya = Ya
self.Xb = Xb
self.Xc = Xc
self.Vertices = [A, B, C, ]
return True
# Scalene right angled (3, 4, 5)
if (self.triangleType == 'SCALENERIGHTANGLE'):
Ya = (1 * Ysign * scale)
A = Vector([0.0, Ya, 0.0])
Xb = 0
B = Vector([Xb, 0.0, 0.0])
Xc = (0.75 * Xsign * scale)
C = Vector([Xc, 0.0, 0.0])
self.Ya = Ya
self.Xb = Xb
self.Xc = Xc
self.Vertices = [A, B, C, ]
return True
return False
def addFaces(self, fType=None):
Ya = self.Ya
Xb = self.Xb
Xc = self.Xc
if (self.triangleFace == 'DEFAULT'):
self.Faces = [[0, 1, 2]]
return True
if (self.triangleFace == 'TRIANGLES'):
A = Vector([0.0, Ya, 0.0])
B = Vector([Xb, 0.0, 0.0])
C = Vector([Xc, 0.0, 0.0])
D = Vector([((A.x + B.x + C.x) / 3), ((A.y + B.y + C.y) / 3), ((A.z + B.z + C.z) / 3)])
self.Vertices = [A, B, C, D, ]
self.Faces = [[0, 1, 3], [1, 2, 3], [2, 0, 3]]
return True
if (self.triangleFace == 'QUADS'):
A = Vector([0.0, Ya, 0.0])
B = Vector([Xb, 0.0, 0.0])
C = Vector([Xc, 0.0, 0.0])
D = Vector([((A.x + B.x + C.x) / 3), ((A.y + B.y + C.y) / 3), ((A.z + B.z + C.z) / 3)])
AB = A.lerp(B, 0.5)
AC = A.lerp(C, 0.5)
BC = B.lerp(C, 0.5)
self.Vertices = [A, AB, B, BC, C, AC, D, ]
self.Faces = [[0, 1, 6, 5], [1, 2, 3, 6], [3, 4, 5, 6]]
return True
if (self.triangleFace == 'SAFEQUADS'):
A = Vector([0.0, Ya, 0.0])
B = Vector([Xb, 0.0, 0.0])
C = Vector([Xc, 0.0, 0.0])
D = Vector([((A.x + B.x + C.x) / 3), ((A.y + B.y + C.y) / 3), ((A.z + B.z + C.z) / 3)])
E = A.lerp(D, 0.5)
AB = A.lerp(B, 0.5)
AC = A.lerp(C, 0.5)
BC = B.lerp(C, 0.5)
AAB = AB.lerp(A, 0.5)
AAC = AC.lerp(A, 0.5)
BBA = AB.lerp(B, 0.5)
BBC = BC.lerp(B, 0.5)
BCC = BC.lerp(C, 0.5)
CCA = AC.lerp(C, 0.5)
self.Vertices = [A, AAB, BBA, B, BBC, BC, BCC, C, CCA, AAC, D, E, ]
self.Faces = [[0, 1, 11, 9], [1, 2, 10, 11], [2, 3, 4, 10],
[4, 5, 6, 10], [6, 7, 8, 10], [8, 9, 11, 10]]
return True
return False
def action_common(self, context):
# definitions:
# a triangle consists of 3 points: A, B, C
# a 'safer' subdividable triangle consists of 4 points: A, B, C, D
# a subdivide friendly triangle consists of 7 points: A, B, C, D, AB, AC, BC
# a truly subdivide friendly triangle consists of (3 x 4 = )12 points:
# A, B, C, D, E, BC, AAB, AAC, BBA, BBC, BCC, CCA
BasicShapeCreated = False
ShapeFacesCreated = False
go = 0
#
# call the functions for creating the triangles and test if successful
#
BasicShapeCreated = self.drawBasicTriangleShape()
if (BasicShapeCreated):
ShapeFacesCreated = self.addFaces()
if ShapeFacesCreated:
go = 1
if (go == 1):
NewMesh = bpy.data.meshes.new("Triangle")
NewMesh.from_pydata(self.Vertices, [], self.Faces)
NewMesh.update()
NewObj = bpy.data.objects.new("Triangle", NewMesh)
context.collection.objects.link(NewObj)
# before doing the deselect make sure edit mode isn't active
exitEditMode()
bpy.ops.object.select_all(action="DESELECT")
NewObj.select_set(True)
context.view_layer.objects.active = NewObj
if self.at_3Dcursor is True:
# we'll need to be sure there is actually an object selected
if NewObj.select_get() is True:
# we also have to check if we're considered to be in 3D View (view3d)
if bpy.ops.view3d.snap_selected_to_cursor.poll() is True:
bpy.ops.view3d.snap_selected_to_cursor()
else:
# as we weren't considered to be in 3D View
# the object couldn't be moved to the 3D cursor
# so to avoid confusion we change the at_3Dcursor boolean to false
self.at_3Dcursor = False
else:
self.report({'WARNING'},
"Triangle could not be completed. (See Console for more Info)")
print("\n[Add Mesh Extra Objects]\n\nModule: add_mesh_triangle")
print("Triangle type: %s\n" % self.triangleType,
"Face type: %s\n" % self.triangleFace,
"Ya: %s, Xb: %s, Xc: %s\n" % (self.Ya, self.Xb, self.Xc),
"Vertices: %s\n" % self.Vertices,
"Faces: %s\n" % self.Faces)
def execute(self, context):
self.action_common(context)
return {"FINISHED"}
def invoke(self, context, event):
self.action_common(context)
return {"FINISHED"}
@@ -0,0 +1,327 @@
# SPDX-FileCopyrightText: 2010-2022 Blender Foundation
#
# SPDX-License-Identifier: GPL-2.0-or-later
# Author: Paulo_Gomes
import bpy
from mathutils import Quaternion, Vector
from math import cos, sin, pi
from bpy.props import (
FloatProperty,
IntProperty,
BoolProperty,
EnumProperty,
)
from bpy_extras import object_utils
from .interface import draw_transform_props
# Create a new mesh (object) from verts/edges/faces
# verts/edges/faces ... List of vertices/edges/faces for the
# new mesh (as used in from_pydata)
# name ... Name of the new mesh (& object)
def create_mesh_object(context, verts, edges, faces, name):
# Create new mesh
mesh = bpy.data.meshes.new(name)
# Make a mesh from a list of verts/edges/faces
mesh.from_pydata(verts, edges, faces)
# Update mesh geometry after adding stuff
mesh.update()
from bpy_extras import object_utils
return object_utils.object_data_add(context, mesh, operator=None)
# A very simple "bridge" tool
def createFaces(vertIdx1, vertIdx2, closed=False, flipped=False):
faces = []
if not vertIdx1 or not vertIdx2:
return None
if len(vertIdx1) < 2 and len(vertIdx2) < 2:
return None
fan = False
if (len(vertIdx1) != len(vertIdx2)):
if (len(vertIdx1) == 1 and len(vertIdx2) > 1):
fan = True
else:
return None
total = len(vertIdx2)
if closed:
# Bridge the start with the end
if flipped:
face = [
vertIdx1[0],
vertIdx2[0],
vertIdx2[total - 1]]
if not fan:
face.append(vertIdx1[total - 1])
faces.append(face)
else:
face = [vertIdx2[0], vertIdx1[0]]
if not fan:
face.append(vertIdx1[total - 1])
face.append(vertIdx2[total - 1])
faces.append(face)
# Bridge the rest of the faces
for num in range(total - 1):
if flipped:
if fan:
face = [vertIdx2[num], vertIdx1[0], vertIdx2[num + 1]]
else:
face = [vertIdx2[num], vertIdx1[num],
vertIdx1[num + 1], vertIdx2[num + 1]]
faces.append(face)
else:
if fan:
face = [vertIdx1[0], vertIdx2[num], vertIdx2[num + 1]]
else:
face = [vertIdx1[num], vertIdx2[num],
vertIdx2[num + 1], vertIdx1[num + 1]]
faces.append(face)
return faces
def add_twisted_torus(major_rad, minor_rad, major_seg, minor_seg, twists):
PI_2 = pi * 2.0
z_axis = (0.0, 0.0, 1.0)
verts = []
faces = []
edgeloop_prev = []
for major_index in range(major_seg):
quat = Quaternion(z_axis, (major_index / major_seg) * PI_2)
rot_twists = PI_2 * major_index / major_seg * twists
edgeloop = []
# Create section ring
for minor_index in range(minor_seg):
angle = (PI_2 * minor_index / minor_seg) + rot_twists
vec = Vector((
major_rad + (cos(angle) * minor_rad),
0.0,
sin(angle) * minor_rad))
vec = quat @ vec
edgeloop.append(len(verts))
verts.append(vec)
# Remember very first edgeloop
if major_index == 0:
edgeloop_first = edgeloop
# Bridge last with current ring
if edgeloop_prev:
f = createFaces(edgeloop_prev, edgeloop, closed=True)
faces.extend(f)
edgeloop_prev = edgeloop
# Bridge first and last ring
f = createFaces(edgeloop_prev, edgeloop_first, closed=True)
faces.extend(f)
return verts, faces
class AddTwistedTorus(bpy.types.Operator, object_utils.AddObjectHelper):
bl_idname = "mesh.primitive_twisted_torus_add"
bl_label = "Add Twisted Torus"
bl_description = "Construct a twisted torus mesh"
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
TwistedTorus : BoolProperty(name = "TwistedTorus",
default = True,
description = "TwistedTorus")
change : BoolProperty(name = "Change",
default = False,
description = "change TwistedTorus")
method: EnumProperty(
name='Method',
description='Method for determining the size and thickness of the torus',
items=(
('MAJOR-MINOR', 'Major / Minor', 'Uses the major radius for the overall size and the minor for the thickness'),
('INT-EXT', 'Interior / Exterior', 'Uses the absolute size of the inner and outer circles to determine the size and thickness'),
),
default='MAJOR-MINOR',
)
major_radius: FloatProperty(
name="Major Radius",
description="Radius from the origin to the"
" center of the cross section",
min=0.01,
max=100.0,
default=1.0
)
minor_radius: FloatProperty(
name="Minor Radius",
description="Radius of the torus' cross section",
min=0.01,
max=100.0,
default=0.25
)
major_segments: IntProperty(
name="Major Segments",
description="Number of segments for the main ring of the torus",
min=3,
max=256,
default=48
)
minor_segments: IntProperty(
name="Minor Segments",
description="Number of segments for the minor ring of the torus",
min=3,
max=256,
default=12
)
twists: IntProperty(
name="Twists",
description="Number of twists of the torus",
min=0,
max=256,
default=1
)
abso_major_rad: FloatProperty(
name="Exterior Radius",
description="Total Exterior Radius of the torus",
min=0.01,
max=100.0,
default=1.0
)
abso_minor_rad: FloatProperty(
name="Inside Radius",
description="Total Interior Radius of the torus",
min=0.01,
max=100.0,
default=0.5
)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
layout.separator()
layout.prop(self, 'method', text='Dimensions Mode')
col = layout.column(align=True)
if self.method == 'MAJOR-MINOR':
col.prop(self, 'major_radius', text='Radius Major')
col.prop(self, 'minor_radius', text='Minor')
else:
col.prop(self, 'abso_major_rad', text='Radius Exterior')
col.prop(self, 'abso_minor_rad', text='Interior')
col = layout.column(align=True)
col.prop(self, 'major_segments', text='Segments Major')
col.prop(self, 'minor_segments', text='Minor')
layout.prop(self, 'twists', expand=True)
if self.change == False:
draw_transform_props(self, layout)
def execute(self, context):
# turn off 'Enter Edit Mode'
use_enter_edit_mode = bpy.context.preferences.edit.use_enter_edit_mode
bpy.context.preferences.edit.use_enter_edit_mode = False
if self.method == 'INT-EXT':
extra_helper = (self.abso_major_rad - self.abso_minor_rad) * 0.5
self.major_radius = self.abso_minor_rad + extra_helper
self.minor_radius = extra_helper
if bpy.context.mode == "OBJECT":
if context.selected_objects != [] and context.active_object and \
(context.active_object.data is not None) and ('TwistedTorus' in context.active_object.data.keys()) and \
(self.change == True):
obj = context.active_object
oldmesh = obj.data
oldmeshname = obj.data.name
verts, faces = add_twisted_torus(
self.major_radius,
self.minor_radius,
self.major_segments,
self.minor_segments,
self.twists
)
mesh = bpy.data.meshes.new('TwistedTorus')
mesh.from_pydata(verts, [], faces)
obj.data = mesh
for material in oldmesh.materials:
obj.data.materials.append(material)
bpy.data.meshes.remove(oldmesh)
obj.data.name = oldmeshname
else:
verts, faces = add_twisted_torus(
self.major_radius,
self.minor_radius,
self.major_segments,
self.minor_segments,
self.twists
)
mesh = bpy.data.meshes.new('TwistedTorus')
mesh.from_pydata(verts, [], faces)
obj = object_utils.object_data_add(context, mesh, operator=self)
obj.data["TwistedTorus"] = True
obj.data["change"] = False
for prm in TwistedTorusParameters():
obj.data[prm] = getattr(self, prm)
if bpy.context.mode == "EDIT_MESH":
active_object = context.active_object
name_active_object = active_object.name
bpy.ops.object.mode_set(mode='OBJECT')
verts, faces = add_twisted_torus(
self.major_radius,
self.minor_radius,
self.major_segments,
self.minor_segments,
self.twists
)
mesh = bpy.data.meshes.new('TwistedTorus')
mesh.from_pydata(verts, [], faces)
obj = object_utils.object_data_add(context, mesh, operator=self)
obj.select_set(True)
active_object.select_set(True)
bpy.context.view_layer.objects.active = active_object
bpy.ops.object.join()
context.active_object.name = name_active_object
bpy.ops.object.mode_set(mode='EDIT')
if use_enter_edit_mode:
bpy.ops.object.mode_set(mode = 'EDIT')
# restore pre operator state
bpy.context.preferences.edit.use_enter_edit_mode = use_enter_edit_mode
return {'FINISHED'}
def TwistedTorusParameters():
TwistedTorusParameters = [
"major_radius",
"minor_radius",
"major_segments",
"minor_segments",
"twists",
"method",
"abso_major_rad",
"abso_minor_rad",
]
return TwistedTorusParameters
@@ -0,0 +1,144 @@
# SPDX-FileCopyrightText: 2015-2023 Blender Foundation
#
# SPDX-License-Identifier: GPL-2.0-or-later
# Originals by meta-androcto, Pablo Vazquez, Liero, Richard Wilks
import bpy
from bpy.types import Operator
from bpy_extras import object_utils
def object_origin(width, height, depth):
"""
This function takes inputs and returns vertex and face arrays.
no actual mesh data creation is done here.
"""
verts = [(+0.0, +0.0, +0.0)]
faces = []
# apply size
for i, v in enumerate(verts):
verts[i] = v[0] * width, v[1] * depth, v[2] * height
return verts, faces
class AddVert(Operator):
bl_idname = "mesh.primitive_vert_add"
bl_label = "Single Vert"
bl_description = "Add a Single Vertice to Edit Mode"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
mesh = bpy.data.meshes.new("Vert")
mesh.vertices.add(1)
object_utils.object_data_add(context, mesh, operator=None)
bpy.ops.object.mode_set(mode='EDIT')
return {'FINISHED'}
class AddEmptyVert(Operator):
bl_idname = "mesh.primitive_emptyvert_add"
bl_label = "Empty Object Origin"
bl_description = "Add an Object Origin to Edit Mode"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
mesh = bpy.data.meshes.new("Vert")
mesh.vertices.add(1)
object_utils.object_data_add(context, mesh, operator=None)
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.delete(type='VERT')
return {'FINISHED'}
def Add_Symmetrical_Empty():
bpy.ops.mesh.primitive_plane_add(enter_editmode=True)
sempty = bpy.context.object
sempty.name = "SymmEmpty"
# check if we have a mirror modifier, otherwise add
if not any(mod.type == 'MIRROR' for mod in sempty.modifiers):
bpy.ops.object.modifier_add(type='MIRROR')
# Delete all!
bpy.ops.mesh.select_all(action='TOGGLE')
bpy.ops.mesh.select_all(action='TOGGLE')
bpy.ops.mesh.delete(type='VERT')
def Add_Symmetrical_Vert():
bpy.ops.mesh.primitive_plane_add(enter_editmode=True)
sempty = bpy.context.object
sempty.name = "SymmVert"
# check if we have a mirror modifier, otherwise add
if not any(mod.type == 'MIRROR' for mod in sempty.modifiers):
bpy.ops.object.modifier_add(type='MIRROR')
# Delete all!
bpy.ops.mesh.select_all(action='TOGGLE')
bpy.ops.mesh.select_all(action='TOGGLE')
bpy.ops.mesh.merge(type='CENTER')
class AddSymmetricalEmpty(Operator):
bl_idname = "mesh.primitive_symmetrical_empty_add"
bl_label = "Add Symmetrical Object Origin"
bl_description = "Object Origin with a Mirror Modifier for symmetrical modeling"
bl_options = {'UNDO'}
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
mirror = next(mod for mod in bpy.context.object.modifiers
if mod.type == 'MIRROR')
layout.prop(mirror, "use_clip", text="Use Clipping")
layout.label(text="Mirror Axis")
col = layout.column(align=True)
col.prop(mirror, "use_axis")
col.prop(mirror, "use_axis")
col.prop(mirror, "use_axis")
def execute(self, context):
Add_Symmetrical_Empty()
return {'FINISHED'}
class AddSymmetricalVert(Operator):
bl_idname = "mesh.primitive_symmetrical_vert_add"
bl_label = "Add Symmetrical Origin & Vert"
bl_description = "Object Origin with a Mirror Modifier for symmetrical modeling"
bl_options = {'UNDO'}
def draw(self, context):
layout = self.layout
mirror = next(mod for mod in bpy.context.object.modifiers
if mod.type == 'MIRROR')
layout.prop(mirror, "use_clip", text="Use Clipping")
layout.label(text="Mirror Axis")
col = layout.column(align=True)
col.prop(mirror, "use_axis")
col.prop(mirror, "use_axis")
col.prop(mirror, "use_axis")
def execute(self, context):
Add_Symmetrical_Vert()
return {'FINISHED'}
@@ -0,0 +1,12 @@
schema_version = "1.0.0"
id = "extra_mesh_objects"
name = "Extra Mesh Objects"
version = "0.4.0"
tagline = "Add extra mesh object types"
maintainer = "Community"
type = "add-on"
tags = ["Add Mesh"]
blender_version_min = "4.2.0"
license = ["SPDX:GPL-3.0-or-later"]
website = "https://projects.blender.org/extensions/add_mesh_extra_objects"
copyright = ["2024 Multiple Authors"]
@@ -0,0 +1,9 @@
import bpy
def draw_transform_props(self, layout):
if hasattr(self, 'align'):
layout.prop(self, 'align', expand=False)
if hasattr(self, 'location'):
layout.prop(self, 'location', expand=True)
if hasattr(self, 'rotation'):
layout.prop(self, 'rotation', expand=True)
@@ -0,0 +1,58 @@
import bpy
class AddMeshExtraObjectsPreferences(bpy.types.AddonPreferences):
bl_idname = __package__
show_round_cube: bpy.props.BoolProperty(
name = "Round Cube",
default = True,
)
show_single_vert: bpy.props.BoolProperty(
name = "Single Vert Menu",
default = True,
)
show_torus_objects: bpy.props.BoolProperty(
name = "Torus Objects Menu",
default = True,
)
show_math_functions: bpy.props.BoolProperty(
name = "Math Functions Menu",
default = True,
)
show_gears: bpy.props.BoolProperty(
name = "Gears Menu",
default = True,
)
show_pipe_joints: bpy.props.BoolProperty(
name = "Pipe Joints Menu",
default = True,
)
show_gemstones: bpy.props.BoolProperty(
name = "Gemstones Menu",
default = True,
)
show_extras: bpy.props.BoolProperty(
name = "Extras Menu",
default = True,
)
show_parent_to_empty: bpy.props.BoolProperty(
name = "Parent to Empty",
default = True,
)
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
col = layout.column(heading="Filter Add Menu Items")
col.prop(self, "show_round_cube")
col.prop(self, "show_single_vert")
col.prop(self, "show_torus_objects")
col.prop(self, "show_math_functions")
col.prop(self, "show_gears")
col.prop(self, "show_pipe_joints")
col.prop(self, "show_gemstones")
col.prop(self, "show_extras")
col.prop(self, "show_parent_to_empty")
@@ -0,0 +1,8 @@
import bpy
op = bpy.context.active_operator
op.radius = 0.5
op.arc_div = 8
op.lin_div = 0
op.size = (0.0, 0.0, 3.0)
op.div_type = 'CORNERS'
@@ -0,0 +1,8 @@
import bpy
op = bpy.context.active_operator
op.radius = 0.4
op.arc_div = 8
op.lin_div = 0
op.size = (1.5, 3.0, 1.0)
op.div_type = 'ALL'
@@ -0,0 +1,9 @@
import bpy
op = bpy.context.active_operator
op.radius = 0.0
op.arc_div = 1
op.lin_div = 0
op.size = (2.0, 2.0, 2.0)
op.div_type = 'CORNERS'
op.odd_axis_align = False
@@ -0,0 +1,7 @@
import bpy
op = bpy.context.active_operator
op.radius = 0
op.size = (2, 2, 2)
op.lin_div = 5
op.div_type = 'ALL'
@@ -0,0 +1,9 @@
import bpy
op = bpy.context.active_operator
op.radius = 1.0
op.arc_div = 1
op.lin_div = 0
op.size = (0.0, 0.0, 0.0)
op.div_type = 'CORNERS'
op.odd_axis_align = True
@@ -0,0 +1,8 @@
import bpy
op = bpy.context.active_operator
op.radius = 1.0
op.arc_div = 8
op.lin_div = 0
op.size = (0.0, 0.0, 0.0)
op.div_type = 'CORNERS'
@@ -0,0 +1,8 @@
import bpy
op = bpy.context.active_operator
op.radius = 0.25
op.arc_div = 8
op.lin_div = 0
op.size = (2.0, 2.0, 2.0)
op.div_type = 'CORNERS'
@@ -0,0 +1,8 @@
import bpy
op = bpy.context.active_operator
op.source = "4"
op.vTrunc = 1
op.eTrunc = 1
op.dual = 0
op.snub = "None"
@@ -0,0 +1,8 @@
import bpy
op = bpy.context.active_operator
op.source = "12"
op.vTrunc = 1.1338
op.eTrunc = 1
op.dual = 1
op.snub = "None"
@@ -0,0 +1,8 @@
import bpy
op = bpy.context.active_operator
op.source = "6"
op.vTrunc = 1.0938
op.eTrunc = 1
op.dual = 1
op.snub = "None"
@@ -0,0 +1,8 @@
import bpy
op = bpy.context.active_operator
op.source = "6"
op.vTrunc = 1.0572
op.eTrunc = 0.585786
op.dual = 1
op.snub = "None"
@@ -0,0 +1,8 @@
import bpy
op = bpy.context.active_operator
op.source = "20"
op.vTrunc = 0.921
op.eTrunc = 0.553
op.dual = 1
op.snub = "None"
@@ -0,0 +1,8 @@
import bpy
op = bpy.context.active_operator
op.source = "12"
op.vTrunc = 1
op.eTrunc = 0
op.dual = 0
op.snub = "None"
@@ -0,0 +1,8 @@
import bpy
op = bpy.context.active_operator
op.source = "12"
op.vTrunc = 1.1235
op.eTrunc = 0.68
op.dual = 1
op.snub = "Left"
@@ -0,0 +1,8 @@
import bpy
op = bpy.context.active_operator
op.source = "6"
op.vTrunc = 1.0875
op.eTrunc = 0.704
op.dual = 1
op.snub = "Left"
@@ -0,0 +1,8 @@
import bpy
op = bpy.context.active_operator
op.source = "20"
op.vTrunc = 2 / 3
op.eTrunc = 0
op.dual = 1
op.snub = "None"
@@ -0,0 +1,8 @@
import bpy
op = bpy.context.active_operator
op.source = "4"
op.vTrunc = 1
op.eTrunc = 1
op.dual = 1
op.snub = "None"
@@ -0,0 +1,8 @@
import bpy
op = bpy.context.active_operator
op.source = "12"
op.vTrunc = 1
op.eTrunc = 0
op.dual = 1
op.snub = "None"
@@ -0,0 +1,8 @@
import bpy
op = bpy.context.active_operator
op.source = "12"
op.vTrunc = 1.1338
op.eTrunc = 1
op.dual = 0
op.snub = "None"
@@ -0,0 +1,8 @@
import bpy
op = bpy.context.active_operator
op.source = "6"
op.vTrunc = 1.0938
op.eTrunc = 1
op.dual = 0
op.snub = "None"
@@ -0,0 +1,8 @@
import bpy
op = bpy.context.active_operator
op.source = "6"
op.vTrunc = 1.0875
op.eTrunc = 0.704
op.dual = 0
op.snub = "Left"
@@ -0,0 +1,8 @@
import bpy
op = bpy.context.active_operator
op.source = "12"
op.vTrunc = 1.1235
op.eTrunc = 0.68
op.dual = 0
op.snub = "Left"
@@ -0,0 +1,8 @@
import bpy
op = bpy.context.active_operator
op.source = "8"
op.vTrunc = 2 / 3
op.eTrunc = 0
op.dual = 1
op.snub = "None"
@@ -0,0 +1,8 @@
import bpy
op = bpy.context.active_operator
op.source = "12"
op.vTrunc = 2 / 3
op.eTrunc = 0
op.dual = 1
op.snub = "None"
@@ -0,0 +1,8 @@
import bpy
op = bpy.context.active_operator
op.source = "6"
op.vTrunc = 2/3
op.eTrunc = 0
op.dual = 1
op.snub = "None"
@@ -0,0 +1,8 @@
import bpy
op = bpy.context.active_operator
op.source = "4"
op.vTrunc = 2 / 3
op.eTrunc = 0
op.dual = 1
op.snub = "None"
@@ -0,0 +1,8 @@
import bpy
op = bpy.context.active_operator
op.source = "6"
op.vTrunc = 2/3
op.eTrunc = 0
op.dual = 0
op.snub = "None"
@@ -0,0 +1,8 @@
import bpy
op = bpy.context.active_operator
op.source = "6"
op.vTrunc = 1.0572
op.eTrunc = 0.585786
op.dual = 0
op.snub = "None"
@@ -0,0 +1,8 @@
import bpy
op = bpy.context.active_operator
op.source = "12"
op.vTrunc = 2 / 3
op.eTrunc = 0
op.dual = 0
op.snub = "None"
@@ -0,0 +1,8 @@
import bpy
op = bpy.context.active_operator
op.source = "20"
op.vTrunc = 2 / 3
op.eTrunc = 0
op.dual = 0
op.snub = "None"
@@ -0,0 +1,8 @@
import bpy
op = bpy.context.active_operator
op.source = "20"
op.vTrunc = 0.921
op.eTrunc = 0.553
op.dual = 0
op.snub = "None"
@@ -0,0 +1,8 @@
import bpy
op = bpy.context.active_operator
op.source = "8"
op.vTrunc = 2/3
op.eTrunc = 0
op.dual = 0
op.snub = "None"
@@ -0,0 +1,8 @@
import bpy
op = bpy.context.active_operator
op.source = "4"
op.vTrunc = 2/3
op.eTrunc = 0
op.dual = 0
op.snub = "None"
@@ -0,0 +1,22 @@
import bpy
op = bpy.context.active_operator
op.x_eq = 'u'
op.y_eq = 'cos(u)*sin(v)'
op.z_eq = 'cos(u)*cos(v)'
op.range_u_min = 0.0
op.range_u_max = 6.2831854820251465
op.range_u_step = 32
op.wrap_u = False
op.range_v_min = 0.0
op.range_v_max = 6.2831854820251465
op.range_v_step = 128
op.wrap_v = False
op.close_v = False
op.n_eq = 1
op.a_eq = '0'
op.b_eq = '0'
op.c_eq = '0'
op.f_eq = '0'
op.g_eq = '0'
op.h_eq = '0'
@@ -0,0 +1,22 @@
import bpy
op = bpy.context.active_operator
op.x_eq = '2/3* (cos(u)* cos(2*v) + sqrt(2)* sin(u)* cos(v))* cos(u) / (sqrt(2) - sin(2*u)* sin(3*v))'
op.y_eq = 'sqrt(2)* cos(u)* cos(u) / (sqrt(2) - sin(2*u)* sin(3*v))'
op.z_eq = '2/3* (cos(u)* sin(2*v) - sqrt(2)* sin(u)* sin(v))* cos(u) / (sqrt(2) - sin(2*u)* sin(3*v))'
op.range_u_min = 0.0
op.range_u_max = 3.1415927410125732
op.range_u_step = 32
op.wrap_u = False
op.range_v_min = 0.0
op.range_v_max = 3.1415927410125732
op.range_v_step = 64
op.wrap_v = False
op.close_v = False
op.n_eq = 1
op.a_eq = '0'
op.b_eq = '0'
op.c_eq = '0'
op.f_eq = '0'
op.g_eq = '0'
op.h_eq = '0'
@@ -0,0 +1,22 @@
import bpy
op = bpy.context.active_operator
op.x_eq = 'u-sin(u)*cosh(v)'
op.y_eq = '4*sin(1/2*u)*sinh(v/2)'
op.z_eq = '1-cos(u)*cosh(v)'
op.range_u_min = -3.1415927410125732
op.range_u_max = 9.42477798461914
op.range_u_step = 32
op.wrap_u = False
op.range_v_min = -2.0
op.range_v_max = 2.0
op.range_v_step = 128
op.wrap_v = False
op.close_v = False
op.n_eq = 1
op.a_eq = '0'
op.b_eq = '0'
op.c_eq = '0'
op.f_eq = '0'
op.g_eq = '0'
op.h_eq = '0'
@@ -0,0 +1,22 @@
import bpy
op = bpy.context.active_operator
op.x_eq = '2*cosh(v/2)*cos(u)'
op.y_eq = 'v'
op.z_eq = '2*cosh(v/2)*sin(u)'
op.range_u_min = -3.1415927410125732
op.range_u_max = 3.1415927410125732
op.range_u_step = 32
op.wrap_u = True
op.range_v_min = -3.1415927410125732
op.range_v_max = 3.1415927410125732
op.range_v_step = 128
op.wrap_v = False
op.close_v = False
op.n_eq = 1
op.a_eq = '0'
op.b_eq = '0'
op.c_eq = '0'
op.f_eq = '0'
op.g_eq = '0'
op.h_eq = '0'
@@ -0,0 +1,22 @@
import bpy
op = bpy.context.active_operator
op.x_eq = 'cos(u+v)/(sqrt(2.)+cos(v-u))'
op.y_eq = 'sin(v-u)/(sqrt(2.)+cos(v-u))'
op.z_eq = 'sin(u+v)/(sqrt(2.)+cos(v-u))'
op.range_u_min = 0.0
op.range_u_max = 3.140000104904175
op.range_u_step = 8
op.wrap_u = False
op.range_v_min = 0.0
op.range_v_max = 6.2831854820251465
op.range_v_step = 128
op.wrap_v = False
op.close_v = False
op.n_eq = 1
op.a_eq = '0'
op.b_eq = '0'
op.c_eq = '0'
op.f_eq = '0'
op.g_eq = '0'
op.h_eq = '0'
@@ -0,0 +1,22 @@
import bpy
op = bpy.context.active_operator
op.x_eq = 'v*cos(u)'
op.y_eq = 'v*sin(u)'
op.z_eq = '0.4*u'
op.range_u_min = 0.0
op.range_u_max = 12.566370964050293
op.range_u_step = 32
op.wrap_u = False
op.range_v_min = 0.0
op.range_v_max = 2.0
op.range_v_step = 32
op.wrap_v = False
op.close_v = False
op.n_eq = 1
op.a_eq = '0'
op.b_eq = '0'
op.c_eq = '0'
op.f_eq = '0'
op.g_eq = '0'
op.h_eq = '0'
@@ -0,0 +1,22 @@
import bpy
op = bpy.context.active_operator
op.x_eq = 'u'
op.y_eq = 'sin(pi*((u)**2+(v)**2))/2'
op.z_eq = 'v'
op.range_u_min = -1.0
op.range_u_max = 1.0
op.range_u_step = 32
op.wrap_u = False
op.range_v_min = -1.0
op.range_v_max = 1.0
op.range_v_step = 128
op.wrap_v = False
op.close_v = False
op.n_eq = 1
op.a_eq = '0'
op.b_eq = '0'
op.c_eq = '0'
op.f_eq = '0'
op.g_eq = '0'
op.h_eq = '0'
@@ -0,0 +1,22 @@
import bpy
op = bpy.context.active_operator
op.x_eq = 'a*cos(u)*sin(v)'
op.y_eq = 'a*sin(u)*sin(v)'
op.z_eq = '(cos(v)+log(tan(v/2)+1e-2)) + b*u'
op.range_u_min = 0.0
op.range_u_max = 12.566370964050293
op.range_u_step = 128
op.wrap_u = False
op.range_v_min = 0.0
op.range_v_max = 2.0
op.range_v_step = 128
op.wrap_v = False
op.close_v = False
op.n_eq = 1
op.a_eq = '1'
op.b_eq = '0.2'
op.c_eq = '0'
op.f_eq = '0'
op.g_eq = '0'
op.h_eq = '0'
@@ -0,0 +1,22 @@
import bpy
op = bpy.context.active_operator
op.x_eq = 'u -u**3/3 + u*v**2'
op.y_eq = 'u**2 - v**2'
op.z_eq = 'v -v**3/3 + v*u**2'
op.range_u_min = -2.0
op.range_u_max = 2.0
op.range_u_step = 32
op.wrap_u = False
op.range_v_min = -2.0
op.range_v_max = 2.0
op.range_v_step = 32
op.wrap_v = False
op.close_v = False
op.n_eq = 1
op.a_eq = '0'
op.b_eq = '0'
op.c_eq = '0'
op.f_eq = '0'
op.g_eq = '0'
op.h_eq = '0'
@@ -0,0 +1,22 @@
import bpy
op = bpy.context.active_operator
op.x_eq = 'sinh(v)*sin(u)'
op.y_eq = '3*u'
op.z_eq = '-sinh(v)*cos(u)'
op.range_u_min = -3.1415927410125732
op.range_u_max = 3.1415927410125732
op.range_u_step = 32
op.wrap_u = False
op.range_v_min = -3.1415927410125732
op.range_v_max = 3.1415927410125732
op.range_v_step = 32
op.wrap_v = False
op.close_v = False
op.n_eq = 1
op.a_eq = '0'
op.b_eq = '0'
op.c_eq = '0'
op.f_eq = '0'
op.g_eq = '0'
op.h_eq = '0'
@@ -0,0 +1,22 @@
import bpy
op = bpy.context.active_operator
op.x_eq = '(1-0.1*cos(v))*cos(u)'
op.y_eq = '0.1*(sin(v) + u/1.7 -10)'
op.z_eq = '(1-0.1*cos(v))*sin(u)'
op.range_u_min = 0.0
op.range_u_max = 12.566370964050293
op.range_u_step = 128
op.wrap_u = False
op.range_v_min = 0.0
op.range_v_max = 6.2831854820251465
op.range_v_step = 128
op.wrap_v = False
op.close_v = False
op.n_eq = 1
op.a_eq = '0'
op.b_eq = '0'
op.c_eq = '0'
op.f_eq = '0'
op.g_eq = '0'
op.h_eq = '0'
@@ -0,0 +1,22 @@
import bpy
op = bpy.context.active_operator
op.x_eq = 'cos(v)**3*cos(u)**3'
op.y_eq = 'sin(u)**3'
op.z_eq = 'sin(v)**3*cos(u)**3'
op.range_u_min = -1.2999999523162842
op.range_u_max = 1.2999999523162842
op.range_u_step = 32
op.wrap_u = False
op.range_v_min = 0.0
op.range_v_max = 6.2831854820251465
op.range_v_step = 32
op.wrap_v = False
op.close_v = False
op.n_eq = 1
op.a_eq = '0'
op.b_eq = '0'
op.c_eq = '0'
op.f_eq = '0'
op.g_eq = '0'
op.h_eq = '0'
@@ -0,0 +1,22 @@
import bpy
op = bpy.context.active_operator
op.x_eq = '(sinh(v)*cos(3*u))/(1+cosh(u)*cosh(v))'
op.y_eq = '(cosh(v)*sinh(u))/(1+cosh(u)*cosh(v))'
op.z_eq = '(sinh(v)*sin(3*u))/(1+cosh(u)*cosh(v))'
op.range_u_min = -3.1415927410125732
op.range_u_max = 3.1415927410125732
op.range_u_step = 32
op.wrap_u = False
op.range_v_min = -3.1415927410125732
op.range_v_max = 3.1415927410125732
op.range_v_step = 128
op.wrap_v = False
op.close_v = False
op.n_eq = 1
op.a_eq = '0'
op.b_eq = '0'
op.c_eq = '0'
op.f_eq = '0'
op.g_eq = '0'
op.h_eq = '0'
@@ -0,0 +1,22 @@
import bpy
op = bpy.context.active_operator
op.x_eq = '(3*(1+sin(v)) + 2*(1-cos(v)/2)*cos(u))*cos(v)'
op.y_eq = '(4+2*(1-cos(v)/2)*cos(u))*sin(v)'
op.z_eq = '-2*(1-cos(v)/2)*sin(u)'
op.range_u_min = 0.0
op.range_u_max = 6.2831854820251465
op.range_u_step = 32
op.wrap_u = True
op.range_v_min = 0.0
op.range_v_max = 6.2831854820251465
op.range_v_step = 128
op.wrap_v = False
op.close_v = False
op.n_eq = 1
op.a_eq = '0'
op.b_eq = '0'
op.c_eq = '0'
op.f_eq = '0'
op.g_eq = '0'
op.h_eq = '0'
@@ -0,0 +1,22 @@
import bpy
op = bpy.context.active_operator
op.x_eq = 'cos(v)+u*cos(v/2)*cos(v)'
op.y_eq = 'u*sin(v/2)'
op.z_eq = 'sin(v)+u*cos(v/2)*sin(v)'
op.range_u_min = -0.4000000059604645
op.range_u_max = 0.4000000059604645
op.range_u_step = 32
op.wrap_u = False
op.range_v_min = 0.0
op.range_v_max = 6.2831854820251465
op.range_v_step = 32
op.wrap_v = False
op.close_v = False
op.n_eq = 1
op.a_eq = '0'
op.b_eq = '0'
op.c_eq = '0'
op.f_eq = '0'
op.g_eq = '0'
op.h_eq = '0'
@@ -0,0 +1,22 @@
import bpy
op = bpy.context.active_operator
op.x_eq = '2.2*(2*cosh(v/2)*cos(u)) '
op.y_eq = '1.51166 * (2*cosh(v/2)*sin(u) * sin((2.2*(2*cosh(v/2)*cos(u)) - -11.0404)*2*pi*1/22.0513) + 1.8*(v) * cos((2.2*(2*cosh(v/2)*cos(u)) - -11.0404)*2*pi*1/22.0513)) '
op.z_eq = '1.51166 * (2*cosh(v/2)*sin(u) * cos((2.2*(2*cosh(v/2)*cos(u)) - -11.0404)*2*pi*1/22.0513) - 1.8*(v) * sin((2.2*(2*cosh(v/2)*cos(u)) - -11.0404)*2*pi*1/22.0513)) '
op.range_u_min = -3.1415927410125732
op.range_u_max = 3.1415927410125732
op.range_u_step = 32
op.wrap_u = False
op.range_v_min = -3.1415927410125732
op.range_v_max = 3.1415927410125732
op.range_v_step = 128
op.wrap_v = False
op.close_v = False
op.n_eq = 1
op.a_eq = '0'
op.b_eq = '0'
op.c_eq = '0'
op.f_eq = '0'
op.g_eq = '0'
op.h_eq = '0'
@@ -0,0 +1,22 @@
import bpy
op = bpy.context.active_operator
op.x_eq = 'cos(u)*cos(v)+sin((sin(u)+1)*2*pi) '
op.y_eq = '4*sin(u) '
op.z_eq = 'cos(u)*sin(v)+cos((sin(u)+1)*2*pi) '
op.range_u_min = -1.5707963705062866
op.range_u_max = 1.5707963705062866
op.range_u_step = 32
op.wrap_u = False
op.range_v_min = 0.0
op.range_v_max = 6.2831854820251465
op.range_v_step = 128
op.wrap_v = False
op.close_v = False
op.n_eq = 1
op.a_eq = '0'
op.b_eq = '0'
op.c_eq = '0'
op.f_eq = '0'
op.g_eq = '0'
op.h_eq = '0'
@@ -0,0 +1,22 @@
import bpy
op = bpy.context.active_operator
op.x_eq = 'a*cos(u)+(b*sin(f*u)+c)*cos(u)*cos(v)'
op.y_eq = 'a*sin(u)+(b*sin(f*u)+c)*sin(u)*cos(v)'
op.z_eq = '(b*sin(f*u)+c)*sin(v)'
op.range_u_min = 0.0
op.range_u_max = 6.2831854820251465
op.range_u_step = 128
op.wrap_u = False
op.range_v_min = 0.0
op.range_v_max = 6.2831854820251465
op.range_v_step = 32
op.wrap_v = False
op.close_v = False
op.n_eq = 1
op.a_eq = '5'
op.b_eq = '0.6'
op.c_eq = '2'
op.f_eq = '10'
op.g_eq = '0'
op.h_eq = '0'
@@ -0,0 +1,22 @@
import bpy
op = bpy.context.active_operator
op.x_eq = 'cos(v)*(1+cos(u))*sin(v/8)'
op.y_eq = 'sin(u)*sin(v/8)+cos(v/8)*1.5'
op.z_eq = 'sin(v)*(1+cos(u))*sin(v/8)'
op.range_u_min = 0.0
op.range_u_max = 6.2831854820251465
op.range_u_step = 32
op.wrap_u = True
op.range_v_min = 0.0
op.range_v_max = 12.566370964050293
op.range_v_step = 128
op.wrap_v = False
op.close_v = False
op.n_eq = 1
op.a_eq = '0'
op.b_eq = '0'
op.c_eq = '0'
op.f_eq = '0'
op.g_eq = '0'
op.h_eq = '0'
@@ -0,0 +1,22 @@
import bpy
op = bpy.context.active_operator
op.x_eq = 'sin(u)'
op.y_eq = 'sin(v)'
op.z_eq = 'sin(u+v)'
op.range_u_min = 0.0
op.range_u_max = 6.2831854820251465
op.range_u_step = 128
op.wrap_u = True
op.range_v_min = 0.0
op.range_v_max = 6.2831854820251465
op.range_v_step = 128
op.wrap_v = True
op.close_v = True
op.n_eq = 1
op.a_eq = '0'
op.b_eq = '0'
op.c_eq = '0'
op.f_eq = '0'
op.g_eq = '0'
op.h_eq = '0'
@@ -0,0 +1,22 @@
import bpy
op = bpy.context.active_operator
op.x_eq = '1.2*(1 -v/(2*pi))*cos(3*v)*(1 + cos(u)) + 3*cos(3*v)'
op.y_eq = '9*v/(2*pi) + 1.2*(1 - v/(2*pi))*sin(u)'
op.z_eq = '1.2*(1 -v/(2*pi))*sin(3*v)*(1 + cos(u)) + 3*sin(3*v)'
op.range_u_min = 0.0
op.range_u_max = 6.2831854820251465
op.range_u_step = 32
op.wrap_u = False
op.range_v_min = 0.0
op.range_v_max = 6.2831854820251465
op.range_v_step = 64
op.wrap_v = False
op.close_v = False
op.n_eq = 1
op.a_eq = '0'
op.b_eq = '0'
op.c_eq = '0'
op.f_eq = '0'
op.g_eq = '0'
op.h_eq = '0'
@@ -0,0 +1,22 @@
import bpy
op = bpy.context.active_operator
op.x_eq = '2.*u/(u*u+v*v+1.)'
op.y_eq = '(u*u+v*v-1.)/(u*u+v*v+1.)'
op.z_eq = '2.*v/(u*u+v*v+1.)'
op.range_u_min = -2.0
op.range_u_max = 2.0
op.range_u_step = 32
op.wrap_u = False
op.range_v_min = -2.0
op.range_v_max = 2.0
op.range_v_step = 32
op.wrap_v = False
op.close_v = False
op.n_eq = 1
op.a_eq = '0'
op.b_eq = '0'
op.c_eq = '0'
op.f_eq = '0'
op.g_eq = '0'
op.h_eq = '0'
@@ -0,0 +1,22 @@
import bpy
op = bpy.context.active_operator
op.x_eq = '(1+0.5*cos(u))*cos(v)'
op.y_eq = '0.5*sin(u)'
op.z_eq = '(1+0.5*cos(u))*sin(v)'
op.range_u_min = 0.0
op.range_u_max = 6.2831854820251465
op.range_u_step = 32
op.wrap_u = False
op.range_v_min = 0.0
op.range_v_max = 6.2831854820251465
op.range_v_step = 128
op.wrap_v = False
op.close_v = False
op.n_eq = 1
op.a_eq = '0'
op.b_eq = '0'
op.c_eq = '0'
op.f_eq = '0'
op.g_eq = '0'
op.h_eq = '0'
@@ -0,0 +1,22 @@
import bpy
op = bpy.context.active_operator
op.x_eq = 'cos(u)*(6-(5./4. + sin(3*v))*sin(v-3*u))'
op.y_eq = '(6-(5./4. + sin(3*v))*sin(v-3*u))*sin(u)'
op.z_eq = '-cos(v-3*u)*(5./4.+sin(3*v))'
op.range_u_min = 0.0
op.range_u_max = 6.2831854820251465
op.range_u_step = 128
op.wrap_u = True
op.range_v_min = 0.0
op.range_v_max = 6.2831854820251465
op.range_v_step = 32
op.wrap_v = True
op.close_v = True
op.n_eq = 1
op.a_eq = '0'
op.b_eq = '0'
op.c_eq = '0'
op.f_eq = '0'
op.g_eq = '0'
op.h_eq = '0'