【Three.js】创建CAD标注线

目录

📖前言

🐞创建箭头对象

🐬创建文字

👻箭头两端的线段

✈️封装方法


📖前言

CAD标注线在工程和制造领域中被广泛用于标记零部件、装配体和机械系统的尺寸、距离、角度等信息。它们帮助工程师和设计师更好地理解设计要求,并确保制造的准确性。在三维场景中添加标注线使得设计更加直观。人们可以在一个真实的三维环境中看到物体的形状、大小和相互关系,相比于传统的二维图纸,更容易理解和把握设计意图。

下面是一个简单的效果图:

要创建上图所示的标注线,我们可以把标注线拆分成三个部分:箭头线、箭头两端的线段 、文本信息。

🐞创建箭头对象

threejs中可以使用ArrowHelper创建模拟方向的三维箭头对象,但是这个只能创建一个单向的箭头,我们需要创建两个相反方向的箭头对象。

ArrowHelper由一个三维坐标点和一个单位向量来确定其位置和方向。

startPoint和endPoint分别是标注线的起点和终点坐标,通过起点和终点可以得出方向向量和长度,取两点的中点作为箭头的起点坐标,就可以绘制两个方向相反的箭头。

const startPoint = new THREE.Vector3(8, 8, 0)
const endPoint= new THREE.Vector3(8, -8, 0)
const color = 0x666666
const headLength = 1
const headWidth = 0.6
// 中点位置
const centerPoint = startPoint.clone().add(endPoint).multiplyScalar(0.5)
// 标注线长度(总长的一半)
const halfLength = startPoint.distanceTo(endPoint) / 2
// 方向向量
const dir0 = startPoint.clone().sub(endPoint).normalize()  //normalize() 归一化操作,使其成为单位向量
const dir1 = dir0.clone().negate() //取反

const arrowHelper0 = new THREE.ArrowHelper(dir0, centerPoint, halfLength, color, headLength, headWidth)
const arrowHelper1 = new THREE.ArrowHelper(dir1, centerPoint, halfLength, color, headLength, headWidth)

scene.add(arrowHelper0, arrowHelper1)

🐬创建文字

 threejs中可以使用TextGeometry类将文本生成为单一的几何体。

这里需要导入字体文件才能加载文本几何体,threejs自带了一些字体文件,存放在 /examples/fonts/ 路径下,可以直接导入使用。但是threejs自带的字体文件不支持中文,如果需要显示中文字体,需要另外寻找字体文件,然后可以通过 typeface.json 将字体文件转成json格式的文件。

🐬导入字体、FontLoader加载器、TextGeometry类

import gentilisRegular from 'three/examples/fonts/gentilis_regular.typeface.json'
import { FontLoader } from 'three/examples/jsm/loaders/FontLoader.js'
import { TextGeometry } from 'three/examples/jsm/geometries/TextGeometry.js'

🐬使用FontLoader加载器加载字体,并调用parse方法解析JSON格式的对象:

const font = new FontLoader().parse(gentilisRegular)

🐬使用TextGeometry生成字体:

const geometry = new TextGeometry('16m', {
  font: font,
  size: 1,
  height: 0.5,
})
//添加材质
const material = new THREE.MeshBasicMaterial({
  color: 0x666666,
}) 
const textLabel = new THREE.Mesh(geometry, material)
scene.add(textLabel)

🐬最后就是要把字体放到合适的位置,上面我们已经计算得出了两个箭头对象的中点位置,把文字位置放到centerPoint位置:

textLabel.position.copy(centerPoint)

🐬但是文字默认是沿x轴正方向排列的,我们需要让文字沿线的方向排列,让文字和线平行:

// 创建四元数  从x轴正方向旋转到方向向量 dir0 所需的旋转角度
const quaternion = new THREE.Quaternion().setFromUnitVectors(new THREE.Vector3(1, 0, 0), dir0) 
// 将字体的旋转设为四元数的旋转
textLabel.setRotationFromQuaternion(quaternion)

🐬但是,此时会发现字体并没有完全居中,字体的初始位置处在了中点位置,而我们需要的是整个文本的中心处在线的中心,因此需要计算文本长度,并将其平移:

geometry.computeBoundingBox()
textLabel.position.sub(
  dir0.clone().multiplyScalar(geometry.boundingBox.getSize(new THREE.Vector3()).x / 2)
)

🐬如果不想字体和线过于贴合,可以计算线段的法向量,然后沿法向量方向平移:

