UNPKG

@polygonjs/plugin-mapbox

Version:

Mapbox plugin for the 3D engine https://polygonjs.com

179 lines (178 loc) 8.04 kB
import { Box2, Matrix4, Vector2, Vector3, PlaneGeometry } from "three"; import mapboxgl from "mapbox-gl"; import { CoreObject } from "@polygonjs/polygonjs/dist/src/core/geometry/Object"; import { ObjectType } from "@polygonjs/polygonjs/dist/src/core/geometry/Constant"; import { CoreGeometry } from "@polygonjs/polygonjs/dist/src/core/geometry/Geometry"; import { CoreMath } from "@polygonjs/polygonjs/dist/src/core/math/_Module"; import { MapboxListenerParamConfig, MapboxListenerSopNode } from "./utils/MapboxListener"; import { CoreMapboxTransform } from "../../../core/mapbox/Transform"; const SCALE_ATTRIB_NAME = "scale"; const NORMAL_ATTRIB_NAME = "normal"; const R_MAT_MAPBOX = new Matrix4().makeRotationAxis(new Vector3(1, 0, 0), -Math.PI * 0.5); const R_MAT_WORLD = new Matrix4().makeRotationAxis(new Vector3(1, 0, 0), Math.PI * 0.5); var MapboxPlaneType = /* @__PURE__ */ ((MapboxPlaneType2) => { MapboxPlaneType2["PLANE"] = "plane"; MapboxPlaneType2["HEXAGONS"] = "hexagon"; return MapboxPlaneType2; })(MapboxPlaneType || {}); const MAPBOX_PLANE_TYPES = ["plane" /* PLANE */, "hexagon" /* HEXAGONS */]; import { NodeParamsConfig, ParamConfig } from "@polygonjs/polygonjs/dist/src/engine/nodes/utils/params/ParamsConfig"; import { MapboxPlaneHexagonsController } from "./utils/mapbox_plane/HexagonsController"; import { isBooleanTrue } from "@polygonjs/polygonjs/dist/src/core/Type"; class MapboxPlaneSopParamsConfig extends MapboxListenerParamConfig(NodeParamsConfig) { constructor() { super(...arguments); this.type = ParamConfig.INTEGER(0, { menu: { entries: MAPBOX_PLANE_TYPES.map((name, i) => { return { name, value: i }; }) } }); this.resolution = ParamConfig.INTEGER(10, { range: [1, 20], rangeLocked: [true, false] }); this.sizeMult = ParamConfig.FLOAT(1, { range: [0, 1], rangeLocked: [true, false] }); this.fullView = ParamConfig.BOOLEAN(1); this.asPoints = ParamConfig.BOOLEAN(0, { visibleIf: { type: MAPBOX_PLANE_TYPES.indexOf("plane" /* PLANE */) } }); this.mapboxTransform = ParamConfig.BOOLEAN(1); } } const ParamsConfig = new MapboxPlaneSopParamsConfig(); export class MapboxPlaneSopNode extends MapboxListenerSopNode { constructor() { super(...arguments); this.paramsConfig = ParamsConfig; this._hexagonsController = new MapboxPlaneHexagonsController(this); } static type() { return "mapboxPlane"; } cook() { this._mapboxListener.cook(); } _postInitController() { const geometry = this._buildPlane(); if (geometry) { let type = ObjectType.MESH; if (isBooleanTrue(this.pv.asPoints) || this._asHexagons()) { type = ObjectType.POINTS; } const object = this.createObject(geometry, type); const core_object = new CoreObject(object, 0); core_object.addAttribute("mapbox_sw", this.pv.southWest); core_object.addAttribute("mapbox_ne", this.pv.northEast); this.setObject(object); } } _buildPlane() { if (!this._cameraNode) { return; } const map_center = this._cameraNode.center(); if (!map_center) { this.states.error.set("map is not yet loaded"); return; } const transformer = new CoreMapboxTransform(this._cameraNode); const mapbox_center_3d = new Vector3(map_center.lng, 0, map_center.lat); transformer.transform_position_FINAL(mapbox_center_3d); const mapbox_center = new Vector2(mapbox_center_3d.x, mapbox_center_3d.z); const vertical_far_lng_lat_points = this._cameraNode.verticalFarLngLatPoints(); const vertical_near_lng_lat_points = this._cameraNode.verticalNearLngLatPoints(); const lng_lat_points = this.pv.fullView ? vertical_far_lng_lat_points : vertical_near_lng_lat_points; if (!lng_lat_points) { return; } const mirrored_near_lng_lat_points = lng_lat_points.map((p) => this._mirrorLngLat(p, map_center)); lng_lat_points.push(map_center); for (let p of mirrored_near_lng_lat_points) { lng_lat_points.push(p); } const box = new Box2(); for (let p of lng_lat_points) { box.expandByPoint(new Vector2(p.lng, p.lat)); } const mapbox_box = new Box2(); for (let p of lng_lat_points) { const pt3d = new Vector3(p.lng, 0, p.lat); transformer.transform_position_FINAL(pt3d); mapbox_box.expandByPoint(new Vector2(pt3d.x, pt3d.z)); } const mapbox_dimensions = new Vector2(); mapbox_box.getSize(mapbox_dimensions); const horizontal_lng_lat_points = this._cameraNode.horizontal_lng_lat_points(); if (!horizontal_lng_lat_points) { return; } const mapbox_horizontal_lng_lat_points = horizontal_lng_lat_points.map((p) => { const pt3d = new Vector3(p.lng, 0, p.lat); transformer.transform_position_FINAL(pt3d); return { lng: pt3d.x, lat: pt3d.z }; }); const mapbox_horizontal_distances = { lng: Math.abs(mapbox_horizontal_lng_lat_points[0].lng - mapbox_horizontal_lng_lat_points[1].lng), lat: Math.abs(mapbox_horizontal_lng_lat_points[0].lat - mapbox_horizontal_lng_lat_points[1].lat) }; const mapbox_horizontal_distance = Math.sqrt(mapbox_horizontal_distances.lng * mapbox_horizontal_distances.lng + mapbox_horizontal_distances.lat * mapbox_horizontal_distances.lat); const mapbox_segment_size = mapbox_horizontal_distance / this.pv.resolution; const segments_counts = { x: CoreMath.highest_even(this.pv.sizeMult * Math.ceil(mapbox_dimensions.x / mapbox_segment_size)), y: CoreMath.highest_even(this.pv.sizeMult * Math.ceil(mapbox_dimensions.y / mapbox_segment_size)) }; mapbox_dimensions.x = segments_counts.x * mapbox_segment_size; mapbox_dimensions.y = segments_counts.y * mapbox_segment_size; const mapbox_box_untransformed = new Box2(); const mapbox_corners = [ mapbox_center.clone().sub(mapbox_dimensions.clone().multiplyScalar(0.5)), mapbox_center.clone().sub(mapbox_dimensions.clone().multiplyScalar(-0.5)), mapbox_center.clone().add(mapbox_dimensions.clone().multiplyScalar(0.5)), mapbox_center.clone().add(mapbox_dimensions.clone().multiplyScalar(-0.5)) ]; for (let p of mapbox_corners) { const untransformed_3d = transformer.untransform_position_FINAL(new Vector3(p.x, 0, p.y)); const untransformed = new Vector2(untransformed_3d.x, untransformed_3d.z); mapbox_box_untransformed.expandByPoint(untransformed); } const world_dimensions = new Vector2(); mapbox_box_untransformed.getSize(world_dimensions); const world_plane_center = new Vector2(map_center.lng, map_center.lat); const horizontal_scale = mapbox_dimensions.x / segments_counts.x; let core_geo; const plane_dimensions = this.pv.mapboxTransform ? mapbox_dimensions : world_dimensions; const rotation_matrix = this.pv.mapboxTransform ? R_MAT_MAPBOX : R_MAT_WORLD; const geometry_center = this.pv.mapboxTransform ? mapbox_center : world_plane_center; let geometry; if (this._asHexagons()) { geometry = this._hexagonsController.geometry(plane_dimensions, segments_counts); } else { geometry = new PlaneGeometry(plane_dimensions.x, plane_dimensions.y, segments_counts.x, segments_counts.y); } geometry.applyMatrix4(rotation_matrix); geometry.translate(geometry_center.x, 0, geometry_center.y); core_geo = new CoreGeometry(geometry); const z_scale = [horizontal_scale, 1][0]; const scale = [horizontal_scale, horizontal_scale, z_scale]; core_geo.addNumericAttrib(SCALE_ATTRIB_NAME, 3, scale); core_geo.addNumericAttrib(NORMAL_ATTRIB_NAME, 3, [0, 1, 0]); return geometry; } _mirrorLngLat(p, map_center) { const delta = { lng: map_center.lng - p.lng, lat: map_center.lat - p.lat }; return new mapboxgl.LngLat(map_center.lng + delta.lng, map_center.lat + delta.lat); } _asHexagons() { return this.pv.type == MAPBOX_PLANE_TYPES.indexOf("hexagon" /* HEXAGONS */); } }