一个简单的threejs盒剖切功能

支持六面方向拖拽、反向、切面填充. 

 代码:






import * as THREE from 'three'
import { MouseHandler } from 'src/renderers/input/mouse'
import {mergeGeometries} from 'three/examples/jsm/utils/BufferGeometryUtils'
import {BaseHandle} from './base'
import {HANDLE_TYPES} from '../constant'

const DEFAULT_EMPTY_ARRAY:any=[]
function createPlaneStencilGroup(geometry:THREE.BufferGeometry, plane:THREE.Plane, renderOrder:number) {

    const group = new THREE.Group();
    const baseMat = new THREE.MeshBasicMaterial();
    baseMat.color.setHex(0x0000ff)
    baseMat.depthWrite = false;
    baseMat.depthTest = false;
    baseMat.colorWrite = false;
    baseMat.stencilWrite = true;
    baseMat.stencilFunc = THREE.AlwaysStencilFunc;

    // back faces
    const mat0 = baseMat.clone();
    mat0.side = THREE.BackSide;
    mat0.clippingPlanes = [plane];
    mat0.stencilFail = THREE.IncrementWrapStencilOp;
    mat0.stencilZFail = THREE.IncrementWrapStencilOp;
    mat0.stencilZPass = THREE.IncrementWrapStencilOp;

    const mesh0 = new THREE.Mesh(geometry, mat0);
    mesh0.renderOrder = renderOrder;
    group.add(mesh0);

    // front faces
    const mat1 = baseMat.clone();
    mat1.side = THREE.FrontSide;
    mat1.clippingPlanes = [plane];
    mat1.stencilFail = THREE.DecrementWrapStencilOp;
    mat1.stencilZFail = THREE.DecrementWrapStencilOp;
    mat1.stencilZPass = THREE.DecrementWrapStencilOp;

    const mesh1 = new THREE.Mesh(geometry, mat1);
    mesh1.renderOrder = renderOrder;

    group.add(mesh1);

    return group;

}


class ClipFace extends THREE.Mesh{
    materialActive:THREE.MeshBasicMaterial
    materialInactive:THREE.MeshBasicMaterial
    lines:ClipLine[]=[]
    constructor(public vertices:{dirty:boolean,position:THREE.Vector3}[],public axes:string){
        super()
        this.materialInactive=new THREE.MeshBasicMaterial({
            colorWrite:false,
            depthWrite:false,
        })
        this.materialActive=new THREE.MeshBasicMaterial({
            color:0x00aaff,
            transparent:true,
            opacity:0.3
        })
        this.geometry=new THREE.BufferGeometry()
        this.geometry.setAttribute('position',new THREE.BufferAttribute(new Float32Array(4*3),3));
        this.geometry.setIndex(new THREE.BufferAttribute(new Uint16Array([2,1,0,3,2,0]),1));
        (this.geometry.attributes.position as THREE.Float32BufferAttribute).setUsage(THREE.DynamicDrawUsage);
        this.setActive(false)
        this.updateVertices(true)

    }
    updateVertices(forceUpdate=false){
        let needsUpdate=false
        this.vertices.forEach((p,i)=>{
            if((forceUpdate||p.dirty)){
                needsUpdate=true
                const v=p.position;
                this.geometry.attributes.position.setXYZ(i,v.x,v.y,v.z)
            } 
       
        })
        this.geometry.attributes.position.needsUpdate=needsUpdate
       
    }
    setActive(active:boolean){
        this.material=active?this.materialActive:this.materialInactive
        this.lines.forEach(line=>{
            line.setActive(active)
        })
    }
}

class ClipLine extends THREE.LineSegments{
    activeColor:number=0x00aaff
    defaultColor:number=0x88aaff
    constructor(public vertices:{dirty:boolean,position:THREE.Vector3}[]){
        super()
        this.material=new THREE.LineBasicMaterial({
            color:this.defaultColor
        })
        this.geometry=new THREE.BufferGeometry()
        this.geometry.setAttribute('position',new THREE.BufferAttribute(new Float32Array(2*3),3));
        (this.geometry.attributes.position as THREE.Float32BufferAttribute).setUsage(THREE.DynamicDrawUsage);
        this.updateVertices(true)
    }
    updateVertices(forceUpdate=false){
        let needsUpdate=false
        this.vertices.forEach((p,i)=>{
            if((forceUpdate||p.dirty)){
                needsUpdate=true
                const v=p.position;
                this.geometry.attributes.position.setXYZ(i,v.x,v.y,v.z)
            } 
       
        })
        this.geometry.attributes.position.needsUpdate=needsUpdate
       
    }
    setActive(active:boolean){
        (this.material as THREE.LineBasicMaterial).color.setHex(active?this.activeColor:this.defaultColor)
    }
}