// 法向量
const normalVector = new THREE.Vector3().crossVectors(dir0, new THREE.Vector3(0, 0, 1)).normalize()
textLabel.position.sub(normalVector.clone().multiplyScalar(0.5))

👻箭头两端的线段

我们已经知道了箭头线的方向向量,那我们可以在这个方向上创建两条线段,然后将它们旋转90°,最后再分别将它们放到startPoint和endPoint位置上即可。

// 创建线段的材质
const material = new THREE.LineBasicMaterial({ 
  color: 0x666666 ,
  linewidth: 1
})
// 创建线段的几何体
const geometry = new THREE.BufferGeometry().setFromPoints([
  new THREE.Vector3(0, 0, 0).clone().add(dir0.clone().multiplyScalar(2)),
  new THREE.Vector3(0, 0, 0).clone().sub(dir1.clone().multiplyScalar(2))
])
const line = new THREE.LineSegments(geometry, material)

const line0 = line.clone()
const line1 = line.clone()

line0.rotateZ(Math.PI / 2)
line1.rotateZ(Math.PI / 2)
line0.position.copy(item.startPoint)
line1.position.copy(item.endPoint)

scene.add(line0, line1)

上面创建出来的线段可能会和物体连接不上,一个简单的方法就是创建一条长一点的线段,但是这样可能会使线段的另一端长出很多,显得不美观,所以我们可以通过平移线段的方法,让线段和物体能够连接上:

// 深度克隆
const line0 = new THREE.Line(line.geometry.clone(), line.material.clone())
const line1 = new THREE.Line(line.geometry.clone(), line.material.clone())

// 获取线段的顶点属性
const startPositions = line0.geometry.attributes.position.array
// 分别将线段的两个端点沿着方向向量平移
startPositions[0] += direction.x * 1.5
startPositions[1] += direction.y * 1.5
startPositions[2] += direction.z * 1.5
startPositions[3] += direction.x * 1.5
startPositions[4] += direction.y * 1.5
startPositions[5] += direction.z * 1.5
// 更新线段的几何体
line0.geometry.attributes.position.needsUpdate = true

🎯这里使用深度克隆,可以避免其中一条线修改影响到另一条线。

✈️封装方法

dimLine.js

import * as THREE from 'three'
import { FontLoader } from 'three/examples/jsm/loaders/FontLoader.js'
import { TextGeometry } from 'three/examples/jsm/geometries/TextGeometry.js'
import gentilisRegular from 'three/examples/fonts/gentilis_regular.typeface.json'

