Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion barmesh/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Julian’s 3-axis tool-surface mesh. **All geometry here is Z-up** (CAD).

| File | Role |
|------|------|
| `barmesh.gd` | `Node` / `Bar` / `BuildRectBarMesh` — cutter locations `(x, y, z)` |
| `barmesh.gd` | `Node` / `Bar` / `BuildRectBarMesh` — CL `(x, y, z)`, contact normal + tri/edge/vertex ref |
| `tool_contact.gd` | Ball-nose drop along **tool axis −Z** from a point above |
| `draw.gd` | ImmediateMesh; **only** place that converts CAD Z-up → Godot Y-up `(x, z, y)` |

Expand Down
9 changes: 9 additions & 0 deletions barmesh/barmesh.gd
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,18 @@ class Partition1:

class BMNode:
## Tool pose in contact: cutter location in CAD XYZ (Z up).
enum ContactFeature { NONE, VERTEX, EDGE, FACE }

var p: Vector3
var i: int
var pointzone = null
## Unit normal at the mesh contact, CAD Z-up, pointing from part toward the tool.
var contact_normal: Vector3 = Vector3.ZERO
var contact_kind: int = ContactFeature.NONE
## Index into the CAD triangle list used for the drop.
var contact_tri: int = -1
## Vertex 0–2, edge 0–2 (a-b, b-c, c-a), unused for FACE.
var contact_elem: int = -1

func _init(p_p: Vector3, p_i: int) -> void:
p = p_p
Expand Down
18 changes: 17 additions & 1 deletion barmesh/draw.gd
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,12 @@ func _drop_last_row(bm: BarMesh, R: float, tris: Array, z_plane: float) -> void:
var start: int = bm.nodes.size() - n
for i in range(n):
var node: BarMesh.BMNode = bm.nodes[start + i]
node.p.z = Contact.drop_tool_z(node.p.x, node.p.y, R, tris, z_plane)
var hit: Dictionary = Contact.drop_tool_contact(node.p.x, node.p.y, R, tris, z_plane)
node.p.z = float(hit["z"])
node.contact_kind = int(hit["kind"])
node.contact_tri = int(hit["tri"])
node.contact_elem = int(hit["elem"])
node.contact_normal = hit["normal"]


func _draw_barmesh(bm: BarMesh) -> void:
Expand All @@ -81,5 +86,16 @@ func _draw_barmesh(bm: BarMesh) -> void:
var b: BarMesh.BMBar = bar
im.surface_add_vertex(cad_to_godot(b.nodeback.p))
im.surface_add_vertex(cad_to_godot(b.nodefore.p))
var tick: float = 0.003
for node in bm.nodes:
var nd: BarMesh.BMNode = node
if nd.contact_kind == BarMesh.BMNode.ContactFeature.NONE:
continue
if nd.contact_normal.length_squared() < 1e-12:
continue
var p0: Vector3 = nd.p
var p1: Vector3 = nd.p + nd.contact_normal * tick
im.surface_add_vertex(cad_to_godot(p0))
im.surface_add_vertex(cad_to_godot(p1))
im.surface_end()
mesh = im
87 changes: 69 additions & 18 deletions barmesh/tool_contact.gd
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,26 @@ static func mesh_triangles_cad(mesh_inst: MeshInstance3D) -> Array:


static func drop_tool_z(x: float, y: float, radius: float, tris_cad: Array, fallback_z: float) -> float:
## Cutter-location Z (sphere centre) for a vertical ball-nose at CAD (x, y).
return float(drop_tool_contact(x, y, radius, tris_cad, fallback_z)["z"])