interface ClipBoxOptions{
    cap?:boolean
    negative?:boolean
    renderer:THREE.WebGLRenderer
    scene:THREE.Scene
    camera:()=>THREE.Camera
    update:()=>void
    onDragRange?:()=>void
    onDragStart?:()=>void
    onDragEnd?:()=>void
}
enum ClipBoxState{
    NONE,// 未激活
    CLIP,// 显示剖切框
    CLIP_EFFECT, // 隐藏剖切框,显示剖切效果
    EXIT // 退出
}
class ClipBox{
    min=new THREE.Vector3()
    max=new THREE.Vector3()
    initMin=new THREE.Vector3()
    initMax=new THREE.Vector3()
    box3=new THREE.Box3()
    target?:THREE.Object3D
    group:THREE.Group=new THREE.Group()
    planes?:THREE.Plane[]
    planeObjects?:THREE.Mesh[]
    vertices?:{dirty:boolean,position:THREE.Vector3}[]
    lines?:ClipLine[]
    faces?:ClipFace[]
    boxGeometry!:THREE.BoxGeometry
    basicMaterial?:THREE.MeshBasicMaterial
    hoverMaterial?:THREE.MeshBasicMaterial
    _clipScene=new THREE.Group()
    mouseHandle!:MouseHandler
    state:ClipBoxState=ClipBoxState.NONE
    constructor(public options:ClipBoxOptions){
            this.options={
                cap:false,
                negative:false,
                ...this.options
            }
    }
    get clipScene(){
        return this._clipScene
    }
    get renderer(){
        return this.options.renderer
    }
    get camera(){
        return this.options.camera()
    }

