# 设计理念

## 模块化设计
    
`vjmap3d`的模块化设计灵感来源于[whs.js](https://github.com/WhitestormJS/whs.js)(具有组件系统和插件支持)。

`vjmap3d`基于模块化开发遵循 `组合优于继承` 的开发设计原则，实现了自己的模块系统。我们将传统复杂的逻辑划分为独立的、可重复利用的部分，每个部分可以单独封装运行逻辑。然后通过灵活组合的形式，将多个简单模块合并在一起运行从而表现复杂的功能和逻辑。

`vjmap3d`中的 [App](https://vjmap.com/map3d/api/classes/App.html) 和 [Entity](https://vjmap.com/map3d/api/classes/Entity.html) 都是基于模块化开发。

示例：

可以给一个实体增加一个事件模块，让实体响应鼠标事件，高亮显示

```js
let entity = vjmap3d.Entity.fromObject3d(mesh);
entity.addTo(app);

// 增加一个事件模块，用来高亮提示
entity.add(vjmap3d.EventModule, {
    hoverSelect: true,
    hoverHtmlText: '我是用vjmap3d创建的Box',
    popupOptions: {
        anchor: "bottom"
    }
});
// 可以获取模块，调用模块中的方法
// let mod = entity.getModule(vjmap3d.EventModule);
// mod.removeEvent();

// 移除事件模块，移除后实体将不会响应鼠标事件
// entity.removeModule(vjmap3d.EventModule)
```
<mapFrame>
<pre>
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
    container: "map", // 容器id
    render: { // 渲染参数
        alpha: true,
        antialias: true
    },
    scene: {  // 场景设置
        gridHelper: { visible: true } // 是否显示坐标网格
    }
})
//
let box = new THREE.BoxGeometry(2, 2, 2)
const material = new THREE.MeshStandardMaterial({
    color: "#0f0",
    side: THREE.DoubleSide
});
let mesh = new THREE.Mesh(box, material);
let entity = vjmap3d.Entity.fromObject3d(mesh);
entity.addTo(app);
// 增加一个事件模块，用来高亮提示
entity.add(vjmap3d.EventModule, {
    hoverSelect: true,
    hoverHtmlText: '我是用vjmap3d创建的Box',
    popupOptions: {
        anchor: "bottom"
    }
});
setTimeout(() => {
    // 过二十秒后移除事件模块，移除后实体将不会响应鼠标事件
    entity.removeModule(vjmap3d.EventModule)
}, 20000)
</pre>
</mapFrame>




     
       

## 兼容性设计
`vjmap3d`基于`threejs`开发，`threejs`拥有庞大的开发者社区和丰富的教程资源，`vjmap3d`能兼容`threejs`写法，利用`threejs`生态资源。
同时`vjmap3d`提供了一些的预设对象和材质，以及粒子系统、后期处理等高级特性，便于快速实现复杂视觉效果， 同时提供丰富的交互组件和事件处理机制，增强用户体验和应用互动性。支持场景中的对象拾取与交互，简化了交互逻辑的实现。

### 原生threejs写法
```js
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );

const renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.getElementById("map").appendChild( renderer.domElement );

const geometry = new THREE.BoxGeometry( 1, 1, 1 );
const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
const cube = new THREE.Mesh( geometry, material );
scene.add( cube );

camera.position.z = 5;

function animate() {
    requestAnimationFrame( animate );

    cube.rotation.x += 0.01;
    cube.rotation.y += 0.01;

    renderer.render( scene, camera );
}

animate();
```

<mapFrame>
<pre>
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
//
const renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.getElementById("map").appendChild( renderer.domElement );
//
const geometry = new THREE.BoxGeometry( 1, 1, 1 );
const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
const cube = new THREE.Mesh( geometry, material );
scene.add( cube );
//
camera.position.z = 5;
const controls = new OrbitControls(camera, renderer.domElement);
controls.update();
//
function animate() {
    requestAnimationFrame( animate );
    cube.rotation.x += 0.01;
    cube.rotation.y += 0.01;
    renderer.render( scene, camera );
}
animate();
</pre>
</mapFrame>


### vjmap3d模仿threejs原生写法
`vjmap3d`中 `scene`可通过 `app.scene` 获取，`camera`可通过  `app.camera` 获取，`renderer`可通过  `app.renderer` 获取

