# ##### BEGIN GPL LICENSE BLOCK ##### # # Basket Arch, a Blender addon # (c) 2014,2015,2020,2025 Michel J. Anders (varkenvarken) # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # ##### END GPL LICENSE BLOCK ##### # This add-on creates a basket arch (a.k.a. three centered arch) mesh, # which is a type of arch with a specific geometry, often used in architecture. # See# https://en.wikipedia.org/wiki/Basket_arch for more information. bl_info = { "name": "Basket Arch", "author": "Michel Anders (varkenvarken)", "version": (0, 0, 20250608095121), "blender": (2, 83, 0), # verified to work with Blender 4.4 "location": "View3D > Add > Mesh", "description": "Adds a basket arch mesh", "warning": "", "wiki_url": "https://github.com/varkenvarken/blenderaddons", "tracker_url": "", "category": "Add Mesh", } from math import atan2, sin, cos, pi, sqrt import bpy, bmesh from bpy.props import FloatProperty, IntProperty from bpy_extras import object_utils def circle(x, y, r, s, e, w): co = [] # in Blender z is considered up if s <= e: while s <= e: co.append((x + r * cos(s), 0, y + r * sin(s))) s += w else: while s >= e: co.append((x + r * cos(s), 0, y + r * sin(s))) s -= w return co def calc_radii(W, H): r = 2 * H / 3.0 h = H / 3.0 w = W / 2.0 alpha = atan2(h, w) b = sqrt(w * w + h * h) s = b / sin(alpha) c = s - h R = c + H return r, R, c def R(w, h): """ calculate the y poistion of the center circle of a basket arch given half the width and the height. """ a = h / 3.0 beta = atan2(a, w) c = sqrt(a**2 + w**2) d = c / 2.0 e = d / sin(beta) return e - a def c(xA, yA, rA, xB, yB, rB): """ return the interection point(s) of two circles. """ d2 = (xB - xA) ** 2 + (yB - yA) ** 2 K = (1 / 4.0) * sqrt(((rA + rB) ** 2 - d2) * (d2 - (rA - rB) ** 2)) xb = (1 / 2.0) * (xB + xA) + (1 / 2.0) * (xB - xA) * (rA**2 - rB**2) / d2 dx = 2 * (yB - yA) * K / d2 yb = (1 / 2.0) * (yB + yA) + (1 / 2.0) * (yB - yA) * (rA**2 - rB**2) / d2 dy = -2 * (xB - xA) * K / d2 return ((xb + dx, yb + dy), (xb - dx, yb - dy)) def basket_arch(W, D, H=0, resolution=1): if (H > 0) and (W / H >= 2.0) and (W / H <= 4.0): w = 2 * H / 3.0 s = W / 2.0 - w rc = R(s, H) r = rc + H c1x = -s c1y = 0 cmx = 0 cmy = -rc c2x = s c2y = 0 CX, CY = c(c1x, c2x, w, cmx, cmy, r)[0] dx = -CX dy = CY + rc beta = atan2(dx, dy) dx = -CX - s dy = CY alpha = atan2(dx, dy) else: w = W / 4.0 h = sqrt(3.0) * w r = 3.0 * w c1x = -w c1y = 0 cmx = 0 cmy = -h c2x = w c2y = 0 # alpha = atan2(h, 2.0 * w) beta = atan2(w, h) alpha = beta co = circle(c1x, c1y, w, pi, pi / 2.0 + alpha, resolution * 3 * pi / 360) co += circle(cmx, cmy, r, pi / 2.0 + beta, pi / 2.0 - beta, resolution * pi / 360) # reversing the generation of the points in this segment will ensure these segments are symmetrical # reversing the resulting list is necessary to maintain the order of the generated polygons co += reversed( circle(c2x, c2y, w, 0.0, pi / 2.0 - alpha, resolution * 3 * pi / 360) ) n = len(co) return co + [(x, y + D, z) for x, y, z in co], [ (i, i + 1, n + i + 1, n + i) for i in range(n - 1) ] class BasketArch(bpy.types.Operator, object_utils.AddObjectHelper): bl_idname = "mesh.basketarch" bl_label = "Create a basket arch" bl_options = {"REGISTER", "UNDO"} width: FloatProperty( name="Width", description="Width of the basket arch (span)", default=4, subtype="DISTANCE", unit="LENGTH", ) height: FloatProperty( name="Height", description="Height of the basket arch (0 if classical)", default=0, subtype="DISTANCE", unit="LENGTH", ) depth: FloatProperty( name="Depth", description="Depth of the basket arch", default=1, subtype="DISTANCE", unit="LENGTH", ) resolution: IntProperty( name="Resolution", description="Higher values = less polygons", default=1, min=1 ) def execute(self, context): verts_loc, faces = basket_arch( self.width, self.depth, self.height, self.resolution ) mesh = bpy.data.meshes.new("BasketArch") bm = bmesh.new() for v_co in verts_loc: bm.verts.new(v_co) # see http://blenderartists.org/forum/archive/index.php/t-354412.html for next bit if hasattr(bm.verts, "ensure_lookup_table"): bm.verts.ensure_lookup_table() for f_idx in faces: bm.faces.new([bm.verts[i] for i in f_idx]) bm.to_mesh(mesh) mesh.update() object_utils.object_data_add(context, mesh, operator=self) return {"FINISHED"} def menu_func(self, context): self.layout.operator( BasketArch.bl_idname, text="Create a basket arch mesh", icon="PLUGIN" ) def register(): bpy.utils.register_class(BasketArch) bpy.types.VIEW3D_MT_mesh_add.append(menu_func) def unregister(): bpy.types.VIEW3D_MT_mesh_add.remove(menu_func) bpy.utils.unregister_class(BasketArch)