Hey! I’m trying to develop a program which takes a gltf file and lets users draw 3d strokes within a shape to fill it in, and then make the resulting mesh exportable. I’m running into an issue with actually getting the stroke to fall within the mesh- I attached a picture below. Currently I’m detecting if the stroke is within the mesh by checking if at least half of projected rays intersect the mesh but I’m not sure if that’s correct. I attached the problem methods below
function getMousePosition(event) {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
}
// Check if point is inside case boundary
function isInsideCase(point) {
if (caseGeometry.length === 0) return false;
// Cast rays in multiple directions to determine if point is inside
const directions = [
new THREE.Vector3(1, 0, 0),
new THREE.Vector3(-1, 0, 0),
new THREE.Vector3(0, 1, 0),
new THREE.Vector3(0, -1, 0),
new THREE.Vector3(0, 0, 1),
new THREE.Vector3(0, 0, -1)
];
let insideCount = 0;
for (let dir of directions) {
raycaster.set(point, dir);
const intersects = raycaster.intersectObjects(caseGeometry, true);
if (intersects.length % 2 === 1) {
insideCount++;
}
}
// If majority of rays indicate inside, point is inside
return insideCount > directions.length / 2;
}
// Get intersection point with case
function getIntersectionPoint(event) {
getMousePosition(event);
raycaster.setFromCamera(mouse, camera);
// First try to intersect with case meshes
const caseIntersects = raycaster.intersectObjects(caseGeometry, true);
if (caseIntersects.length > 0) {
const point = caseIntersects[0].point;
// Offset slightly inward from the surface
const normal = caseIntersects[0].face.normal.clone();
normal.transformDirection(caseIntersects[0].object.matrixWorld);
point.add(normal.multiplyScalar(-0.02));
return point;
}
// If no case intersection, create a point at a fixed distance from camera
const direction = new THREE.Vector3();
raycaster.ray.direction.clone().normalize();
const distance = 2; // Fixed distance from camera
const point = camera.position.clone().add(raycaster.ray.direction.multiplyScalar(distance));
return point;
}
// Create tube geometry from stroke points
function createStrokeTube(points, radius = 0.2) {
if (points.length < 4) return null;
try {
const curve = new THREE.CatmullRomCurve3(points);
const tubeGeometry = new THREE.TubeGeometry(curve, points.length * 2, radius, 8, false);
const tubeMaterial = new THREE.MeshStandardMaterial({
color: 0xc0c0c0, // Silver color
metalness: 0.8,
roughness: 0.2,
});
const mesh = new THREE.Mesh(tubeGeometry, tubeMaterial);
console.log('Successfully created tube mesh'); // Debug output
return mesh;
} catch (error) {
console.error(‘Error creating tube:’, error);
// Fallback: create spheres at each point
const group = new THREE.Group();
const sphereGeometry = new THREE.SphereGeometry(radius, 8, 8);
const sphereMaterial = new THREE.MeshStandardMaterial({
color: 0xc0c0c0,
metalness: 0.8,
roughness: 0.2,
});
points.forEach(point => {
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
sphere.position.copy(point);
group.add(sphere);
});
console.log('Created fallback spheres'); // Debug output
return group;
}
}