export const useDimLine = () => {
	// 创建实线
	const createSoildLine = (data) => {
		const {startPoint, endPoint, color, linewidth} = {...data}
		// 创建线段的材质
		const material = new THREE.LineBasicMaterial({ 
			color: color ,
			linewidth: linewidth || 1
		})
		// 创建线段的几何体
		const geometry = new THREE.BufferGeometry().setFromPoints([startPoint, endPoint])
		const line = new THREE.LineSegments(geometry, material)

		return {
			line,
			material,
			geometry
		}
	}
	
	// 创建文字
	const createText = (data) => {
		// data = {
		// 	text,
		// 	position,
		// 	size,
		// 	height,
		// 	color
		//  direction
		// }
		const font = new FontLoader().parse(gentilisRegular)
		let geometrys = []
		let materials = []
		let textLabels = []
		data.map(item => {
			if(item.text === ''){
				return
			}
			const geometry = new TextGeometry(item.text, {
				font: font,
				size: item.size,
				height: item.height,
			})
			const material = new THREE.MeshBasicMaterial({
				color: item.color,
			}) 
			const textLabel = new THREE.Mesh(geometry, material)
			textLabel.position.copy(item.position)

			item.direction = item.direction ? item.direction : new THREE.Vector3(1, 0, 0)

			// 使用线的方向向量创建四元数  从方向向量 new THREE.Vector3(1, 0, 0) 旋转到方向向量 dir1 所需的旋转
			const quaternion = new THREE.Quaternion().setFromUnitVectors(new THREE.Vector3(1, 0, 0), item.direction) 
			// 将字体的旋转设为四元数的旋转
			textLabel.setRotationFromQuaternion(quaternion)
			// 将字体完全居中
			geometry.computeBoundingBox()
			textLabel.position.sub(
				item.direction.clone().multiplyScalar(geometry.boundingBox.getSize(new THREE.Vector3()).x / 2)
			)
			// 方向向量法向量
			const normalVector = new THREE.Vector3().crossVectors(item.direction, new THREE.Vector3(0, 0, 1)).normalize()
			textLabel.position.sub(normalVector.clone().multiplyScalar(item.paddingBottom || 0.1))
			
			geometrys.push(geometry)
			textLabels.push(textLabel)
			materials.push(material)
		})
		
		return {
			geometrys, 
			textLabels,
			materials
		}
	}
	
	// 创建标注线
	const createDimLine = (data) => {
		// data = [
		// 	{
		// 		type: 'arrow', //'type'
		// 		startPoint: new THREE.Vector3(8, 8, 0),
		// 		endPoint: new THREE.Vector3(8, -8, 0),
		// 		color: "#bd2fa7",
		// 		headLength: 1, //箭头长
		// 		headWidth: 0.6, //箭头宽
		// 		textObj: {
		// 			text: '57m',
		// 			size: 1,
		// 			height: 0.5,
		// 			color: '#bd2fa7',
		// 			paddingBottom: 0.1, //文字和线的距离
		// 		},
		// 		// 两端的线段长度
		// 		closeLine: {
		// 			startLength: 10, //distance+2 = 8+2 = 10 线总长
		// 			endLength: 4,
		// 			translateStart: -3,// startLength/2-distance = 5-8 = -3 平移距离
		// 			translateEnd: 0,
		// 		}
		// 	}
		// ]
		const group = new THREE.Group()
		let geometrys = []
		let materials = []
		data.map(item => {
			// 中点位置
      const centerPoint = item.startPoint.clone().add(item.endPoint).multiplyScalar(0.5)
      // 标注线长度(一半)
      const halfLength = item.startPoint.distanceTo(item.endPoint) / 2
      // 方向向量 2个
      const dir0 = item.startPoint.clone().sub(item.endPoint).normalize()  //normalize() 归一化操作,使其成为单位向量
      const dir1 = dir0.clone().negate() //取反
      
      const arrowHelper0 = new THREE.ArrowHelper(dir0, centerPoint, halfLength, item.color, item.headLength, item.headWidth)
      const arrowHelper1 = new THREE.ArrowHelper(dir1, centerPoint, halfLength, item.color, item.headLength, item.headWidth)
      
      const direction = item.startPoint.x <= item.endPoint.x ? dir1 : dir0
      const textLabelObj = createText([
        {
          ...item.textObj,
          position: centerPoint,
          direction
        }
      ])
      
      // 标注线两端的线段
      if(item.closeLine.startLength > 0){
        // 创建两端线段
        const lineObj0 = createSoildLine({
          startPoint: new THREE.Vector3(0, 0, 0).clone().add(direction.clone().multiplyScalar(item.closeLine.startLength/2)), 
          endPoint: new THREE.Vector3(0, 0, 0).clone().sub(direction.clone().multiplyScalar(item.closeLine.startLength/2)), 
          color: item.color, 
          linewidth: 1
        })
        const line0 = lineObj0.line
      
        // 获取线段的顶点属性
        const startPositions = line0.geometry.attributes.position.array
        // 分别将线段的两个端点沿着方向向量平移
        startPositions[0] += direction.x * item.closeLine.translateStart
        startPositions[1] += direction.y * item.closeLine.translateStart
        startPositions[2] += direction.z * item.closeLine.translateStart
        startPositions[3] += direction.x * item.closeLine.translateStart
        startPositions[4] += direction.y * item.closeLine.translateStart
        startPositions[5] += direction.z * item.closeLine.translateStart
        // 更新线段的几何体
        line0.geometry.attributes.position.needsUpdate = true
        
        line0.rotateZ(Math.PI / 2)
        line0.position.copy(item.startPoint)
        
        group.add(line0)
        geometrys.push(lineObj0.geometry)
        materials.push(lineObj0.material)
      }	
      if(item.closeLine.endLength > 0){
        // 创建两端线段
        const lineObj1 = createSoildLine({
          startPoint: new THREE.Vector3(0, 0, 0).clone().add(direction.clone().multiplyScalar(item.closeLine.endLength/2)), 
          endPoint: new THREE.Vector3(0, 0, 0).clone().sub(direction.clone().multiplyScalar(item.closeLine.endLength/2)), 
          color: item.color, 
          linewidth: 1
        })
        const line1 = lineObj1.line
        
        // 获取线段的顶点属性
        const endPositions = line1.geometry.attributes.position.array
        // 分别将线段的两个端点沿着方向向量平移
        endPositions[0] += direction.x * item.closeLine.translateEnd
        endPositions[1] += direction.y * item.closeLine.translateEnd
        endPositions[2] += direction.z * item.closeLine.translateEnd
        endPositions[3] += direction.x * item.closeLine.translateEnd
        endPositions[4] += direction.y * item.closeLine.translateEnd
        endPositions[5] += direction.z * item.closeLine.translateEnd
        // 更新线段的几何体
        line1.geometry.attributes.position.needsUpdate = true
        line1.rotateZ(Math.PI / 2)
        line1.position.copy(item.endPoint)
        
        group.add(line1)
        geometrys.push(lineObj1.geometry)
        materials.push(lineObj1.material)
      }

      group.add(arrowHelper0, arrowHelper1, textLabelObj.textLabels[0])
      geometrys.push(textLabelObj.geometrys[0])
      materials.push(textLabelObj.materials[0])
			
		})
		
		return {
			group,
			geometrys,
			materials
		}
	}

	return {
    createSoildLine,
    createText,
		createDimLine,
	}
}

使用:

//导入
import { useDimLine } from './dimLine.js'
const { createDimLine } = useDimLine()

//使用
const wireDim = createDimLine([
{
  startPoint: new THREE.Vector3(8, 8, 0),//起点
  endPoint: new THREE.Vector3(8, -8, 0),//终点
  color: "#666666",//线条颜色
  headLength: 1,//箭头长度
  headWidth: 0.6,//箭头宽度
  textObj: {
    text: '16m',//标注文本
    size: 1,//文本大小
    height: 0.5,//文本厚度
    color: '#666666',//文本颜色
    paddingBottom: 0.5,//文本和线的距离
  },
  // 两端的线段长度
  closeLine: {
    startLength: 10, //起点端的线段长度
    endLength: 4,//终点端的线段长度
    translateStart: -3,// 平移距离startLength/2-distance = 5-8 = -3
    translateEnd: 0,
  }
},
{
  startPoint: new THREE.Vector3(-6, -12, 0),
  endPoint: new THREE.Vector3(6, -12, 0),
  color: "#666666",
  headLength: 1,
  headWidth: 0.6,
  textObj: {
    text: '12m',
    size: 1,
    height: 0.5,
    color: '#666666',
    paddingBottom: 0.5,
  },
  // 两端的线段长度
  closeLine: {
    startLength: 5,
    endLength: 5,
    translateStart: 1.5,
    translateEnd: 1.5,
  }
},
])

scene.add(wireDim.group)

参数解释: 

完整代码:

<template>
  <div id="three"></div>
</template>

<script setup>
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { onMounted } from 'vue'
import { useDimLine } from './dimLine.js'

const { createDimLine } = useDimLine()

const initThree = (domContainer, cameraPos) => {
  const renderer = new THREE.WebGLRenderer({ 
    antialias: true, //抗锯齿
    alpha: true
  })
  const scene = new THREE.Scene()
  const camera = new THREE.PerspectiveCamera(75, domContainer?.offsetWidth / domContainer?.offsetHeight, 0.01, 10000) //透视视角
  const controls = new OrbitControls(camera, renderer.domElement) //创建控件对象
  camera.position.copy(cameraPos)
  camera.lookAt(new THREE.Vector3(0, 0, 0))
  scene.add(camera)
  // 设置背景颜色 0表示透明
  renderer.setClearColor(0xffffff, 0)
  renderer.setSize(domContainer?.offsetWidth, domContainer?.offsetHeight)
  controls.update()
  // 将webgl渲染的canvas内容添加到domContainer
  domContainer?.appendChild(renderer.domElement)
  const animate = () => {
    renderer.render(scene, camera)
    // 请求下一帧
    requestAnimationFrame(animate)
  }
  animate()
  return scene
}