```js
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
    container: "map", // 容器id
    camera: {
        fov: 75,
        near: 0.1, 
        far: 1000,
        position: new THREE.Vector3(0, 0, 5)
    }
})
const geometry = new THREE.BoxGeometry( 1, 1, 1 );
const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
const cube = new THREE.Mesh( geometry, material );
app.scene.add( cube );

function animate() {
    requestAnimationFrame( animate );

    cube.rotation.x += 0.01;
    cube.rotation.y += 0.01;
    app.renderer.render( app.scene, app.camera );
}

animate();
```

<mapFrame>
<pre>
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
    container: "map", // 容器id
    camera: {
        fov: 75,
        near: 0.1, 
        far: 1000,
        position: new THREE.Vector3(0, 0, 5)
    }
})
const geometry = new THREE.BoxGeometry( 1, 1, 1 );
const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
const cube = new THREE.Mesh( geometry, material );
app.scene.add( cube );
//
function animate() {
    requestAnimationFrame( animate );
    cube.rotation.x += 0.01;
    cube.rotation.y += 0.01;
    app.renderer.render( app.scene, app.camera );
}
animate();
</pre>
</mapFrame>
   

### vjmap3d封装写法  
 也可以用`vjmap3d`封装的一些函数进上面的进行改进，达到更好的效果，如对上面的立方体`辉光`处理，只需创建一个`实体`和`立方体`对象进行关联。
```js
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
    container: "map", // 容器id
    camera: {
        fov: 75,
        near: 0.1, 
        far: 1000,
        position: new THREE.Vector3(0, 0, 5)
    }
})
const geometry = new THREE.BoxGeometry( 1, 1, 1 );
const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
const cube = new THREE.Mesh( geometry, material );
app.scene.add( cube );
let entity = vjmap3d.Entity.attchObject(cube); // 创建一个实体与对象关联
// 或者 上面的两行也可以用下面的两行代替
//let entity = vjmap3d.Entity.fromObject3d(cube); // 创建一个来源于object3d的实体对象
//entity.addTo(app);// 把实体加进应用中
entity.bloom = true; // 设置实体辉光
entity.selected = true; // 设置实体选择
// 原生的requestAnimationFrame可用事件回调来处理，更简洁
app.signal.onAppRender.add(() => {
    cube.rotation.x += 0.01;
    cube.rotation.y += 0.01;
})
```
<mapFrame>
<pre>
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
    container: "map", // 容器id
    camera: {
        fov: 75,
        near: 0.1, 
        far: 1000,
        position: new THREE.Vector3(0, 0, 5)
    }
})
const geometry = new THREE.BoxGeometry( 1, 1, 1 );
const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
const cube = new THREE.Mesh( geometry, material );
app.scene.add( cube );
let entity = vjmap3d.Entity.attchObject(cube); // 创建一个实体与对象关联
// 或者 上面的两行也可以用下面的两行代替
//let entity = vjmap3d.Entity.fromObject3d(cube); // 创建一个来源于object3d的实体对象
//entity.addTo(app);// 把实体加进应用中
entity.bloom = true; // 设置实体辉光
entity.selected = true; // 设置实体选择
// 原生的requestAnimationFrame可用事件回调来处理，更简洁
app.signal.onAppRender.add(() => {
    cube.rotation.x += 0.01;
    cube.rotation.y += 0.01;
})
</pre>
</mapFrame>
   



## 插件化设计

 `vjmap3d`将额外的功能和特性作为独立的模块添加到做为插件库，通过加载插件来使用。这样不仅使核心库保持简洁和高效，还使用更新和维护变得更加简便，而不需要修改核心代码。

 加载几何扩展插件库示例:

<mapFrame showCode="true">
<pre>
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
    container: "map", // 容器id
    scene: {
        gridHelper: { visible: true } // 是否显示坐标网格
    }
})
// 加载几何插件库，对几何图形进行交差并补计算
let amLib = await vjmap3d.loadPluginAlgorithm();
const sphere = new amLib.Brush( new THREE.SphereGeometry(1) );
sphere.material.color = new THREE.Color("#0f0")
sphere.position.set(0, 0, 0)
sphere.updateMatrixWorld();
const box = new amLib.Brush( new THREE.BoxGeometry(1, 1, 1) );
box.position.set(0, 0.5, 0)
box.material.color = new THREE.Color("#f00")
box.updateMatrixWorld();
const evaluator = new amLib.Evaluator();
// 在球的表面挖去立方体的表面
const result = evaluator.evaluate( sphere, box, amLib.HOLLOW_SUBTRACTION );
app.scene.add(result)
</pre>
</mapFrame>