hit test edgeId detection
This commit is contained in:
parent
e8c635c139
commit
0354391f96
|
|
@ -1,14 +1,15 @@
|
|||
import type { Face, Mesh, Solid, Surface, Vertex } from "../../types";
|
||||
import type { Edge, Face, HalfEdge, Mesh, Solid, Surface, Vertex } from "../../types";
|
||||
|
||||
export type MeshDto = {
|
||||
vertices: Float32Array;
|
||||
normals: Float32Array
|
||||
indices: Uint16Array;
|
||||
|
||||
vertexIds: Vertex['id'][];
|
||||
// vertexIds: Vertex['id'][];
|
||||
faceId: Face['id'];
|
||||
surfaceId: Surface['id'];
|
||||
solidId: Solid['id'];
|
||||
loop: { edge: Edge['id'], halfEdge: HalfEdge['id'], vertex: Vertex['id'], vertex2: Vertex['id'] }[];
|
||||
};
|
||||
|
||||
export function meshToDto(mesh: Mesh): MeshDto {
|
||||
|
|
@ -16,9 +17,10 @@ export function meshToDto(mesh: Mesh): MeshDto {
|
|||
vertices: new Float32Array(mesh.vertices.flat()),
|
||||
normals: new Float32Array(mesh.normals.flat()),
|
||||
indices: new Uint16Array(mesh.indices.flat()),
|
||||
vertexIds: mesh.vertexIds,
|
||||
// vertexIds: mesh.vertexIds,
|
||||
faceId: mesh.faceId,
|
||||
surfaceId: mesh.surfaceId,
|
||||
solidId: mesh.solidId,
|
||||
loop: mesh.loop,
|
||||
};
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import earcut from 'earcut';
|
||||
|
||||
import type { Face, Mesh, V2, V3, Vertex } from "../../types";
|
||||
import type { Edge, Face, HalfEdge, Mesh, V2, V3, Vertex } from "../../types";
|
||||
import { db } from "../db";
|
||||
|
||||
export type Basis3D = { origin: V3; u: V3; v: V3; normal: V3; };
|
||||
|
|
@ -48,7 +48,10 @@ function getNormal(vertices: V3[]): V3 {
|
|||
export class PlaneTessellator {
|
||||
public static tessellate(face: Face): Omit<Mesh, 'faceId' | 'surfaceId' | 'solidId'>[] {
|
||||
|
||||
const vertices3d = PlaneTessellator.getVerticesByLoop(face.outerLoop);
|
||||
const loop = PlaneTessellator.getLoop(face.outerLoop);
|
||||
const loopWithV2s = loop.map((v, idx) => ({ ...v, vertex2: loop[(idx + 1) % loop.length].vertex }));
|
||||
|
||||
const vertices3d = loop.map((v) => v.vertex);
|
||||
|
||||
const basis = PlaneTessellator.getBasis(vertices3d);
|
||||
const vertices2d = PlaneTessellator.projectVertices(vertices3d, basis);
|
||||
|
|
@ -60,11 +63,12 @@ export class PlaneTessellator {
|
|||
vertices: vertices3d.map((v) => [v.x, v.y, v.z]),
|
||||
normals: vertices3d.map(() => basis.normal),
|
||||
indices,
|
||||
vertexIds: vertices3d.map((v) => v.id),
|
||||
// vertexIds: vertices3d.map((v) => v.id),
|
||||
loop: loopWithV2s.map((v) => ({ halfEdge: v.halfEdge.id, edge: v.edge.id, vertex: v.vertex.id, vertex2: v.vertex2.id })),
|
||||
}];
|
||||
}
|
||||
|
||||
private static getVerticesByLoop(loopId: string): Vertex[] {
|
||||
private static getLoop(loopId: string): { halfEdge: HalfEdge, edge: Edge, vertex: Vertex }[] {
|
||||
const halfEdges = db.halfEdgesByLoop(loopId)
|
||||
.map((he) => ({
|
||||
halfedge: he,
|
||||
|
|
@ -74,7 +78,11 @@ export class PlaneTessellator {
|
|||
throw new Error(`Loop ${loopId}: only linear half-edges are supported for plane tesselation`);
|
||||
|
||||
return halfEdges
|
||||
.map((he) => db.vertexById(he.halfedge.origin)!);
|
||||
.map((halfEdge, idx) => ({
|
||||
halfEdge: halfEdge.halfedge,
|
||||
edge: halfEdge.edge,
|
||||
vertex: db.vertexById(halfEdge.halfedge.origin)!,
|
||||
}));
|
||||
}
|
||||
|
||||
public static projectVertices(vertices: Vertex[], basis: Basis3D): V2[] {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import * as THREE from 'three';
|
|||
import { CONTAINED, ExtendedTriangle, INTERSECTED, NOT_INTERSECTED } from 'three-mesh-bvh';
|
||||
import { CircularFrustum } from './circularFrustum';
|
||||
import type { Id } from '../types';
|
||||
import type { MeshDto } from '../backend/dto';
|
||||
|
||||
export type TriangleVertexHitDetail = {
|
||||
kind: 'vertex',
|
||||
|
|
@ -11,7 +12,8 @@ export type TriangleVertexHitDetail = {
|
|||
export type TriangleEdgeHitDetail = {
|
||||
kind: 'edge',
|
||||
index: 0 | 1 | 2, // edge 0=AB, 1=BC, 2=CA
|
||||
id?: Id,
|
||||
aId?: Id,
|
||||
bId?: Id,
|
||||
}
|
||||
export type TriangleFaceHitDetail = {
|
||||
kind: 'face',
|
||||
|
|
@ -113,9 +115,9 @@ function classifyTriangleHit(
|
|||
const onBC = u < BARYCENTRIC_EPSILON;
|
||||
const onCA = v < BARYCENTRIC_EPSILON;
|
||||
|
||||
if (onAB) return { kind: 'edge', index: 0, id: `${vertexIds?.[0]}-${vertexIds?.[1]}` };
|
||||
if (onBC) return { kind: 'edge', index: 1, id: `${vertexIds?.[1]}-${vertexIds?.[2]}` };
|
||||
if (onCA) return { kind: 'edge', index: 2, id: `${vertexIds?.[2]}-${vertexIds?.[0]}` };
|
||||
if (onAB) return { kind: 'edge', index: 0, aId: vertexIds?.[0], bId: vertexIds?.[1] };
|
||||
if (onBC) return { kind: 'edge', index: 1, aId: vertexIds?.[1], bId: vertexIds?.[2] };
|
||||
if (onCA) return { kind: 'edge', index: 2, aId: vertexIds?.[2], bId: vertexIds?.[0] };
|
||||
|
||||
return { kind: 'face' };
|
||||
}
|
||||
|
|
@ -380,7 +382,9 @@ export class CircularFrustumIntersection {
|
|||
}
|
||||
|
||||
private intersectionToHitResult(intersection: Intersection): HitResult[] {
|
||||
const faceId = intersection.object.userData.faceId;
|
||||
const userData = intersection.object.userData as MeshDto;
|
||||
const faceId = userData.faceId;
|
||||
const loop = userData.loop;
|
||||
|
||||
const results: HitResult[] = [{
|
||||
kind: 'face',
|
||||
|
|
@ -392,15 +396,18 @@ export class CircularFrustumIntersection {
|
|||
},
|
||||
}];
|
||||
if (intersection.triHit?.kind === 'edge') {
|
||||
results.unshift({
|
||||
kind: 'edge',
|
||||
id: intersection.triHit?.id,
|
||||
faceId,
|
||||
intersection: {
|
||||
...intersection,
|
||||
triHit: undefined,
|
||||
},
|
||||
});
|
||||
const triHit = intersection.triHit;
|
||||
const edge = loop.find((v) => (v.vertex === triHit.aId) && (v.vertex2 === triHit.bId))?.edge;
|
||||
if (edge !== undefined)
|
||||
results.unshift({
|
||||
kind: 'edge',
|
||||
id: edge,
|
||||
faceId,
|
||||
intersection: {
|
||||
...intersection,
|
||||
triHit: undefined,
|
||||
},
|
||||
});
|
||||
}
|
||||
if (intersection.triHit?.kind === 'vertex') {
|
||||
results.unshift({
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ export class SceneSync {
|
|||
side: THREE.DoubleSide,
|
||||
transparent: true,
|
||||
depthWrite: false,
|
||||
wireframe: true,
|
||||
uniforms: {
|
||||
frontColor: { value: new THREE.Color(0x88ccff) },
|
||||
frontOpacity: { value: 0.6 },
|
||||
|
|
@ -90,10 +91,8 @@ export class SceneSync {
|
|||
const geoVCount = geo.attributes.position.count;
|
||||
geo.setAttribute('selected', new THREE.BufferAttribute(new Float32Array(geoVCount), 1));
|
||||
const mesh = new THREE.Mesh(geo, this.baseMaterial);
|
||||
mesh.userData.faceId = faceId;
|
||||
mesh.userData.vertexIds = dto.vertexIds;
|
||||
mesh.userData.surfaceId = dto.surfaceId;
|
||||
mesh.userData.solidId = dto.solidId;
|
||||
mesh.userData = dto
|
||||
mesh.userData.vertexIds = dto.loop.map((v) => v.vertex);
|
||||
|
||||
this.scene.add(mesh);
|
||||
this.meshByFace[faceId] = mesh;
|
||||
|
|
|
|||
|
|
@ -54,6 +54,14 @@ export class Model {
|
|||
return this.halfEdges[id];
|
||||
}
|
||||
|
||||
public halfEdgesByFilter(filter: (halfEdge: HalfEdge) => boolean): HalfEdge[] {
|
||||
return Object.values(this.halfEdges).filter(filter);
|
||||
}
|
||||
|
||||
public halfEdgesByVertexId(id: Vertex['id']): HalfEdge[] {
|
||||
return this.halfEdgesByFilter((he) => he.origin === id);
|
||||
}
|
||||
|
||||
public halfEdgesByLoop(loopId: string): HalfEdge[] {
|
||||
const loop = this.loopById(loopId)!;
|
||||
const startHalfEdgeId = loop.start;
|
||||
|
|
@ -76,6 +84,10 @@ export class Model {
|
|||
return this.edges[id];
|
||||
}
|
||||
|
||||
public edgesByFilter(filter: (edge: Edge) => boolean): Edge[] {
|
||||
return Object.values(this.edges).filter(filter);
|
||||
}
|
||||
|
||||
public loopById(id: Loop['id']): Loop | undefined {
|
||||
return this.loops[id];
|
||||
}
|
||||
|
|
@ -91,6 +103,19 @@ export class Model {
|
|||
public solidById(id: Solid['id']): Solid | undefined {
|
||||
return this.solids[id];
|
||||
}
|
||||
|
||||
public edgesByVertexIds(a: Vertex['id'], b: Vertex['id'], belongsToFaceId?: Face['id']): Edge[] {
|
||||
let hesA = this.halfEdgesByVertexId(a);
|
||||
let hesB = this.halfEdgesByVertexId(b);
|
||||
|
||||
const edges = this.edgesByFilter((e) => (
|
||||
(e.a ? hesA.some((he) => he.id === a) : true) && (e.b ? hesB.some((he) => he.id === b) : true)
|
||||
||
|
||||
(e.a ? hesB.some((he) => he.id === a) : true) && (e.b ? hesA.some((he) => he.id === b) : true)
|
||||
));
|
||||
|
||||
return edges;
|
||||
}
|
||||
}
|
||||
|
||||
export const model = new Model();
|
||||
|
|
|
|||
|
|
@ -1,16 +1,15 @@
|
|||
import type { Face, Solid, Surface, Vertex } from "./brep";
|
||||
import type { Edge, Face, HalfEdge, Solid, Surface, Vertex } from "./brep";
|
||||
|
||||
export type V2 = [x: number, y: number];
|
||||
export type V3 = [x: number, y: number, z: number];
|
||||
// export type V3 = [number, number, number];
|
||||
|
||||
export type Mesh = {
|
||||
vertices: V3[];
|
||||
normals: V3[];
|
||||
indices: number[];
|
||||
|
||||
vertexIds: Vertex['id'][];
|
||||
faceId: Face['id'];
|
||||
surfaceId: Surface['id'];
|
||||
solidId: Solid['id'];
|
||||
loop: { edge: Edge['id'], halfEdge: HalfEdge['id'], vertex: Vertex['id'] }[];
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue