@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
148 lines (120 loc) • 4.08 kB
JavaScript
import { query_vertex_is_boundary } from "../query/query_vertex_is_boundary.js";
import { collapseEdge, partialEdgeCollapse } from "./collapseEdge.js";
import { computeEdgeCollapseCost } from "./computeEdgeCollapseCost.js";
export class EdgeCollapseCandidate {
constructor() {
/**
*
* @type {TopoEdge|null}
*/
this.edge = null;
/**
*
* @type {TopoVertex|null}
*/
this.victim_vertex = null;
/**
*
* @type {TopoVertex|null}
*/
this.target_vertex = null;
/**
*
* @type {number}
*/
this.cost = 0;
/**
* Debug value, used to track how many times the edge was updated
* @type {number}
*/
this.version = 0;
/**
* Collapsed victim point belonged to the border
* @type {boolean}
* @private
*/
this.__border_collapse = false;
}
/**
*
* @returns {boolean}
*/
isBorderCollapse() {
return this.__border_collapse;
}
validate() {
if (!this.edge.containsBothVertices(this.victim_vertex, this.target_vertex)) {
return false;
}
return true;
}
/**
*
* @param {TopoMesh} mesh
* @param {Map<number, Quadratic3>} vertex_quadratics
*/
collapse(mesh, vertex_quadratics) {
// if (!this.validate()) {
// throw new Error("Invalid state");
// }
const v_from = this.victim_vertex;
const v_to = this.target_vertex;
if (v_from === v_to) {
// degenerate
partialEdgeCollapse(mesh, this.edge, v_from, v_to);
} else {
if (query_vertex_is_boundary(v_from)) {
this.__border_collapse = true;
}
collapseEdge(mesh, this.edge, v_from, v_to);
// accumulate error from removed vertex to the remaining one
const q_from = vertex_quadratics.get(v_from.index);
const q_to = vertex_quadratics.get(v_to.index);
q_to.add(q_from);
}
}
/**
* Compute cost of collapse as well as which vertex should be the victim
* @param {Map<number, Quadratic3>} vertex_quadratics
* @param {Set<number>} restricted_vertices
* @returns {boolean}
*/
update(vertex_quadratics, restricted_vertices) {
this.version++;
const edge = this.edge;
const v0 = edge.v0;
const v1 = edge.v1;
const v0_restricted = restricted_vertices.has(v0.index);
const v1_restricted = restricted_vertices.has(v1.index);
if (v0_restricted) {
if (v1_restricted) {
return false;
} else {
this.victim_vertex = v1;
this.target_vertex = v0;
this.cost = computeEdgeCollapseCost(v1, v0, edge, vertex_quadratics);
}
} else {
if (v1_restricted) {
this.victim_vertex = v0;
this.target_vertex = v1;
this.cost = computeEdgeCollapseCost(v0, v1, edge, vertex_quadratics);
} else {
// unrestricted
const cost_forward = computeEdgeCollapseCost(v0, v1, edge, vertex_quadratics);
const cost_backward = computeEdgeCollapseCost(v1, v0, edge, vertex_quadratics);
// pick minimum cost collapse
if (cost_forward > cost_backward) {
this.victim_vertex = v1;
this.target_vertex = v0;
this.cost = cost_backward;
} else {
this.victim_vertex = v0;
this.target_vertex = v1;
this.cost = cost_forward;
}
}
}
return true;
}
}