# 拉伸实体

 `FillExtrusionsEntity` 是由 实体`Entity` 和 `FillExtrusionsModule` 模块组合而成的新的实体。

 [FillExtrusionsEntity](https://vjmap.com/map3d/api/classes/FillExtrusionsEntity.html) =  [Entity](https://vjmap.com/map3d/api/classes/Entity.html) + [FillExtrusionsModule](https://vjmap.com/map3d/api/classes/FillExtrusionsModule.html) 

 ## 常用方法

 `FillExtrusionsEntity` 是由 实体`Entity` 派生而来。具体 [Entity](https://vjmap.com/map3d/api/classes/Entity.html) 的所有属性和方法。同时也具有[FillExtrusionsModule](https://vjmap.com/map3d/api/classes/FillExtrusionsModule.html)  的相关方法。

|方法|描述|
|:---|:---|
|[getFillExtrusions](https://vjmap.com/map3d/api/classes/FillExtrusionsEntity.html#getFillExtrusions)|获取拉伸对象|
|[getBorderPolyline](https://vjmap.com/map3d/api/classes/FillExtrusionsEntity.html#getBorderPolyline)|获取拉伸边框对象|
|[setDepths](https://vjmap.com/map3d/api/classes/FillExtrusionsEntity.html#setDepths)|设置每项的拉伸后的深度、颜色、透明度值：`setDepths([{index, depth?, color2?, opacity?}])`。⚠️ 实际只有 `depth` 生效，且是顶面的**绝对**坐标值（不是相对 `baseHeight` 的高度）；`color2`/`opacity` 被写到 attribute 对象而非 `.array` 上，**无效果**；带 `baseHeight` 的项底面顶点也被当成顶面一起改，调用后会**压扁**。要改颜色/透明度用 `setData`|
|[getVertexSymbol](https://vjmap.com/map3d/api/classes/FillExtrusionsEntity.html#getVertexSymbol)|获取顶点对象|
|[isShowVertex](https://vjmap.com/map3d/api/classes/FillExtrusionsEntity.html#isShowVertex)|是否显示顶点|
|[getVertexData](https://vjmap.com/map3d/api/classes/FillExtrusionsEntity.html#getVertexData)|获取顶点数据|
|[setShowVertex](https://vjmap.com/map3d/api/classes/FillExtrusionsEntity.html#setShowVertex)|设置是否显示顶点|
|[getBorderPolyline](https://vjmap.com/map3d/api/classes/FillExtrusionsEntity.html#getBorderPolyline)|获取拉伸边框对象|
|[updateStyle](https://vjmap.com/map3d/api/classes/FillExtrusionsEntity.html#updateStyle)|**设置**样式 —— `updateStyle(style: MeshBasicMaterialParameters)`，**必须传样式参数**（不是无参"刷新"方法）|
|[updateHightStyle](https://vjmap.com/map3d/api/classes/FillExtrusionsEntity.html#updateHightStyle)|**设置**高亮样式 —— `updateHightStyle(style: MeshBasicMaterialParameters)`，同上必传参（注意官方拼写是 `Hight`）|
|[setData](https://vjmap.com/map3d/api/classes/FillExtrusionsEntity.html#setData)|修改数据|
|[getData](https://vjmap.com/map3d/api/classes/FillExtrusionsEntity.html#getData)|获取数据|
|[getItemDataByFaceIndex](https://vjmap.com/map3d/api/classes/FillExtrusionsEntity.html#getItemDataByFaceIndex)|通过faceIndex获取数据|
|[refreshHighlight](https://vjmap.com/map3d/api/classes/FillExtrusionsEntity.html#refreshHighlight)|刷新高亮数据|   
|[setItemHighlight](https://vjmap.com/map3d/api/classes/FillExtrusionsEntity.html#setItemHighlight)|设置某项高亮与否。⚠️ `setItemHighlight(id, false)` 不重绘，需随后调 `refreshHighlight()`|  
|[isItemHighlight](https://vjmap.com/map3d/api/classes/FillExtrusionsEntity.html#isItemHighlight)|获取某项高亮与否|  
|[clearHighlight](https://vjmap.com/map3d/api/classes/FillExtrusionsEntity.html#clearHighlight)|清除所有高亮|  

## 使用示例

### 创建FillExtrusionsEntity
```js
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
    container: "map", // 容器id
    scene: {
        gridHelper: { visible: true } // 是否显示坐标网格
    },
    camera: {  // 相机设置
        viewHelper: { enable: true, position: "leftBottom" } // 是否显示视角指示器
    }
})
let data = [];
data.push({
    coordinates: [[7,  3],[-4,  -2],[-5,  3]],
    color: "#f00",
    extrude: {
        depth: Math.random()  + 1,
        steps: 1,
        bevelEnabled: false
    }
})
let fillExtrusions = new vjmap3d.FillExtrusionsEntity({ //FillExtrusionsEntity
    data: data
})
fillExtrusions.addTo(app)
```

> **TS 用户注意**：
> - `coordinates` 声明为 `[number,number][]`（或带洞的 `[number,number][][]`）——普通 `number[][]` 字面量推断不过，须 `as const`、显式 `[number,number][]` 标注或 `toArray()` 返回值。
> - `.on("mouseover", e => ...)` 的事件参数 d.ts 未标类型，`strict` 下写 `e =>` 报 implicit-any，用 `(e: any) =>` 或自行收窄；`e.intersection.faceIndex` 运行时存在。
> - `updateStyle()`/`updateHightStyle()` 必传 `MeshBasicMaterialParameters`；要"刷新数据"用 `setData(getData())`，要刷高亮用 `refreshHighlight()`（无参）。
> - **`updateHightStyle({color:'#00ff00'})` 会抛 `Cannot create property 'needsUpdate' on string`**：实现是逐键 `material[k]=v` 然后 `v.needsUpdate=true`——**值必须传 THREE 对象**（`color: new THREE.Color('#00ff00')` 可用）；它作用在**高亮拉伸体**（`highlightFillExtrusions`）的材质上，不是边框线；且参数被记进 `style` 而非 `highlightStyle`。`updateStyle`（作用于 `fillExtrusions` 材质）同型实现同坑。
> - `baseHeight` 是数值（基准高度，世界单位），但 d.ts 里误标为 `boolean`——按数值传参是对的，strict 下需自行断言。
### 创建一个复杂的拉伸对象
```js
data.push({
    // 当为多个多边形时，第一个多边形后面的多边形将掏空挖洞处理
    coordinates: [[[7,  -8],[-1,  -1],[-8,  -9],[17,  -18]],  [ [-2,  -9],[2, -10],[2,  -7],[-1,  -6]]],
    color: "#FFFE91", // 底部颜色
    color2: "#0ff", // 顶部颜色
    showBorder: true, // 显示边框线
    borderColor: "#39107B",// 边框线底部颜色
    borderColor2: "#EA3FF7",// 边框线顶部颜色
    baseHeight: 1, // 基准高度
    extrude: { // 拉伸属性
        depth: Math.random() * 3  + 1,
        steps: 1,
        bevelEnabled: false
    }
})
```

### 增加事件
```js
fillExtrusions.pointerEvents = true; // 允许事件交互
fillExtrusions.on("mouseover", e => {
    if (e?.intersection?.faceIndex !== undefined) {
        let itemData = fillExtrusions.getItemDataByFaceIndex(e?.intersection?.faceIndex);
        if (itemData && itemData.id) fillExtrusions.setItemHighlight(itemData.id, true)
    }
})
fillExtrusions.on("mouseout", e => {
    fillExtrusions.clearHighlight()
})
```

### 修改、新增、删除
调用 [setData](https://vjmap.com/map3d/api/classes/FillExtrusionsEntity.html#setData) 来对数据进行增删改
```js
// 如新增一条
data.push({
    coordinates: [[7, 3],[-4, -2],[-5, 3]],
    color: "#f00",
    extrude: {
        depth: Math.random() * 1 + 1,
        steps: 1,
        bevelEnabled: false
    }
})
fillExtrusions.setData(data)
```

### 全部显示或隐藏边框线
```js
// 获取模块对象
let mod = fillExtrusions.getModule(vjmap3d.FillExtrusionsModule);
mod.showBorder = true; // 设置属性
mod.setData(mod.getData()); // 刷新下
```

### 3D示例
<mapFrame>
<pre>
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
    container: "map", // 容器id
    scene: {
        gridHelper: { visible: true } // 是否显示坐标网格
    },
    stat: {
        show: true,
        left: "0"
    },
    camera: {  // 相机设置
        viewHelper: { enable: true, position: "leftBottom" } // 是否显示视角指示器
    }
})
let data = [];
data.push({
    coordinates: [[7,  3],[-4,  -2],[-5,  3]],
    color: "#f00",
    extrude: {
        depth: Math.random()  + 1,
        steps: 1,
        bevelEnabled: false
    }
})
data.push({
    coordinates: [[[7,  -8],[-1,  -1],[-8,  -9],[17,  -18]],  [ [-2,  -9],[2, -10],[2,  -7],[-1,  -6]]],
    color: "#FFFE91", // 底部颜色
    color2: "#0ff", // 顶部颜色
    showBorder: true, // 显示边框线
    borderColor: "#39107B",// 边框线底部颜色
    borderColor2: "#EA3FF7",// 边框线顶部颜色
    baseHeight: 1, // 基准高度
    extrude: {
        depth: Math.random() * 3  + 1,
        steps: 1,
        bevelEnabled: false
    }
})
//
let fillExtrusions = new vjmap3d.FillExtrusionsEntity({ //FillExtrusionsEntity
    data: data,
    // showBorder: true, 可以全局控制是否显示边框线
    highlightStyle: {
        color: "#777" // 高亮时颜色
    }
})
fillExtrusions.addTo(app)
//
fillExtrusions.pointerEvents = true; // 允许事件交互
fillExtrusions.on("mouseover", e => {
    if (e?.intersection?.faceIndex !== undefined) {
        let itemData = fillExtrusions.getItemDataByFaceIndex(e?.intersection?.faceIndex);
        if (itemData && itemData.id) fillExtrusions.setItemHighlight(itemData.id, true)
    }
})
fillExtrusions.on("mouseout", e => {
    fillExtrusions.clearHighlight()
})
//
const ui = await app.getConfigPane({ title: "设置", style: { width: "250px", overflow: "hidden"}});
let cfg = [
    {
        type: 'checkbox',
        label: '显示顶点',
        getValue: () => fillExtrusions.isShowVertex(),
        setValue: v => {
            fillExtrusions.setShowVertex(v)
        }
    },
    {
        type: 'checkbox',
        label: '显示边框线',
        value: false,
        onChange: ({ value } )=> {
            // 获取模块对象
            let mod = fillExtrusions.getModule(vjmap3d.FillExtrusionsModule);
            mod.showBorder = value; // 设置属性
            mod.setData(mod.getData()); // 刷新下
        }
    },
    {
        type: 'button',
        label: '增加一个拉伸',
        value: ()=> {
            data.push({
                coordinates: [vjmap3d.randPoint2D(),vjmap3d.randPoint2D(),vjmap3d.randPoint2D()],
                color: vjmap3d.randColor(),
                extrude: {
                    depth: Math.random() * 1 + 1,
                    steps: 1,
                    bevelEnabled: false
                }
            })
            
            fillExtrusions.setData(data)
        }
    },
    {
        type: 'button',
        label: '修改第一个拉伸颜色',
        value: ()=> {
            if (data.length > 0) {
                data[0].color = vjmap3d.randColor()
            }
            fillExtrusions.setData(data)
        }
    },
    {
        type: 'button',
        label: '删除第一个拉伸',
        value: ()=> {
            if (data.length == 0) {
                return
            }
            data.splice(0, 1)
            fillExtrusions.setData(data)
        }
    }
]
cfg.forEach(c => ui.appendChild(c));
</pre>
</mapFrame>

### 2D地图模式示例

<mapFrame>
<pre>
 let svc = new vjmap.Service(env.serviceUrl, env.accessToken)
// 打开地图
let res = await svc.openMap({
    mapid: env.exampleMapId, // 地图ID
    // @ts-ignore
    mapopenway: vjmap.MapOpenWay.GeomRender, // 以几何数据渲染方式打开
    style: vjmap.openMapLightStyle() // div为深色背景颜色时，这里也传深色背景样式
})
if (res.error) {
    // 如果打开出错
    showError(res.error)
    return;
}
// 获取地图范围
let mapBounds = vjmap.GeoBounds.fromString(res.bounds);
// 根据地图范围建立几何投影坐标系
let prj = new vjmap.GeoProjection(mapBounds);
//
// 地图对象
let map = new vjmap.Map({
    container: 'map', // DIV容器ID
    style: svc.rasterStyle(), // 样式，这里是栅格样式
    center: prj.toLngLat(mapBounds.center()), // 设置地图中心点
    zoom: 2, // 设置地图缩放级别
    pitch: 45,
    antialias: true, // 反锯齿
    renderWorldCopies: false // 不显示多屏地图
});
//
// 关联服务对象和投影对象
map.attach(svc, prj);
await map.onLoad();
//
// 创建3d图层
let mapLayer = new vjmap3d.MapThreeLayer(map, {
    stat: {
        show: true,
        left: "0"
    }
});
map.addLayer(new vjmap.ThreeLayer({ context: mapLayer }))
let app = mapLayer.app;
let data = [];
// 外面边界的cad图上点坐标
let edge = [
	[587653761.9404196, 3103856376.721289],
	[587602251.0620328, 3103887684.472128],
	[587616705.4171646, 3103910542.4372025],
	[587668345.3998574, 3103879913.002372]
].map(p => vjmap3d.toWorld(p).toArray());
// 里面洞1的cad图上点坐标
let holes1 = [
	[587611243.649386, 3103892845.733727],
	[587618986.3449678, 3103888191.3405075],
	[587624099.183289, 3103897555.098504],
	[587616962.944966, 3103901844.930998]
].map(p => vjmap3d.toWorld(p).toArray());
// 里面洞2的cad图上点坐标
let holes2 = [
	[587648133.0725995, 3103883387.5836215],
	[587642667.2109047, 3103873956.0132804],
	[587650162.938667, 3103869450.080676],
	[587655761.2825799, 3103878763.0610456]
].map(p => vjmap3d.toWorld(p).toArray());
//
let bounds = map.getMapExtent();
let depth = vjmap3d.toDist(bounds.width() / 40);// 取地图范围的宽度的1/40距离做为拉伸高度
data.push({
    coordinates: [edge, holes1, holes2],
    color: "#FFFE91", // 底部颜色
    color2: "#0ff", // 顶部颜色
    showBorder: true, // 显示边框线
    borderColor: "#39107B",// 边框线底部颜色
    borderColor2: "#EA3FF7",// 边框线顶部颜色
    baseHeight: depth / 5, // 基准高度
    extrude: {
        depth: depth,
        steps: 1,
        bevelEnabled: false
    }
})
//
let fillExtrusions = new vjmap3d.FillExtrusionsEntity({ //FillExtrusionsEntity
    data: data,
    // showBorder: true, 可以全局控制是否显示边框线
    highlightStyle: {
        color: "#777" // 高亮时颜色
    }
})
fillExtrusions.addTo(app)
//
fillExtrusions.pointerEvents = true; // 允许事件交互
fillExtrusions.on("mouseover", e => {
    if (e?.intersection?.faceIndex !== undefined) {
        let itemData = fillExtrusions.getItemDataByFaceIndex(e?.intersection?.faceIndex);
        if (itemData && itemData.id) fillExtrusions.setItemHighlight(itemData.id, true)
    }
})
fillExtrusions.on("mouseout", e => {
    fillExtrusions.clearHighlight()
})
//
const ui = await app.getConfigPane({ title: "设置", style: { width: "250px", overflow: "hidden"}});
let cfg = [
    {
        type: 'checkbox',
        label: '显示顶点',
        getValue: () => fillExtrusions.isShowVertex(),
        setValue: v => {
            fillExtrusions.setShowVertex(v)
        }
    },
    {
        type: 'checkbox',
        label: '显示边框线',
        value: false,
        onChange: ({ value } )=> {
            let mod = fillExtrusions.getModule(vjmap3d.FillExtrusionsModule);
            mod.showBorder = value;
            mod.setData(mod.getData());
        }
    },
    {
        type: 'button',
        label: '增加一个拉伸',
        value: ()=> {
            data.push({
                coordinates: [mapBounds.randomPoint().toArray(),mapBounds.randomPoint().toArray(),mapBounds.randomPoint().toArray()].map(p => vjmap3d.toWorld(p).toArray()),
                color: vjmap3d.randColor(),
                extrude: {
                    depth: vjmap3d.toDist(bounds.width() * (Math.random() + 0.1) / 60),
                    steps: 1,
                    bevelEnabled: false
                }
            })
            fillExtrusions.setData(data)
        }
    },
    {
        type: 'button',
        label: '修改第一个拉伸颜色',
        value: ()=> {
            if (data.length > 0) {
                data[0].color = vjmap3d.randColor()
            }
            fillExtrusions.setData(data)
        }
    },
    {
        type: 'button',
        label: '删除第一个拉伸',
        value: ()=> {
            if (data.length == 0) {
                return
            }
            data.splice(0, 1)
            fillExtrusions.setData(data)
        }
    }
]
cfg.forEach(c => ui.appendChild(c));
</pre>
</mapFrame>