onMounted(() => {
  const scene = initThree(document.getElementById('three'), new THREE.Vector3(0, 0, 50))

  const geometry = new THREE.ConeGeometry( 6, 16, 32 )
  const material = new THREE.MeshBasicMaterial( {color: 0xe3ab9a} )
  const cone = new THREE.Mesh( geometry, material )

  const wireDim = createDimLine([
    {
      startPoint: new THREE.Vector3(8, 8, 0),
      endPoint: new THREE.Vector3(8, -8, 0),
      color: "#666666",
      headLength: 1,
      headWidth: 0.6,
      textObj: {
        text: '16m',
        size: 1,
        height: 0.5,
        color: '#666666',
        paddingBottom: 0.5,
      },
      // 两端的线段长度
      closeLine: {
        startLength: 10, //distance+2 = 8+2 = 10
        endLength: 4,
        translateStart: -3,// startLength/2-distance = 5-8 = -3
        translateEnd: 0,
      }
    },
    {
      startPoint: new THREE.Vector3(-6, -12, 0),
      endPoint: new THREE.Vector3(6, -12, 0),
      color: "#666666",
      headLength: 1,
      headWidth: 0.6,
      textObj: {
        text: '12m',
        size: 1,
        height: 0.5,
        color: '#666666',
        paddingBottom: 0.5,
      },
      // 两端的线段长度
      closeLine: {
        startLength: 5,
        endLength: 5,
        translateStart: 1.5,
        translateEnd: 1.5,
      }
    },
  ])

  scene.add(cone, wireDim.group)
})

</script>

<style lang="scss" scoped>
	#three{
    width: 100vw;
    height: 100vh;
	}
</style>

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

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

相关文章

web自动化 -- selenium及应用

selenium简介 随着互联网的发展&#xff0c;前端技术不断变化&#xff0c;数据加载方式也不再是通过服务端渲染。现在许多网站使用接口或JSON数据通过JavaScript进行渲染。因此&#xff0c;使用requests来爬取内容已经不再适用&#xff0c;因为它只能获取服务器端网页的源码&am…

计算机网络体系的形成

目录 1、开放系统互连参考模型OSI/RM 2、两种国际标准 3、协议与划分层次 4、网络协议的三要素 5、划分层次 &#xff08;1&#xff09;文件发送模块使两个主机交换文件 &#xff08;2&#xff09;通信服务模块 &#xff08;3&#xff09;接入网络模块 6、分层带来的好…

Linux下Python调用C语言

一&#xff1a;Python调用C语言场景 1&#xff0c;已经写好的C语言代码&#xff0c;不容易用Python实现&#xff0c;想直接通过Python调用写好的C语言代码 2&#xff0c;C比Python快&#xff08;只是从语言层面&#xff0c;不能绝对说C程序就是比Python快&#xff09; 3&…

docker配置redis插件

docker配置redis插件 运行容器redis_6390 docker run -it \ --name redis_6390 \ --privileged \ -p 6390:6379 \ --network wn_docker_net \ --ip 172.18.12.19 \ --sysctl net.core.somaxconn1024 \ -e TIME_ZONE"Asia/Shanghai" -e TZ"Asia/Shanghai"…

电磁兼容EMC理论基础汇总

目录 0. 序言 1. EMC的基础介绍 1.1 EMC电磁兼容的定义 1.2 EMC的重要性 1.3 EMC的三要素 2. 库仑定律 3. 趋肤效应与趋肤深度 4. 电阻抗公式 4.1 电阻 4.2 容抗 4.3 感抗 4.4 电路元件的非理想性 5. 麦克斯韦方程组 5.1 高斯磁定律 5.2 高斯定律 5.3 法拉…

【FPGA】Verilog:二进制并行加法器 | 超前进位 | 实现 4 位二进制并行加法器和减法器 | MSI/LSI 运算电路

Ⅰ. 前置知识 0x00 并行加法器和减法器 如果我们要对 4 位加法器和减法器进行关于二进制并行运算功能&#xff0c;可以通过将加法器和减法器以 N 个并行连接的方式&#xff0c;创建一个执行 N 位加法和减法运算的电路。 4 位二进制并行加法器 4 位二进制并行减法器 换…

YoloV8改进策略:Swift Parameter-free Attention,无参注意力机制,超分模型的完美迁移

摘要 https://arxiv.org/pdf/2311.12770.pdf https://github.com/hongyuanyu/SPAN SPAN是一种超分网络模型。SPAN模型通过使用参数自由的注意力机制来提高SISR的性能。这种注意力机制能够增强重要信息并减少冗余,从而在图像超分辨率过程中提高图像质量。 具体来说,SPAN模…

J-Link RTT的使用(原理 + 教程 + 应用 + 代码)

MCU:STM32F407VE MDK:5.29 IAR:8.32 目录--点击可快速直达 目录 写在前面什么是RTT?RTT的工作原理RTT的性能快速使用教程高级使用教程附上测试代码2019年12月27日更新--增加打印float的功能 写在前面 本文介绍了J-Link RTT的部分使用内容&#xff0c;很多地方参考和使用…