    initClipBox(){
      
        this.clipScene.matrixWorldAutoUpdate=false;
        this.clipScene.matrixAutoUpdate=false
        this.clipScene.matrixWorldNeedsUpdate=false;
        this.box3.setFromObject(this.target!).expandByScalar(1.2)
        this.min.copy(this.box3.min)
        this.initMin.copy(this.box3.min)
        this.max.copy(this.box3.max)
        this.initMax.copy(this.box3.max)
        this.initPlanes()
        this.initVertices()
        this.initFaces()
        this.initLines()
        this.updatePlanes()
        this.initEvents()
        this.initCap()
        this.updateTargetClipPlanes()
        this.options.scene.add(this.clipScene)
      
    }
    // 还原
    reset(){
        this.min.copy(this.initMin)
        this.max.copy(this.initMax)
        this.update()
    }
    // 显示剖切框
    showClip(){
        if(this.state===ClipBoxState.CLIP){
            return
        }
        if(this.state===ClipBoxState.NONE){
            this.initClipBox()
        }else if(this.state===ClipBoxState.EXIT){
            this.updateTargetClipPlanes()
        }
        this.updateAllClipBox()
        this.renderer.localClippingEnabled=true;
        this.state=ClipBoxState.CLIP
        this.options.scene.add(this.clipScene)
        this.attachInteractive()
        this.update()
   
    }
    // 隐藏剖切框
    hideClip(){
        if(this.state===ClipBoxState.CLIP){
            this.state=ClipBoxState.CLIP_EFFECT
            this.options.scene.remove(this.clipScene)
            this.mouseHandle.detachEvents()
            this.update()
        }
    }
    attach(obj:THREE.Object3D){
        this.target=obj
    }
    update(){
        this.options.update()
    }
    detach(){
        this.target=undefined    
    }
    exit(){
        if(this.state===ClipBoxState.EXIT){
            return
        }
        this.min.copy(this.initMin)
        this.max.copy(this.initMax)
        this.renderer.localClippingEnabled=false
        this.state=ClipBoxState.EXIT
        this.options.scene.remove(this.clipScene)
        this.mouseHandle.detachEvents()
        this.resetTargetClipPlanes()
        this.target=undefined    
        this.update()

    }
    initEvents(){
        this.mouseHandle=new MouseHandler(this.renderer.domElement,document,()=>this.camera)
        const plane=new THREE.Plane()
        const cameraDirection=new THREE.Vector3()
        const startPoint=new THREE.Vector3()
        const deltaPoint=new THREE.Vector3()
        const intersectionPoint=new THREE.Vector3()
        this.mouseHandle.addEventListener('object-enter',e=>{
               (e.intersections[0].object as ClipFace).setActive(true)
               this.update()
        })
        this.mouseHandle.addEventListener('object-leave',e=>{
            (e.intersections[0].object as ClipFace).setActive(false)
            this.update()
        })

        this.mouseHandle.addEventListener('select',e=>{
            this.camera.getWorldDirection(cameraDirection)
            plane.setFromNormalAndCoplanarPoint(cameraDirection,e.target.selectedIntersections[0].point)
            if(this.mouseHandle.raycaster.ray.intersectPlane(plane,intersectionPoint)){
                startPoint.copy(intersectionPoint)
            }
            this.options.onDragStart?.()
            e.target.pointerEvent?.stopImmediatePropagation()
        })
        this.mouseHandle.addEventListener('object-drag',e=>{
            this.mouseHandle.updateRaycaster()
            if(this.mouseHandle.raycaster.ray.intersectPlane(plane,intersectionPoint)){
                const dragObject=e.target.selectedIntersections[0].object as ClipFace
                deltaPoint.copy(intersectionPoint).sub(startPoint)
                startPoint.copy(intersectionPoint)
                
                if(dragObject.axes==='x+'){
                    this.max.x=Math.min(this.initMax.x,Math.max(this.initMin.x,this.max.x+deltaPoint.x))
                } 
                if(dragObject.axes==='x-'){
                    this.min.x=Math.min(this.initMax.x,Math.max(this.initMin.x,this.min.x+deltaPoint.x))
                }
                if(dragObject.axes==='y+'){
                    this.max.y=Math.min(this.initMax.y,Math.max(this.initMin.y,this.max.y+deltaPoint.y))
                }
                if(dragObject.axes==='y-'){
                    this.min.y=Math.min(this.initMax.y,Math.max(this.initMin.y,this.min.y+deltaPoint.y))
                }
                if(dragObject.axes==='z+'){
                    this.max.z=Math.min(this.initMax.z,Math.max(this.initMin.z,this.max.z+deltaPoint.z))
                }
                if(dragObject.axes==='z-'){
                    this.min.z=Math.min(this.initMax.z,Math.max(this.initMin.z,this.min.z+deltaPoint.z))
                }
       
                this.updateClipBox(dragObject)
                this.options.onDragRange?.()
                this.update()
            }
        })
        this.mouseHandle.addEventListener('pointerup',e=>{
            this.options.onDragEnd?.()
        })
    }
    attachInteractive(){
        this.mouseHandle.hoverObjects=this.faces!.slice()
        this.mouseHandle.selectObjects=this.faces!.slice();
        this.mouseHandle.attachEvents()
    }
    initPlanes(){
        this.planes=[
            new THREE.Plane(new THREE.Vector3(1, 0, 0), 0),// 左
            new THREE.Plane(new THREE.Vector3(-1, 0, 0), 0), // 右
            new THREE.Plane(new THREE.Vector3(0, 1, 0), 0),// 下
            new THREE.Plane(new THREE.Vector3(0, -1, 0), 0),// 上
            new THREE.Plane(new THREE.Vector3(0, 0, 1), 0), // 后
            new THREE.Plane(new THREE.Vector3(0, 0, -1), 0), // 前
        ]
      
    }
    initVertices(){
        this.vertices=[
            {dirty:false,position:new THREE.Vector3(this.min.x,this.min.y,this.min.z)},// left bottom
            {dirty:false,position: new THREE.Vector3(this.min.x,this.max.y,this.min.z)},// left top
            {dirty:false,position:new THREE.Vector3(this.max.x,this.max.y,this.min.z)}, // right top
            {dirty:false,position:new THREE.Vector3(this.max.x,this.min.y,this.min.z)}, // right bottom
            // positive
            {dirty:false,position:new THREE.Vector3(this.min.x,this.min.y,this.max.z)},// left bottom
            {dirty:false,position:new THREE.Vector3(this.min.x,this.max.y,this.max.z)},// left top
            {dirty:false,position:new THREE.Vector3(this.max.x,this.max.y,this.max.z)}, // right top
            {dirty:false,position:new THREE.Vector3(this.max.x,this.min.y,this.max.z)}, // right bottom
        ]
    }
    initFaces(){
        this.faces=[
            new ClipFace([7,6,2,3].map(i=>this.vertices![i]),'x+'),// x+
            new ClipFace([0,1,5,4].map(i=>this.vertices![i]),'x-'), // x-
            new ClipFace([5,1,2,6].map(i=>this.vertices![i]),'y+'),// y+
            new ClipFace([7,3,0,4].map(i=>this.vertices![i]),'y-'),// y-
            new ClipFace([3,2,1,0].map(i=>this.vertices![i]),'z-'), // z-
            new ClipFace([4,5,6,7].map(i=>this.vertices![i]),'z+'), // z+
        ]
        this.faces.forEach(face=>{
            this.clipScene.add(face)
        })
    }
    initLines(){

        this.lines=[
            new ClipLine([0,1].map(i=>this.vertices![i])),// positive left 0
            new ClipLine([1,2].map(i=>this.vertices![i])),// positive top  1
            new ClipLine([2,3].map(i=>this.vertices![i])),// positive right 2
            new ClipLine([3,0].map(i=>this.vertices![i])),// positive bottom 3

            new ClipLine([4,5].map(i=>this.vertices![i])),// negative left 4
            new ClipLine([5,6].map(i=>this.vertices![i])),// negative top 5
            new ClipLine([6,7].map(i=>this.vertices![i])),// negative right 6
            new ClipLine([7,4].map(i=>this.vertices![i])),// negative bottom 7

            new ClipLine([1,5].map(i=>this.vertices![i])),// left top 8
            new ClipLine([0,4].map(i=>this.vertices![i])),// left bottom 9

            
            new ClipLine([2,6].map(i=>this.vertices![i])),// right top 10
            new ClipLine([3,7].map(i=>this.vertices![i])),// right bottom 11
        ]
        this.faces![0].lines=[10,11,2,6].map(d=>this.lines![d])
        this.faces![1].lines=[8,9,0,4].map(d=>this.lines![d])
        this.faces![2].lines=[8,10,1,5].map(d=>this.lines![d])
        this.faces![3].lines=[9,11,3,7].map(d=>this.lines![d])
        this.faces![4].lines=[0,1,2,3].map(d=>this.lines![d])
        this.faces![5].lines=[4,5,6,7].map(d=>this.lines![d])
        this.lines.forEach(line=>{
            this.clipScene.add(line)
        })
    }
    capRemoves=[]
    initCap(){
        if(!this.options.cap&&this.planeObjects){
            this.capRemoves.forEach(obj=>{
                this.clipScene.remove(obj)
            })
            this.planeObjects=undefined
            this.capRemoves=[]
        }
        if(this.planeObjects){
            return
        }

                const planeObjects = this.planeObjects=[] as THREE.Mesh[];
                const size=new THREE.Vector3()
                this.box3.getSize(size)
               // const center=this.box3.getCenter(new THREE.Vector3())
                const max=Math.max(size.x,size.y,size.z)
				const planeGeom = new THREE.PlaneGeometry(max,max);
   
                let planes=this.planes!;
        
                const geometries:THREE.BufferGeometry[]=[]
                this.target!.updateMatrix()
                this.target?.updateMatrixWorld(true)
                this.target?.traverse((obj)=>{
                    if((obj as any).isMesh||obj.isLine){
                        const geometry=(obj as THREE.BufferGeometry).geometry.clone()
                        geometry.applyMatrix4(obj.matrixWorld)
                        geometries.push(geometry)
                    }
                })
                const geometry=mergeGeometries(Array.from(geometries))

                const object = new THREE.Group();
                object.name='stencilGroup'
				this.clipScene.add( object );
                this.capRemoves.push(object)
				for ( let i = 0; i < planes.length; i ++ ) {

					const poGroup = new THREE.Group();
					const plane = planes[ i ];
					const stencilGroup = createPlaneStencilGroup( geometry, plane, i + 1 );
					const planeMat =
						new THREE.MeshNormalMaterial( {
						//	color: 0xff0000,
                         //   depthTest:false,
                            polygonOffset:true,
                            polygonOffsetFactor:1,
                            polygonOffsetUnits:0,
							clippingPlanes: planes.filter( p => p !== plane ),
							stencilWrite: true,
							stencilRef: 0,
                           // side:THREE.DoubleSide,
							stencilFunc: THREE.NotEqualStencilFunc,
							stencilFail: THREE.ReplaceStencilOp,
							stencilZFail: THREE.ReplaceStencilOp,
							stencilZPass: THREE.ReplaceStencilOp,
						} );
					const po = new THREE.Mesh( planeGeom, planeMat );
					po.onAfterRender = function ( renderer ) {
						renderer.clearStencil();
					};
					po.renderOrder = i + 1.1;
					object.add( stencilGroup );
					poGroup.add( po );
					planeObjects.push(po);
                    this.capRemoves.push(poGroup)
					this.clipScene.add( poGroup );
				}
        
			
    }
    resetTargetClipPlanes(){
        this.target!.traverse((obj:any)=>{
            if(obj.isMesh||obj.isLine){
                if(Array.isArray(obj.material)){
                    obj.material.forEach((m:any)=>{
                        m.clipIntersection=false;
                        m.clippingPlanes=DEFAULT_EMPTY_ARRAY
                    })
                }else{
                    obj.material.clipIntersection=false
                    obj.material.clippingPlanes=DEFAULT_EMPTY_ARRAY
                }
            }
        })
    }
    updateTargetClipPlanes(){
        this.target!.traverse((obj:any)=>{
            if(obj.isMesh||obj.isLine){
                if(Array.isArray(obj.material)){
                    obj.material.forEach((m:any)=>{
                        m.clipIntersection=this.options.negative
                        m.clippingPlanes=this.planes
                    })
                }else{
                    obj.material.clipIntersection=this.options.negative
                    obj.material.clippingPlanes=this.planes
                }
            }
        })
    }
    updatePlanes(){
        const planes=this.planes!;
        planes[0].normal.set(1, 0, 0)
        planes[1].normal.set(-1, 0, 0)
        planes[2].normal.set(0, 1, 0)
        planes[3].normal.set(0, -1, 0)
        planes[4].normal.set(0, 0, 1)
        planes[5].normal.set(0, 0, -1)


        planes[0].constant=-this.min.x;
        planes[1].constant=this.max.x;
        planes[2].constant=-this.min.y;
        planes[3].constant=this.max.y;
        planes[4].constant=-this.min.z;
        planes[5].constant=this.max.z;
        if(this.options.negative){
            planes.forEach(plane=>{
                plane.negate()
            })
        }
       
    }
    updateVertices(){
        const vertices=this.vertices!;
        vertices[0].position.set(this.min.x,this.min.y,this.min.z)
        vertices[1].position.set(this.min.x,this.max.y,this.min.z)
        vertices[2].position.set(this.max.x,this.max.y,this.min.z)
        vertices[3].position.set(this.max.x,this.min.y,this.min.z)
        vertices[4].position.set(this.min.x,this.min.y,this.max.z)
        vertices[5].position.set(this.min.x,this.max.y,this.max.z)
        vertices[6].position.set(this.max.x,this.max.y,this.max.z)
        vertices[7].position.set(this.max.x,this.min.y,this.max.z)

    }
    updateClipBox(face: ClipFace){
        this.updateVertices()
        face.vertices.forEach(v=>{
            v.dirty=true;
        })
        this.faces?.forEach(face=>{
            face.updateVertices()
        })
        this.lines?.forEach(line=>{
            line.updateVertices()
        })
        this.vertices?.forEach(v=>{
            v.dirty=false;
        })
        this.updatePlanes()
        this.updateClipCap()
    }
    updateAllClipBox(){
        this.initCap()
        this.updateVertices()
        this.faces?.forEach(face=>{
            face.vertices.forEach(v=>{
                v.dirty=true;
            })
        })
      
        this.faces?.forEach(face=>{
            face.updateVertices()
        })
        this.lines?.forEach(line=>{
            line.updateVertices()
        })
        this.vertices?.forEach(v=>{
            v.dirty=false;
        })
        this.updatePlanes()
        this.updateClipCap()
        this.updateTargetClipPlanes()
        this.update()
    }
    updateClipBoxFromAxes(axes:string){
        this.updateVertices()
        this.faces?.forEach(face=>{
            if(face.axes.charAt(0)===axes.toLowerCase()){
                face.vertices.forEach(v=>{
                    v.dirty=true;
                })
            }
        })
      
        this.faces?.forEach(face=>{
            face.updateVertices()
        })
        this.lines?.forEach(line=>{
            line.updateVertices()
        })
        this.vertices?.forEach(v=>{
            v.dirty=false;
        })
        this.updatePlanes()
        this.updateClipCap()
    }
    updateClipCap(){
        if(!this.options.cap){
            return
        }
        const planes=this.planes!;
        const planeObjects=this.planeObjects!;
        const center=this.initMin.clone().add(this.initMax).multiplyScalar(0.5)
        const targetDist=[this.min.x-center.x,center.x-this.max.x,this.min.y-center.y,center.y-this.max.y,this.min.z-center.z,center.z-this.max.z]
        const size=new THREE.Vector3()
        this.box3.getSize(size)
        const max=Math.max(size.x,size.y,size.z)
        for ( let i = 0; i < planeObjects.length; i ++ ) {

            const po = planeObjects[ i ];
            const plane = planes[i];
           // plane.coplanarPoint( po.position );
             po.material.clipIntersection=this.options.negative
            if(this.options.negative){
                po.material.clippingPlanes=this.planes.map(p=>{
                    return p.clone().translate(p.normal.clone().negate());
                })
            }else{
               po.material.clippingPlanes=this.planes?.filter(p=>p!==plane)
            }
            // po.lookAt(
            //     po.position.x - plane.normal.x,
            //     po.position.y - plane.normal.y,
            //     po.position.z - plane.normal.z,
            // );
            //      if(!this.helpers[i]){
            //     this.helpers[i]=new THREE.Mesh(new THREE.PlaneGeometry(max,max),new THREE.MeshBasicMaterial({
            //         color:[0xff000,0xffff00,0x0000ff,0x00ffff,0x00ff00,0xff00ff][i],
            //         //side:THREE.DoubleSide,
            //         depthTest:true,
            //     }))
            //     this.clipScene.add(this.helpers[i])
               
            // }
            // this.helpers[i].position.set(0,0,0)
            // this.helpers[i].lookAt(plane.normal.clone().negate())
            // this.helpers[i].position.copy(plane.normal).multiplyScalar(targetDist[i])
            // this.helpers[i].position.add(center)


          //  po.position.set(0,0,0)
          //  po.lookAt(plane.normal.clone().negate())
            po.position.copy(plane.normal).multiplyScalar(targetDist[i])
            po.position.add(center)

            po.lookAt(
                po.position.x - plane.normal.x,
                po.position.y - plane.normal.y,
                po.position.z - plane.normal.z,
            );
            po.updateMatrixWorld()
        }
    }
    helpers=[]
    negativeClip(){
        this.options.negative=!this.options.negative;
        this.updatePlanes()
        this.updateClipCap()
        this.updateTargetClipPlanes()
    
        this.update()
    }
   

}

