@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
142 lines (127 loc) • 4.1 kB
text/typescript
import {Vector3} from 'three';
import {arraySum, arrayMax} from '../ArrayUtils';
import {BaseCorePoint} from '../geometry/entities/point/CorePoint';
import {isNumber} from '../Type';
const _positionSrc = new Vector3();
const _positionDest = new Vector3();
export class CoreInterpolate {
static perform(
point_dest: BaseCorePoint,
points_src: BaseCorePoint[],
attrib_name: string,
distance_threshold: number,
blend_with: number
): number {
switch (points_src.length) {
case 0:
return point_dest.attribValue(attrib_name) as number;
case 1:
return this._interpolate_with_1_point(
point_dest,
points_src[0],
attrib_name,
distance_threshold,
blend_with
);
default:
return this._interpolate_with_multiple_points(
point_dest,
points_src,
attrib_name,
distance_threshold,
blend_with
);
}
}
static _interpolate_with_1_point(
point_dest: BaseCorePoint,
point_src: BaseCorePoint,
attrib_name: string,
distance_threshold: number,
blend_with: number
): number {
point_dest.position(_positionDest);
point_src.position(_positionSrc);
const distance = _positionDest.distanceTo(_positionSrc);
const value_src = point_src.attribValue(attrib_name);
if (isNumber(value_src)) {
return this._weighted_value_from_distance(
point_dest,
value_src,
attrib_name,
distance,
distance_threshold,
blend_with
);
} else {
console.warn('value is not a number', value_src);
return 0;
}
}
static _weight_from_distance(distance: number, distance_threshold: number, blend_with: number) {
return (distance - distance_threshold) / blend_with;
}
static _weighted_value_from_distance(
point_dest: BaseCorePoint,
value_src: number,
attrib_name: string,
distance: number,
distance_threshold: number,
blend_with: number
): number {
if (distance <= distance_threshold) {
return value_src;
} else {
const value_dest = point_dest.attribValue(attrib_name);
if (isNumber(value_dest)) {
const blend = this._weight_from_distance(distance, distance_threshold, blend_with);
return blend * value_dest + (1 - blend) * value_src;
} else {
console.warn('value is not a number', value_dest);
return 0;
}
}
}
static _interpolate_with_multiple_points(
point_dest: BaseCorePoint,
points_src: BaseCorePoint[],
attrib_name: string,
distance_threshold: number,
blend_with: number
): number {
const weighted_values_src = points_src.map((point_src) => {
return this._interpolate_with_1_point(point_dest, point_src, attrib_name, distance_threshold, blend_with);
});
return arrayMax(weighted_values_src) || 0;
}
// https://math.stackexchange.com/questions/1336386/weighted-average-distance-between-3-or-more-positions
static weights(current_position: Vector3, other_positions: Vector3[]) {
switch (other_positions.length) {
case 1:
return 1;
case 2:
return this._weights_from_2(current_position, other_positions);
default:
other_positions = other_positions.slice(0, 3);
return this._weights_from_3(current_position, other_positions);
}
}
static _weights_from_2(current_position: Vector3, other_positions: Vector3[]) {
const dist_to_positions = other_positions.map((other_position) => current_position.distanceTo(other_position));
const distance_total = arraySum(dist_to_positions);
return [dist_to_positions[1] / distance_total, dist_to_positions[0] / distance_total];
}
static _weights_from_3(current_position: Vector3, other_positions: Vector3[]) {
const dist_to_positions = other_positions.map((other_position) => current_position.distanceTo(other_position));
const distance_total = arraySum([
dist_to_positions[0] * dist_to_positions[1],
dist_to_positions[0] * dist_to_positions[2],
dist_to_positions[1] * dist_to_positions[2],
]);
return [
(dist_to_positions[1] * dist_to_positions[2]) / distance_total,
(dist_to_positions[0] * dist_to_positions[2]) / distance_total,
(dist_to_positions[0] * dist_to_positions[1]) / distance_total,
];
}
}