ImportError: cannot import name ‘metadata‘ from ‘importlib‘

yolov8 编译问题 ImportError: cannot import name ‘metadata’ from ‘importlib’ 将 from importlib import metadata 更改为 import importlib_metadata as metadata

canvas绘制单个圆和同心圆

代码实现&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdev…

【Java 基础】18 I/O流

文章目录 1.基本概念2.字节流3.字符流4.标准输入输出5.最佳实践 I/O流&#xff08;Input/Output 流&#xff09;是计算机程序中不可或缺的一部分&#xff0c; 往大了说所有的操作都是IO。Java 提供了强大而灵活的 I/O 框架&#xff0c;支持各种数据的 读取和 写入操作。 1.基…

网络安全--网络环境构成,系统的安全

2. 网络攻防环境 目标 了解攻防环境构成了解入侵检测系统&#xff08;平台&#xff09;的部署位置 2.1. 环境构成 2.1.1. 环境框图 一个基本的网络攻防实验环境包括&#xff1a;靶机、攻击机、入侵检测分析系统、网络连接四部分组成。 一个基础的网络攻防实验环境需要如下…

Mybatis 操作续集(结合上文)

当我们增加一个数据之后,如果我们想要获取它的 Id 进行别的操作,我们该如何获取 Id 呢? 用那个Options package com.example.mybatisdemo.mapper;import com.example.mybatisdemo.model.UserInfo; import org.apache.ibatis.annotations.*;import java.util.List;Mapper pub…

Leetcode刷题详解——乘积为正数的最长子数组长度

1. 题目链接&#xff1a;1567. 乘积为正数的最长子数组长度 2. 题目描述&#xff1a; 给你一个整数数组 nums &#xff0c;请你求出乘积为正数的最长子数组的长度。 一个数组的子数组是由原数组中零个或者更多个连续数字组成的数组。 请你返回乘积为正数的最长子数组长度。 示…

java面向对象 + 内存解析

这篇博客主要是重点讲解一些内存和一些规定的解释&#xff0c;对于定义我不会过多赘述&#xff0c;没有Java基础的话可以去翻看我之前的博客&#xff0c;学习完成之后再进行阅读。 面向对象可以说是Java中最重要的一部分了&#xff0c;这次复习我发现有几个点比较重要&#xf…

【Rust日报】2023-12-02 深度学习框架 Burn 发布 v0.11.0

深度学习框架 Burn 发布 v0.11.0 深度学习框架 Burn 发布 v0.11.0&#xff0c;新版本引入了自动内核融合&#xff08;Kernel Fusion&#xff09;功能&#xff0c;大大提升了访存密集型&#xff08;memory-bound&#xff09;操作的性能。同时宣布成立 Tracel AI (https://tracel…

深入理解 Kafka 集群搭建与管理

Apache Kafka 作为分布式流处理平台的核心&#xff0c;其集群搭建与管理是确保高可用性和高性能的关键。本文将深入研究 Kafka 集群的构建、配置、工作原理、节点角色以及一些高级管理策略&#xff0c;以助力读者更深层次地理解和灵活运用 Kafka 集群。 Kafka 集群基础 1 集群…

PHP开源问答网站平台源码系统 源码全部开源可二次开发 附带完整的搭建教程

目前&#xff0c;问答网站已经成为人们获取知识、交流思想的重要平台。然而&#xff0c;对于许多开发者来说&#xff0c;从头开始构建一个问答网站可能会面临各种挑战。今天&#xff0c;小编给大家介绍一款基于PHP的开源问答网站平台源码系统&#xff0c;它不仅源码全部开源&am…

HR看好的字符函数和字符串处理函数!!!

本篇会加入个人的所谓‘鱼式疯言’❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言,而是理解过并总结出来通俗易懂的大白话,我会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的&#xff0c;可能说的不是那么严谨.但小编初心是能让更多人能接受我们这个概念 前言 在本篇…

基于maobox-gl 纯前端绘制全球色斑图

基于maobox-gl.js、turf.js 绘制全球色斑图绘制 1、准备全球的某一类的点位数据&#xff0c;可以使用turf.js 随机生成点&#xff0c;并点数据赋properties属性 let points turf.randomPoint(30, { bbox: [-180, -90, 180, 90]}); let interpolateOptions {gridType: "…