export class BoxSliceHandle extends BaseHandle<{},{
     'value-change':{}
}>{
    static handleName: HANDLE_TYPES=HANDLE_TYPES.CUTTING_BOX;
    clipBox?:ClipBox;
    init(): void {

        this.clipBox=new ClipBox({
            renderer:this.renderer.renderer,
            scene:this.renderer.scene,
            camera:()=>this.renderer.camera,
            onDragStart:()=>{
                this.renderer.disableControls()
            },
            onDragEnd:()=>{
                this.renderer.enableControls()
            },
            update:()=>{
                this.renderer.refresh()
            },
            onDragRange:()=>{
                this.dispatchEvent({type:'value-change'})
            }
        })
    }
    resetClip(){
        this.clipBox?.reset()
    }

    setBoxClipValue(axes:string,min:number,max:number){
        const clipBox=this.clipBox!;
        if(axes==='x'){
            clipBox.min.x=min
            clipBox.max.x=max
        }else if(axes==='y'){
            clipBox.min.y=min
            clipBox.max.y=max
        }else if(axes==='z'){
            clipBox.min.z=min
            clipBox.max.z=max
        }
        this.clipBox?.updateClipBoxFromAxes(axes)
        this.refresh()
    }
    getBoxClipValue(){
        const clipBox=this.clipBox!;
        return [clipBox.min.clone(),clipBox.max.clone()]
    }
    getBoxClipRange(){
        const clipBox=this.clipBox!;
        return [clipBox.initMin.clone(),clipBox.initMax.clone()]
    }
    onEnter(options?: {} | undefined): void {
        this.clipBox?.attach(this.renderer.modelScene)
        this.clipBox?.showClip()
    }
    onLeave(): void {
        this.clipBox?.hideClip()
    }
    exitBoxClip(){
        this.clipBox?.exit()
        this.ownerHandle.switchNone()
    }
    dispose(): void {
        
    }
   
    handlePointer(e: { target: MouseHandler; type: string;intersections:THREE.Intersection[] }): void {
           if(e.type==='object-enter'){
               // e.intersections[0]
           }else if(e.type==='pointerup'){
            //this.switch(HANDLE_TYPES.NONE)

           }
    }

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/692782.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MathType7永久破解免费版下载最新2024

“数学公式”作为学术和科普写作中不可或缺的一环&#xff0c;一直困扰着很多作者。 在Word等文本编辑器中&#xff0c;虽然提供了插入公式的功能&#xff0c;但使用起来却并不友好&#xff0c;不仅效率低下&#xff0c;而且在调整格式时也会遇到各种问题。而MathType公式编辑器…

【Python机器学习】PCA——特征提取(2)

上一篇写过了用单一最近邻分类器训练后的精度只有0.22. 现在用PCA。想要度量人脸的相似度&#xff0c;计算原始像素空间中的距离是一种相当糟糕的方法。用像素表示来比较两张图像时&#xff0c;我们比较的是每个像素的灰度值与另一张图像对应位置的像素灰度值。这种表示与人们…

flask实现抽奖程序(一)

后端代码E:\LearningProject\lottery\app.py from flask import Flask, render_template import randomapp Flask(__name__)employees [赵一, 钱二, 孙三, 李四, 周五, 吴六, 郑七, 王八]app.route(/) def hello_world():return render_template(index.html, employeesemplo…

Centos7系统禁用Nouveau内核驱动程序【笔记】

在CentOS系统中,Nouveau是开源的NVIDIA显卡驱动程序,但它与NVIDIA的官方驱动程序NVIDIA Proprietary Driver存在兼容性问题。 如果你想要禁用Nouveau并使用NVIDIA官方驱动,可以按照以下步骤操作: 1、创建一个黑名单文件以禁用Nouveau驱动。 echo blacklist nouveau | su…

M3ID和CD的区别

M3ID的公式&#xff1a; CD的公式&#xff08;概率空间版本&#xff09;&#xff1a; CD的公式&#xff08;logits空间版本&#xff09;&#xff1a; 为简单对比&#xff0c;主要比较概率空间版本。logits空间版本已有证明和概率空间版本等效&#xff0c;在此不做详细讨论&a…

Transformer论文精读

Transformer&#xff1a;Attention is all you need Abstract&#xff1a; 在主流的序列转录模型&#xff08;sequence transduction models&#xff1a;给一个序列&#xff0c;生成另一个序列&#xff09;&#xff0c;主要依赖循环或者卷积神经网络&#xff0c;一般是用enco…

【Gitlab】Gitlab MAC M1通过Docker Desktop安装教程

目录 一、拉取镜像 二、配置容器 2.1 配置Volumes 2.2 配置Gitlab 2.3 配置完成&#xff0c;重启GitLab容器 2.4 查看GitLab的root密码 三、brew安装gitlab 3.1 安装命令 3.2 启动命令 参考资料 一、拉取镜像 docker pull yrzr/gitlab-ce-arm64v8 二、配置容器 2.1 …

wps:基本使用【笔记】

wps&#xff1a;基本使用【笔记】 前言版权推荐wps&#xff1a;基本使用如何去除复制文本的样式显示空格、换行、分节符快捷键设置字体添加章节添加奇数页分节符设置页边距设置页眉页脚设置页码 最后 前言 2024-6-5 23:10:12 以下内容源自《【笔记】》 仅供学习交流使用 版权…

折腾日记:如何在Mac上连接Esp32

个人博客地址 最近购买了一块Esp32单片机&#xff0c;在Mac环境上进行开发&#xff0c;并且成功点亮LED灯和连上屏幕&#xff0c;为什么会上手选择Esp32开发板&#xff0c;主要考虑它自带Wi-Fi和蓝牙&#xff0c;单价也不高&#xff0c;就算后面不玩了&#xff0c;也能转成物联…

【CS.AL】八大排序算法 —— 快速排序全揭秘:从基础到优化

文章目录 1. 快速排序简介1.1 定义1.2 时间复杂度1.3 相关资源 2. 最优的Partition算法 &#x1f525;2.1 Introsort简介2.2 过程示例 3. 非递归快速排序3.1 实现 4. 递归快速排序4.1 实现 5. 有问题的Partition5.1 实现 6. 三中位数主元选择6.1 实现 7. 总结 1. 快速排序简介 …

Typescript 中 tsconfig.json 无法写入文件,因为它会覆盖输入文件

先来看看问题 在开发ts项目的时候&#xff0c;包错提示无法写入文件 tsconfig.json无法写入文件"url"因为它会覆盖输入文件 这是tsconfig.json文件配置问题&#xff0c;需要加入下面的配置就好了&#xff1a; {"compilerOptions": {"outDir": …

python爬虫入门教程(一)

上一篇文章讲了爬虫的工作原理&#xff0c;这篇文章以后就要重点开始讲编程序了。 简单爬虫的的两个步骤&#xff1a; 使用HTTPRequest工具模拟HTTP请求&#xff0c;接收到返回的文本。用于请求的包有: requests、urllib等。 对接收的文本进行筛选,获取想要的内容。用户筛选文…

操作系统期末复习整理知识点

操作系统的概念&#xff1a;①控制和管理整个计算机系统的硬件和软件资源&#xff0c;并合理地组织调度计算机的工作和资源的分配&#xff1b;②提供给用户和其他软件方便的接口和环境&#xff1b;③是计算机中最基本的系统软件 功能和目标&#xff1a; ①操作系统作为系统资源…

专业场景化ChatGPT论文润色提示词指令,更精准、更有效辅助学术论文撰写

大家好&#xff0c;感谢关注。我是七哥&#xff0c;一个在高校里不务正业&#xff0c;折腾学术科研AI实操的学术人。可以添加我&#xff08;yida985&#xff09;交流学术写作或ChatGPT等AI领域相关问题&#xff0c;多多交流&#xff0c;相互成就&#xff0c;共同进步。 在学术写…

JVM类加载机制详解(JDK源码级别)

提示&#xff1a;从JDK源码级别彻底剖析JVM类加载机制、双亲委派机制、全盘负责委托机制、打破双亲委派机制的程序、Tomcat打破双亲委派机制、tomcat自定义类加载器详解、tomcat的几个主要类加载器、手写tomcat类加载器 文章目录 前言一、loadClass的类加载大概有如下步骤二、j…

Spring Boot通过自定义注解和Redis+Lua脚本实现接口限流

&#x1f604; 19年之后由于某些原因断更了三年&#xff0c;23年重新扬帆起航&#xff0c;推出更多优质博文&#xff0c;希望大家多多支持&#xff5e; &#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Mi…

【CS.AI】AI引领编程新时代:深度探索GitHub Copilot

文章目录 引言0. TOP TAKEAWAYS 重要要点1. Copilot的基本功能2. 技术原理3. 优势与局限优势局限 4. 使用体验4.1 初次使用4.2 在 JetBrains 全家桶中使用 GitHub Copilot1. 安装插件2. 配置插件3. 使用 GitHub Copilot 4.3 日常开发4.4 体验与反馈 5. 对开发者生态系统的影响5…

深度学习500问——Chapter10:迁移学习(1)

文章目录 11.1 迁移学习基础知识 11.1.1 什么是迁移学习 11.1.2 为什么需要迁移学习 11.1.3 迁移学习的基本问题有哪些 11.1.4 迁移学习有哪些常用概念 11.1.5 迁移学习与传统机器学习有什么区别 11.1.6 迁移学习的核心及度量准则 11.1.7 迁移学习与其他概念的区别 11.1.8 什么…

【设计模式】行为型设计模式之 状态模式,带你探究有限状态机FSM的三种实现方式

什么是有限状态机 Finite state Machine FSM 简称状态机&#xff1a;状态机由三部分组成&#xff0c;状态(State) 事件(Event) 和动作(Action)组成。 其中事件也被称为转移条件&#xff0c;事件触发状态的转移和动作的执行。不过动作不是必须的&#xff0c;也可能只存在状态转…

人工智能与能源约束的矛盾能否化解

以下文章来源&#xff1a;澎湃新闻 人工智能技术在台前展示的是比特世界的算力、算法和数据&#xff0c;但其“轻盈的灵魂”背后则是土地、能源和水等物理世界“沉重的肉身”。根据本文三种情境的模拟测算&#xff0c;未来人工智能发展需要可持续的巨量能源支撑&#xff0c;能源…