static func drop_tool_contact(x: float, y: float, radius: float, tris_cad: Array, fallback_z: float) -> Dictionary:
## Highest ball-nose CL at CAD (x, y), plus contact feature and normal (Z-up).
var z_best: float = -1e30
var found := false
var kind := 0
var tri_i := -1
var elem := -1
var hit_pt := Vector3(x, y, fallback_z)
var R2 := radius * radius
for tri in tris_cad:
for ti in range(tris_cad.size()):
var tri: Array = tris_cad[ti]
var a: Vector3 = tri[0]
var b: Vector3 = tri[1]
var c: Vector3 = tri[2]
var verts: Array = [a, b, c]
for vi in 3:
var v: Vector3 = tri[vi]
var v: Vector3 = verts[vi]
var dx: float = x - v.x
var dy: float = y - v.y
var d2: float = dx * dx + dy * dy
Expand All @@ -74,40 +84,81 @@ static func drop_tool_z(x: float, y: float, radius: float, tris_cad: Array, fall
if z > z_best:
z_best = z
found = true
kind = 1
tri_i = ti
elem = vi
hit_pt = v
var edges: Array = [[a, b], [b, c], [c, a]]
for edge in edges:
var z_e: float = _ball_edge_z(x, y, radius, edge[0], edge[1])
if not is_nan(z_e) and z_e > z_best:
z_best = z_e
for ei in 3:
var edge: Array = edges[ei]
var hit: Dictionary = _ball_edge_hit(x, y, radius, edge[0], edge[1])
if not hit.is_empty() and float(hit["z"]) > z_best:
z_best = float(hit["z"])
found = true
kind = 2
tri_i = ti
elem = ei
hit_pt = hit["point"]
var z_f: float = _ball_face_z(x, y, radius, a, b, c)
if not is_nan(z_f) and z_f > z_best:
z_best = z_f
found = true
if found:
return z_best
return fallback_z
kind = 3
tri_i = ti
elem = -1
hit_pt = Vector3(x, y, z_f - radius)
if not found:
return {
"z": fallback_z,
"kind": 0,
"tri": -1,
"elem": -1,
"normal": Vector3(0, 0, 1),
"point": Vector3(x, y, fallback_z),
}
var cl := Vector3(x, y, z_best)
var n := (cl - hit_pt).normalized()
if kind == 3:
var tri_n: Array = tris_cad[tri_i]
n = (tri_n[1] - tri_n[0]).cross(tri_n[2] - tri_n[0]).normalized()
if n.dot(cl - tri_n[0]) < 0.0:
n = -n
elif n.length_squared() < 1e-12:
n = Vector3(0, 0, 1)
return {
"z": z_best,
"kind": kind,
"tri": tri_i,
"elem": elem,
"normal": n,
"point": hit_pt,
}


static func _ball_edge_z(x: float, y: float, R: float, p0: Vector3, p1: Vector3) -> float:
var hit := _ball_edge_hit(x, y, R, p0, p1)
if hit.is_empty():
return NAN
return float(hit["z"])


static func _ball_edge_hit(x: float, y: float, R: float, p0: Vector3, p1: Vector3) -> Dictionary:
var ex := p1.x - p0.x
var ey := p1.y - p0.y
var ez := p1.z - p0.z
var len2 := ex * ex + ey * ey
if len2 < 1e-18:
return NAN
return {}
var t := ((x - p0.x) * ex + (y - p0.y) * ey) / len2
t = clampf(t, 0.0, 1.0)
var cx: float = p0.x + ex * t
var cy: float = p0.y + ey * t
var cz: float = p0.z + ez * t
var dx: float = x - cx
var dy: float = y - cy
var pt := Vector3(p0.x + ex * t, p0.y + ey * t, p0.z + ez * t)
var dx: float = x - pt.x
var dy: float = y - pt.y
var d2: float = dx * dx + dy * dy
var R2: float = R * R
if d2 > R2:
return NAN
return cz + sqrt(R2 - d2)
return {}
return {"z": pt.z + sqrt(R2 - d2), "point": pt}


static func _ball_face_z(x: float, y: float, R: float, a: Vector3, b: Vector3, c: Vector3) -> float:
Expand Down