extends BrushShape
class_name ConeShape
## A cone standing on the aimed surface: an N-gon base on the surface, apex at the far
## side of the bounds along the normal (TrenchBroom BasicShapes Cone). One convex brush.
func id() -> String:
return "cone"
func params() -> Array:
return [{"id": "segments", "label": "Sides", "type": "int", "default": 12,
"min": 3, "max": 32, "step": 1, "tip": "Sides of the base. More = rounder."}]
func pieces(lo: Vector3, hi: Vector3, ctx: Dictionary) -> Array:
var vert: int = int(ctx["axis"])
var ab := CylinderShape.across(vert)
var n := clampi(int(param(ctx, "segments")), 3, 32)
if hi[ab[0]] - lo[ab[0]] < 1.0e-4 or hi[ab[1]] - lo[ab[1]] < 1.0e-4 or hi[vert] - lo[vert] < 1.0e-4:
return []
# The base sits ON the aimed surface; the apex points away from it.
var up: bool = (ctx["normal"] as Vector3)[vert] >= 0.0
var base_h: float = lo[vert] if up else hi[vert]
var apex_h: float = hi[vert] if up else lo[vert]
var ring: Array[Vector3] = []
for p: Vector2 in CylinderShape.ring2d(lo, hi, ab[0], ab[1], n):
var v := Vector3.ZERO
v[ab[0]] = p.x
v[ab[1]] = p.y
v[vert] = base_h
ring.append(v)
var apex := Vector3.ZERO
apex[ab[0]] = (lo[ab[0]] + hi[ab[0]]) * 0.5
apex[ab[1]] = (lo[ab[1]] + hi[ab[1]]) * 0.5
apex[vert] = apex_h
var piece := poly_piece(ring, apex)
return [piece] if not piece.is_empty() else []
## A pyramid over a convex `ring`, apex `apex`: the base plane + one plane per ring edge
## through the apex. Plane signs from the centroid (the ShapeKit rule: an interior
## point cannot be got backwards).
static func poly_piece(ring: Array[Vector3], apex: Vector3) -> Dictionary:
var n := ring.size()
if n < 3:
return {}
var centroid := apex
for p: Vector3 in ring:
centroid += p
centroid /= float(n + 1)
var planes := PackedFloat32Array()
var tris: Array = [[ring[0], ring[1], ring[2]]]
for i in n:
tris.append([apex, ring[i], ring[(i + 1) % n]])
for t: Array in tris:
var nn: Vector3 = ((t[1] as Vector3) - (t[0] as Vector3)).cross((t[2] as Vector3) - (t[0] as Vector3))
if nn.length() < 1.0e-9:
return {}
nn = nn.normalized()
var d: float = nn.dot(t[0])
if nn.dot(centroid) > d:
nn = -nn
d = -d
planes.append_array(PackedFloat32Array([nn.x, nn.y, nn.z, d]))
var edges := PackedVector3Array()
for i in n:
edges.append(ring[i]); edges.append(ring[(i + 1) % n])
edges.append(ring[i]); edges.append(apex)
return {"planes": planes, "edges": edges}
func describe(_lo: Vector3, _hi: Vector3, ctx: Dictionary, size_text: String) -> String:
return "cone, %d sides, %s" % [clampi(int(param(ctx, "segments")), 3, 32), size_text]