@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
274 lines (243 loc) • 8.93 kB
text/typescript
import {Vector3, Matrix4, LinearInterpolant, Group, Object3D, BufferGeometry, Mesh, Box3} from 'three';
import {CoreGroup} from '../../geometry/Group';
import {CoreMapboxUtils} from './Utils';
import mapboxgl from 'mapbox-gl';
import {PolyDictionary} from '../../../types/GlobalTypes';
import {LngLat} from './Common';
import {pointsFromObject} from '../../geometry/entities/point/CorePointUtils';
import { CorePoint } from '../../geometry/entities/point/CorePoint';
import { CoreObjectType } from '../../geometry/ObjectContent';
const dummyMesh = new Mesh();
const tmpBox = new Box3();
const tmpCenter = new Vector3();
const tmpSize = new Vector3();
const _position = new Vector3();
const _points:CorePoint<CoreObjectType>[]=[]
const MAT_RX = new Matrix4().makeRotationAxis(new Vector3(1, 0, 0), -Math.PI / 2);
const POSITION_ATTRIB_NAME = 'position';
const STEP_SIZE_BY_ZOOM: PolyDictionary<number> = {
1: 1802297.1652004332,
2: 901148.582600187,
3: 450574.29129994207,
4: 225287.14564998331,
5: 112643.57282498456,
6: 56321.78641249478,
7: 28160.89320639847,
8: 14080.446603198769,
9: 7040.223301600898,
10: 3520.1116506467515,
11: 1760.0558254750213,
12: 880.027912584861,
13: 440.01395644506556,
14: 220.00697807141114,
15: 110.00348918733653,
16: 55.0017445946869,
17: 27.50087214470841,
18: 13.750436073372839,
19: 6.8752180371957365,
20: 3.437609169195639,
21: 1.7188044319627807,
22: 0.8594022154866252,
23: 0.42970126142608933,
};
export class CoreMapboxTransform {
static WORLD_SCALE = 5.41843220338983e-8;
private pos_offset: [number, number] = [0, 0];
setLngLat(lngLat: LngLat) {
return (this.pos_offset = CoreMapboxUtils.fromLL(lngLat.lng, lngLat.lat));
}
// transform_geometry(geometry: BufferGeometry) {
// const max_ratio = this.geometry_bbox_ratio(geometry);
// this.transform_geometry_with_max_ratio(geometry, max_ratio);
// }
transform_group2(group: Group) {
const coreGroup = new CoreGroup();
coreGroup.setAllObjects(group.children);
coreGroup.boundingBox(tmpBox);
tmpBox.getCenter(tmpCenter);
tmpBox.getSize(tmpSize);
const new_center = CoreMapboxUtils.fromLLv(tmpCenter);
const new_min = CoreMapboxUtils.fromLLv(tmpBox.min);
const new_max = CoreMapboxUtils.fromLLv(tmpBox.max);
const new_size = new_max /*().clone()*/
.sub(new_min);
// const tr_offset = center.clone().sub(new_center)
const s_offset = tmpSize /*.clone()*/
.multiply(new_size);
s_offset.x = Math.abs(s_offset.x);
s_offset.z = Math.abs(s_offset.z);
s_offset.y = 0.5 * (s_offset.x + s_offset.z);
const mat_tr = new Matrix4();
const mat_tr_reset = new Matrix4();
const mat_s = new Matrix4();
mat_tr_reset.makeTranslation(-tmpCenter.x, -tmpCenter.y, -tmpCenter.z);
mat_tr.makeTranslation(new_center.x - this.pos_offset[0], new_center.y, new_center.z - this.pos_offset[1]);
mat_s.makeScale(s_offset.x, s_offset.y, s_offset.z);
group.traverse((object) => {
const geometry = (object as Mesh).geometry as BufferGeometry;
if (geometry) {
geometry.applyMatrix4(mat_tr_reset);
geometry.applyMatrix4(mat_s);
geometry.applyMatrix4(mat_tr);
// this.transform_geometry_with_max_ratio(geometry, max_ratio)
geometry.applyMatrix4(MAT_RX);
if (geometry.attributes.normal) {
geometry.computeVertexNormals();
}
}
});
}
transform_groupGeometry_FINAL(object: Object3D) {
this.transform_group3(object);
}
transform_groupObject_FINAL(object: Object3D) {
// this.transform_group3(object);
this.transform_position_FINAL(object.position);
object.updateMatrix();
}
transform_geometry_FINAL(geometry: BufferGeometry) {
this.transform_geometry3(geometry);
}
transform_position_FINAL(position: Vector3): Vector3 {
return this.transform_position3(position);
}
untransform_position_FINAL(position: Vector3): Vector3 {
return this.untransform_position3(position);
}
transform_group3(group: Object3D) {
// const max_ratio = this.group_bbox_ratio(group);
group.traverse((object) => {
const geometry = (object as Mesh).geometry as BufferGeometry;
if (geometry) {
this.transform_geometry_FINAL(geometry);
}
});
}
transform_group(group: Group) {
const max_ratio = this.group_bbox_ratio(group);
group.traverse((object) => {
const geometry = (object as Mesh).geometry as BufferGeometry;
if (geometry) {
this.transform_geometry_with_max_ratio(geometry, max_ratio);
}
});
}
// transform_positions(positions: Vector3[]){
// const min = new Vector3(
// ArrayUtils.min(positions.map(v=>v.x)),
// ArrayUtils.min(positions.map(v=>v.y)),
// ArrayUtils.min(positions.map(v=>v.z))
// )
// const max = new Vector3(
// ArrayUtils.max(positions.map(v=>v.x)),
// ArrayUtils.max(positions.map(v=>v.y)),
// ArrayUtils.max(positions.map(v=>v.z))
// )
// const bbox = new Box3(min, max)
// const max_ratio = this.bbox_ratio(bbox)
// positions.forEach(position=>{
// this.transform_position_with_max_ratio(position, max_ratio)
// })
// }
private transform_geometry3(geometry: BufferGeometry) {
dummyMesh.geometry = geometry;
pointsFromObject(dummyMesh,_points);
for (const point of _points) {
point.position(_position);
this.transform_position_FINAL(_position);
point.setAttribValue(POSITION_ATTRIB_NAME, _position);
}
// geometry.applyMatrix(MAT_RX);
// geometry.computeVertexNormals(); // this messes up when transforming points
}
private transform_geometry_with_max_ratio(geometry: BufferGeometry, max_ratio: number) {
dummyMesh.geometry = geometry;
pointsFromObject(dummyMesh,_points);
for (const point of _points) {
point.position(_position);
this.transform_position_with_max_ratio(_position, max_ratio);
point.setAttribValue(POSITION_ATTRIB_NAME, _position);
}
geometry.applyMatrix4(MAT_RX);
if (geometry.attributes.normal) {
geometry.computeVertexNormals();
}
}
private transform_position3(position: Vector3): Vector3 {
const lon = position.x;
const altitude = position.y;
const lat = position.z;
// const pos = Utils.fromLL(lon, lat);
const mercator_pos = mapboxgl.MercatorCoordinate.fromLngLat([lon, lat], altitude);
position.x = mercator_pos.x - this.pos_offset[0];
position.y = mercator_pos.z || 0;
// position.y *= -max_ratio;
position.z = mercator_pos.y - this.pos_offset[1];
position.divideScalar(CoreMapboxTransform.WORLD_SCALE);
return position;
}
private untransform_position3(position: Vector3): Vector3 {
position.multiplyScalar(CoreMapboxTransform.WORLD_SCALE);
const lon = position.x + this.pos_offset[0];
const altitude = position.y;
const lat = position.z + this.pos_offset[1];
// const pos = Utils.fromLL(lon, lat);
const mercator = new mapboxgl.MercatorCoordinate(lon, lat, altitude);
const lng_lat_like = mercator.toLngLat();
position.x = lng_lat_like.lng;
position.y = altitude;
// position.y *= -max_ratio;
position.z = lng_lat_like.lat;
return position;
}
private transform_position_with_max_ratio(position: Vector3, max_ratio: number) {
const lon = position.x;
const lat = position.z;
const pos = CoreMapboxUtils.fromLL(lon, lat);
position.x = pos[0] - this.pos_offset[0];
position.y *= -max_ratio;
position.z = pos[1] - this.pos_offset[1];
}
private group_bbox_ratio(group: Group): number {
const core_group = new CoreGroup();
core_group.setAllObjects(group.children);
core_group.boundingBox(tmpBox);
return this.bbox_ratio(tmpBox);
}
// private geometry_bbox_ratio(geometry: BufferGeometry): number {
// geometry.computeBoundingBox();
// return geometry.boundingBox;
// }
private bbox_ratio(bbox: Box3): number {
const new_bbox_min = CoreMapboxUtils.fromLL(bbox.min.x, bbox.min.z);
const new_bbox_max = CoreMapboxUtils.fromLL(bbox.max.x, bbox.max.z);
const new_bbox_size = [new_bbox_max[1] - new_bbox_min[1], new_bbox_max[0] - new_bbox_min[0]];
const old_bbox_size = [bbox.max.x - bbox.min.x, bbox.max.z - bbox.min.z];
const bbox_ratio = [new_bbox_size[0] / old_bbox_size[0], new_bbox_size[1] / old_bbox_size[1]];
return Math.max(bbox_ratio[0], bbox_ratio[1]);
}
//
//
// ZOOM INTERPOLANT
//
//
private static _interpolant: LinearInterpolant | undefined;
static _step_size_from_zoom_interpolant() {
return (this._interpolant = this._interpolant || this._create_step_size_from_zoom_interpolant());
}
static _create_step_size_from_zoom_interpolant() {
const positions = Object.keys(STEP_SIZE_BY_ZOOM)
.map((p) => parseFloat(p))
.sort();
const values = [];
for (const position of positions) {
values.push(STEP_SIZE_BY_ZOOM[position]);
}
const values_count = 1;
const interpolated_values = new Float32Array(values_count);
return new LinearInterpolant(positions, values, values_count, interpolated_values);
}
static step_size_from_zoom(zoom: number): number {
return this._step_size_from_zoom_interpolant().evaluate(zoom)[0];
}
}