图形裁剪算法
1.学习目标
理解区域编码(Region Code,RC)
设计Cohen-Sutherland直线裁剪算法
编程实现Cohen-Sutherland直线裁剪算法
2.具体代码
-
1.具体算法
/*** Cohen-Sutherland直线裁剪算法 - 优化版* @author AI Assistant* @license MIT*/// 区域编码常量 - 使用对象枚举以增强可读性
const RegionCode = Object.freeze({INSIDE: 0, // 0000LEFT: 1, // 0001RIGHT: 2, // 0010BOTTOM: 4, // 0100TOP: 8 // 1000
});/*** 计算点的区域编码* @param {number} x - 点的x坐标* @param {number} y - 点的y坐标* @param {Object} clipWindow - 裁剪窗口* @returns {number} - 区域编码*/
function computeCode(x, y, clipWindow) {const { xmin, ymin, xmax, ymax } = clipWindow;// 使用位运算计算区域编码let code = RegionCode.INSIDE;// 左/右测试if (x < xmin) {code |= RegionCode.LEFT;} else if (x > xmax) {code |= RegionCode.RIGHT;}// 下/上测试if (y < ymin) {code |= RegionCode.BOTTOM;} else if (y > ymax) {code |= RegionCode.TOP;}return code;
}/*** 计算线段与裁剪窗口边界的交点* @param {number} code - 端点的区域编码* @param {Point} p1 - 线段起点* @param {Point} p2 - 线段终点* @param {Object} clipWindow - 裁剪窗口* @returns {Point} - 交点坐标*/
function computeIntersection(code, p1, p2, clipWindow) {const { xmin, ymin, xmax, ymax } = clipWindow;const { x: x1, y: y1 } = p1;const { x: x2, y: y2 } = p2;let x, y;// 根据区域编码确定交点if ((code & RegionCode.TOP) !== 0) {// 与上边界相交x = x1 + (x2 - x1) * (ymax - y1) / (y2 - y1);y = ymax;} else if ((code & RegionCode.BOTTOM) !== 0) {// 与下边界相交x = x1 + (x2 - x1) * (ymin - y1) / (y2 - y1);y = ymin;} else if ((code & RegionCode.RIGHT) !== 0) {// 与右边界相交y = y1 + (y2 - y1) * (xmax - x1) / (x2 - x1);x = xmax;} else if ((code & RegionCode.LEFT) !== 0) {// 与左边界相交y = y1 + (y2 - y1) * (xmin - x1) / (x2 - x1);x = xmin;}return { x, y };
}/*** Cohen-Sutherland直线裁剪算法* @param {Point} p1 - 线段起点 {x, y}* @param {Point} p2 - 线段终点 {x, y}* @param {Object} clipWindow - 裁剪窗口 {xmin, ymin, xmax, ymax}* @returns {Object|null} - 裁剪后的线段坐标,如果线段完全在窗口外则返回null*/
function cohenSutherlandClip(p1, p2, clipWindow) {// 创建点的副本,避免修改原始数据let point1 = { ...p1 };let point2 = { ...p2 };// 计算端点的区域编码let code1 = computeCode(point1.x, point1.y, clipWindow);let code2 = computeCode(point2.x, point2.y, clipWindow);let isAccepted = false;// 主循环while (true) {// 情况1: 两端点都在裁剪窗口内if ((code1 | code2) === 0) {isAccepted = true;break;}// 情况2: 两端点都在裁剪窗口外的同一区域else if ((code1 & code2) !== 0) {break;}// 情况3: 线段部分在裁剪窗口内,需要裁剪else {// 选择一个在窗口外的端点const outCode = code1 !== 0 ? code1 : code2;// 计算交点const intersection = computeIntersection(outCode, point1, point2, clipWindow);// 更新端点和区域编码if (outCode === code1) {point1 = intersection;code1 = computeCode(point1.x, point1.y, clipWindow);} else {point2 = intersection;code2 = computeCode(point2.x, point2.y, clipWindow);}}}// 返回裁剪结果return isAccepted ? {x1: point1.x,y1: point1.y,x2: point2.x,y2: point2.y} : null;
}/*** 绘制裁剪窗口* @param {CanvasRenderingContext2D} ctx - Canvas上下文* @param {Object} clipWindow - 裁剪窗口* @param {Object} style - 绘制样式*/
function drawClipWindow(ctx, clipWindow, style = {}) {const { xmin, ymin, xmax, ymax } = clipWindow;const { strokeStyle = 'blue', lineWidth = 2,fillStyle = 'rgba(200, 220, 255, 0.1)'} = style;ctx.save();// 设置样式ctx.strokeStyle = strokeStyle;ctx.lineWidth = lineWidth;ctx.fillStyle = fillStyle;// 绘制填充矩形ctx.fillRect(xmin, ymin, xmax - xmin, ymax - ymin);// 绘制边框ctx.strokeRect(xmin, ymin, xmax - xmin, ymax - ymin);// 绘制区域标签ctx.font = '12px Arial';ctx.fillStyle = 'rgba(0, 0, 100, 0.7)';ctx.textAlign = 'center';// 标记窗口四角的区域编码const padding = 15;ctx.fillText('1001', xmin - padding, ymin - padding); // 左上ctx.fillText('1010', xmax + padding, ymin - padding); // 右上ctx.fillText('0101', xmin - padding, ymax + padding); // 左下ctx.fillText('0110', xmax + padding, ymax + padding); // 右下ctx.restore();
}/*** 绘制线段* @param {CanvasRenderingContext2D} ctx - Canvas上下文* @param {Object} line - 线段数据* @param {Object} style - 绘制样式*/
function drawLine(ctx, line, style = {}) {const { x1, y1, x2, y2 } = line;const { strokeStyle = 'red', lineWidth = 1.5,drawEndpoints = false,endpointRadius = 4,dashPattern = []} = style;ctx.save();// 设置样式ctx.strokeStyle = strokeStyle;ctx.lineWidth = lineWidth;if (dashPattern.length > 0) {ctx.setLineDash(dashPattern);}// 绘制线段ctx.beginPath();ctx.moveTo(x1, y1);ctx.lineTo(x2, y2);ctx.stroke();// 绘制端点if (drawEndpoints) {ctx.fillStyle = strokeStyle;// 起点ctx.beginPath();ctx.arc(x1, y1, endpointRadius, 0, Math.PI * 2);ctx.fill();// 终点ctx.beginPath();ctx.arc(x2, y2, endpointRadius, 0, Math.PI * 2);ctx.fill();}ctx.restore();
}/*** 获取线段区域编码的文本描述* @param {number} code - 区域编码* @returns {string} - 编码的二进制表示*/
function getRegionCodeText(code) {// 将编码转换为4位二进制字符串return (code | 0).toString(2).padStart(4, '0');
}// 导出所有函数和常量
export {RegionCode,computeCode,cohenSutherlandClip,drawClipWindow,drawLine,getRegionCodeText
};
-
2.服务器配置
const http = require('http');
const fs = require('fs');
const path = require('path');const PORT = 3000;// MIME类型映射
const mimeTypes = {'.html': 'text/html','.js': 'text/javascript','.css': 'text/css','.json': 'application/json','.png': 'image/png','.jpg': 'image/jpeg','.gif': 'image/gif','.svg': 'image/svg+xml','.ico': 'image/x-icon'
};// 创建HTTP服务器
const server = http.createServer((req, res) => {console.log(`请求: ${req.url}`);// 处理主页请求let filePath = '.' + req.url;if (filePath === './') {filePath = './cohenSutherlandDemo.html';}// 获取文件扩展名const extname = path.extname(filePath);// 设置默认的MIME类型let contentType = mimeTypes[extname] || 'application/octet-stream';// 读取文件fs.readFile(filePath, (err, content) => {if (err) {if (err.code === 'ENOENT') {// 文件未找到res.writeHead(404);res.end('404 Not Found');} else {// 服务器错误res.writeHead(500);res.end(`Server Error: ${err.code}`);}} else {// 成功响应// 添加正确的CORS头部,以允许ES模块加载res.writeHead(200, { 'Content-Type': contentType,'Access-Control-Allow-Origin': '*'});res.end(content, 'utf-8');}});
});// 启动服务器
server.listen(PORT, () => {console.log(`服务器运行在 http://localhost:${PORT}/`);console.log('请使用浏览器访问上述地址查看Cohen-Sutherland算法演示');
});
-
3.HTML前端页面
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Cohen-Sutherland直线裁剪算法演示</title><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"><style>:root {--primary-color: #3f51b5;--secondary-color: #f50057;--success-color: #4caf50;--bg-color: #f8f9fa;--canvas-bg: #ffffff;}body {font-family: 'Microsoft YaHei', 'PingFang SC', sans-serif;background-color: var(--bg-color);margin: 0;padding: 20px;color: #333;}.container {max-width: 1000px;margin: 0 auto;background-color: #fff;border-radius: 10px;box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);padding: 20px;}h1 {color: var(--primary-color);text-align: center;margin-bottom: 20px;font-weight: bold;font-size: 2rem;}.canvas-container {position: relative;margin: 20px 0;border-radius: 8px;overflow: hidden;box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);}#canvas {display: block;background-color: var(--canvas-bg);width: 100%;height: 500px;cursor: default;}.controls {display: flex;flex-wrap: wrap;gap: 10px;margin-bottom: 20px;justify-content: center;}.btn-primary {background-color: var(--primary-color);border-color: var(--primary-color);}.btn-danger {background-color: var(--secondary-color);border-color: var(--secondary-color);}.btn-success {background-color: var(--success-color);border-color: var(--success-color);}.legend {background-color: rgba(255, 255, 255, 0.9);border-radius: 8px;padding: 15px;margin-top: 20px;box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);display: flex;flex-wrap: wrap;gap: 15px;}.legend-item {display: flex;align-items: center;margin-right: 15px;}.legend-color {width: 20px;height: 3px;margin-right: 8px;border-radius: 2px;}.legend-point {width: 8px;height: 8px;border-radius: 50%;margin-right: 8px;}.blue {background-color: var(--primary-color);}.red {background-color: var(--secondary-color);}.green {background-color: var(--success-color);}.status-bar {margin-top: 10px;padding: 10px;border-radius: 5px;background-color: #f5f5f5;font-family: monospace;min-height: 40px;}.point-info {display: flex;justify-content: space-between;flex-wrap: wrap;margin-top: 10px;}.info-card {background-color: #f5f5f5;border-radius: 5px;padding: 10px;width: 48%;margin-bottom: 10px;}.code-display {font-family: monospace;font-weight: bold;color: var(--primary-color);}footer {text-align: center;margin-top: 20px;font-size: 0.8rem;color: #666;}</style>
</head>
<body><div class="container"><h1>Cohen-Sutherland直线裁剪算法演示</h1><div class="controls"><button id="drawLineBtn" class="btn btn-primary"><i class="bi bi-pencil"></i> 绘制新线段</button><button id="clipBtn" class="btn btn-success"><i class="bi bi-scissors"></i> 裁剪线段</button><button id="resetBtn" class="btn btn-danger"><i class="bi bi-trash"></i> 重置</button><button id="toggleCodeBtn" class="btn btn-secondary"><i class="bi bi-code-slash"></i> 显示/隐藏区域编码</button></div><div class="canvas-container"><canvas id="canvas"></canvas></div><div class="status-bar" id="statusBar">准备就绪。点击"绘制新线段"按钮开始。</div><div class="point-info"><div class="info-card" id="point1Info"><h5>起点:</h5><p>坐标:<span id="p1Coords">-</span></p><p>区域编码:<span class="code-display" id="p1Code">-</span></p></div><div class="info-card" id="point2Info"><h5>终点:</h5><p>坐标:<span id="p2Coords">-</span></p><p>区域编码:<span class="code-display" id="p2Code">-</span></p></div></div><div class="legend"><div class="legend-item"><div class="legend-color blue"></div><span>裁剪窗口</span></div><div class="legend-item"><div class="legend-color red"></div><span>原始线段</span></div><div class="legend-item"><div class="legend-color green"></div><span>裁剪后的线段</span></div><div class="legend-item"><div class="legend-point red"></div><span>线段端点</span></div></div><footer>Cohen-Sutherland直线裁剪算法 © 2023</footer></div><script type="module">// 导入优化后的Cohen-Sutherland模块import { RegionCode, computeCode, cohenSutherlandClip,drawClipWindow,drawLine,getRegionCodeText} from './cohenSutherland.js';// DOM元素const canvas = document.getElementById('canvas');const statusBar = document.getElementById('statusBar');const p1CoordsElem = document.getElementById('p1Coords');const p2CoordsElem = document.getElementById('p2Coords');const p1CodeElem = document.getElementById('p1Code');const p2CodeElem = document.getElementById('p2Code');// 调整Canvas以适应容器大小function setupCanvas() {// 获取容器的宽度,高度固定为500pxconst containerWidth = canvas.parentElement.clientWidth;canvas.width = containerWidth;canvas.height = 500;}// 调用初始化setupCanvas();// 监听窗口大小变化,调整Canvaswindow.addEventListener('resize', setupCanvas);// 获取Canvas上下文const ctx = canvas.getContext('2d');// 裁剪窗口定义const clipWindow = {xmin: Math.round(canvas.width * 0.25),ymin: Math.round(canvas.height * 0.25),xmax: Math.round(canvas.width * 0.75),ymax: Math.round(canvas.height * 0.75)};// 状态变量let lines = [];let isDrawing = false;let startPoint = null;let selectedLine = null;let showRegionCodes = false;// 更新状态栏function updateStatus(message) {statusBar.textContent = message;}// 更新点信息function updatePointInfo(p1, p2) {if (p1) {p1CoordsElem.textContent = `(${Math.round(p1.x)}, ${Math.round(p1.y)})`;const code = computeCode(p1.x, p1.y, clipWindow);p1CodeElem.textContent = getRegionCodeText(code);} else {p1CoordsElem.textContent = '-';p1CodeElem.textContent = '-';}if (p2) {p2CoordsElem.textContent = `(${Math.round(p2.x)}, ${Math.round(p2.y)})`;const code = computeCode(p2.x, p2.y, clipWindow);p2CodeElem.textContent = getRegionCodeText(code);} else {p2CoordsElem.textContent = '-';p2CodeElem.textContent = '-';}}// 绘制所有元素function redraw() {ctx.clearRect(0, 0, canvas.width, canvas.height);// 绘制裁剪窗口drawClipWindow(ctx, clipWindow, {strokeStyle: '#3f51b5',lineWidth: 2,fillStyle: 'rgba(63, 81, 181, 0.05)'});// 绘制区域编码标记if (showRegionCodes) {// 绘制中心区域标记ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';ctx.font = '12px Arial';ctx.textAlign = 'center';ctx.textBaseline = 'middle';// 中心区域ctx.fillText('0000', (clipWindow.xmin + clipWindow.xmax) / 2, (clipWindow.ymin + clipWindow.ymax) / 2);// 上方区域ctx.fillText('1000', (clipWindow.xmin + clipWindow.xmax) / 2, clipWindow.ymin / 2);// 下方区域ctx.fillText('0100', (clipWindow.xmin + clipWindow.xmax) / 2, (clipWindow.ymax + canvas.height) / 2);// 左方区域ctx.fillText('0001', clipWindow.xmin / 2, (clipWindow.ymin + clipWindow.ymax) / 2);// 右方区域ctx.fillText('0010', (clipWindow.xmax + canvas.width) / 2, (clipWindow.ymin + clipWindow.ymax) / 2);}// 绘制所有线段for (const line of lines) {// 原始线段drawLine(ctx, line, {strokeStyle: '#f50057',lineWidth: 1.5,drawEndpoints: true,endpointRadius: 4});// 如果有裁剪结果,绘制裁剪后的线段if (line.clipped) {drawLine(ctx, line.clipped, {strokeStyle: '#4caf50',lineWidth: 2.5,dashPattern: [],drawEndpoints: true,endpointRadius: 4});}}// 如果正在绘制,显示预览线段if (isDrawing && startPoint) {const mousePos = canvas.mousePosition || { x: 0, y: 0 };drawLine(ctx, {x1: startPoint.x,y1: startPoint.y,x2: mousePos.x,y2: mousePos.y}, {strokeStyle: 'rgba(245, 0, 87, 0.5)',lineWidth: 1.5,dashPattern: [5, 3],drawEndpoints: true});}}// 为所有线段应用裁剪算法function clipAllLines() {if (lines.length === 0) {updateStatus('没有线段可裁剪!');return;}for (const line of lines) {// 使用优化后的接口调用裁剪算法line.clipped = cohenSutherlandClip({ x: line.x1, y: line.y1 },{ x: line.x2, y: line.y2 },clipWindow);}redraw();updateStatus(`已完成${lines.length}条线段的裁剪。`);}// 绑定按钮事件document.getElementById('drawLineBtn').addEventListener('click', function() {if (isDrawing) {isDrawing = false;startPoint = null;canvas.style.cursor = 'default';updateStatus('取消绘制线段。');} else {isDrawing = true;startPoint = null;canvas.style.cursor = 'crosshair';updateStatus('请点击绘制线段的起点...');}});document.getElementById('clipBtn').addEventListener('click', function() {clipAllLines();});document.getElementById('resetBtn').addEventListener('click', function() {lines = [];isDrawing = false;startPoint = null;selectedLine = null;canvas.style.cursor = 'default';updatePointInfo(null, null);updateStatus('已重置。点击"绘制新线段"按钮开始。');redraw();});document.getElementById('toggleCodeBtn').addEventListener('click', function() {showRegionCodes = !showRegionCodes;redraw();updateStatus(showRegionCodes ? '显示区域编码。' : '隐藏区域编码。');});// 跟踪鼠标位置canvas.addEventListener('mousemove', function(e) {const rect = canvas.getBoundingClientRect();canvas.mousePosition = {x: e.clientX - rect.left,y: e.clientY - rect.top};// 如果正在绘制,更新预览if (isDrawing && startPoint) {redraw();}});// 处理鼠标点击canvas.addEventListener('mousedown', function(e) {if (!isDrawing) return;const rect = canvas.getBoundingClientRect();const x = e.clientX - rect.left;const y = e.clientY - rect.top;if (!startPoint) {// 设置起点startPoint = { x, y };updateStatus('请点击绘制线段的终点...');updatePointInfo({ x, y }, null);} else {// 设置终点,创建线段const endPoint = { x, y };// 创建新线段const newLine = {x1: startPoint.x,y1: startPoint.y,x2: x,y2: y,clipped: null};lines.push(newLine);// 更新点信息updatePointInfo(startPoint, endPoint);// 重置绘制状态startPoint = null;isDrawing = false;canvas.style.cursor = 'default';updateStatus(`已添加线段 #${lines.length}。点击"裁剪线段"查看结果。`);redraw();}});// 初始绘制redraw();// 添加键盘快捷键document.addEventListener('keydown', function(e) {if (e.key === 'Escape') {// ESC键取消绘制if (isDrawing) {isDrawing = false;startPoint = null;canvas.style.cursor = 'default';updateStatus('取消绘制线段。');redraw();}}});</script><!-- 添加Bootstrap图标 --><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.0/font/bootstrap-icons.css">
</body>
</html>
3.运行结果

4.详细介绍
# Cohen-Sutherland直线裁剪算法演示
这是一个交互式的Cohen-Sutherland直线裁剪算法演示项目,使用现代JavaScript和Canvas技术实现。
## 项目介绍
Cohen-Sutherland算法是一种经典的二维直线裁剪算法,用于确定一条线段是否与给定的矩形窗口相交,并计算交点。该算法使用区域编码(Region Code)来快速判断线段是否需要裁剪。
### 主要特点
- 交互式线段绘制和裁剪
- 美观的用户界面
- 实时显示区域编码
- 详细的算法步骤可视化
- 响应式设计,适应不同屏幕大小
## 文件结构
- `cohenSutherland.js` - 算法核心实现,使用现代ES模块
- `cohenSutherlandDemo.html` - 交互式演示界面
- `server.js` - 用于本地运行的简易HTTP服务器
- `README.md` - 项目说明文档
## 使用方法
1. 启动本地服务器:
```bash
node server.js
```
2. 打开浏览器访问 `http://localhost:3000`
3. 使用界面功能:
- 点击"绘制新线段"按钮,然后在画布上点击两次定义一条线段
- 点击"裁剪线段"按钮对绘制的线段进行裁剪
- 点击"显示/隐藏区域编码"按钮查看区域编码
- 点击"重置"按钮清除所有线段
## 区域编码说明
Cohen-Sutherland算法将二维平面划分为9个区域,使用4位二进制编码表示点所在的区域:
```
1001 | 1000 | 1010
----------------------
0001 | 0000 | 0010
----------------------
0101 | 0100 | 0110
```
每一位的含义:
- 第1位(LEFT):点在窗口左侧 (0001)
- 第2位(RIGHT):点在窗口右侧 (0010)
- 第3位(BOTTOM):点在窗口下方 (0100)
- 第4位(TOP):点在窗口上方 (1000)
中间区域(0000)表示点在窗口内部。
## 算法步骤
1. 计算线段两个端点P1(x1,y1)和P2(x2,y2)的区域编码code1和code2
2. 如果(code1 | code2) == 0,说明两点都在窗口内,直接接受该线段
3. 如果(code1 & code2) != 0,说明线段完全在窗口外的同一侧,直接拒绝该线段
4. 否则,线段部分在窗口内,需要裁剪:
- 选择一个在窗口外的端点
- 根据区域编码确定端点与窗口边界的交点
- 用交点替换原来的端点
- 重新计算新端点的区域编码
- 重复上述步骤,直到两点都在窗口内或线段被拒绝
## 技术栈
- 原生JavaScript (ES6+)
- HTML5 Canvas
- CSS3
- Bootstrap 5 (样式)
- Node.js (本地服务器)
相关文章:
图形裁剪算法
1.学习目标 理解区域编码(Region Code,RC) 设计Cohen-Sutherland直线裁剪算法 编程实现Cohen-Sutherland直线裁剪算法 2.具体代码 1.具体算法 /*** Cohen-Sutherland直线裁剪算法 - 优化版* author AI Assistant* license MIT*/// 区域编码常量 - 使用对象枚举…...
Python字典实战: 三大管理系统开发指南(班级+会议+购物车)(附源码)
目录 摘要 一、班级管理系统(含成绩模块) 1. 功能概述 2. 完整代码与解析 3. 代码解析与亮点 二、会议管理系统 1. 功能概述 2. 完整代码 3. 代码解析与亮点 三、购物车管理系统 1. 功能概述 2. 完整代码 3. 代码解析与亮点 四、总结与扩…...
R 语言科研绘图第 36 期 --- 饼状图-基础
在发表科研论文的过程中,科研绘图是必不可少的,一张好看的图形会是文章很大的加分项。 为了便于使用,本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中,获取方式: R 语言科研绘图模板 --- sciRplothttps://mp.…...
vue 3 从零开始到掌握
vue3从零开始一篇文章带你学习 升级vue CLI 使用命令 ## 查看vue/cli版本,确保vue/cli版本在4.5.0以上 vue --version ## 安装或者升级你的vue/cli npm install -g vue/cli ## 创建 vue create vue_test ## 启动 cd vue_test npm run servenvm管理node版本&#…...
【R语言绘图】圈图绘制代码
绘制代码 rm(list ls())# 加载必要包 library(data.table) library(circlize) library(ComplexHeatmap) library(rtracklayer) library(GenomicRanges) library(BSgenome) library(GenomicFeatures) library(dplyr)### 数据准备阶段 ### # 1. 读取染色体长度信息 df <- re…...
OCR迁移
一、环境 操作系统:Centos57.6 数据库版本:12.2.0.1 场景:将OCR信息从DATA磁盘组迁移到OCR磁盘组 二、操作步骤 1.查看可用空盘 set lin 200 set pagesize 200 col DGNAME format a15 col DISKNAME format a15 col PATH format a20 col N…...
OpenCV 图形API(17)计算输入矩阵 src 中每个元素的平方根函数sqrt()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 描述 计算数组元素的平方根。 cv::gapi::sqrt 函数计算每个输入数组元素的平方根。对于多通道数组,每个通道会独立处理。其精度大约与内置的 …...
python中的in关键字查找的时间复杂度
列表(List) 对于列表来说, in 运算符的复杂度是 O(n),其中n是列表的长度。这意味着如果列表中有n个元素,那么执行 in 运算符需要遍历整个列表来查找目标元素。 以下是一个示例,演示了在列表中使用 in 运算…...
Python爬虫第6节-requests库的基本用法
目录 前言 一、准备工作 二、实例引入 三、GET请求 3.1 基本示例 3.2 抓取网页 3.3 抓取二进制数据 3.4 添加headers 四、POST请求 五、响应 前言 前面我们学习了urllib的基础使用方法。不过,urllib在实际应用中存在一些不便之处。以网页验证和Cookies处理…...
什么是可靠性工程师?
一、什么是可靠性工程师? 可靠性工程师就是负责确保产品在使用过程中不出故障、不给客户添麻烦。 你可以理解为是那种“挑毛病的人”,但不是事后挑,是提前想清楚产品在哪些情况下可能会出问题,然后解决掉。 比如: …...
linux (CentOS 10)使用传统程序语言(C)进行编译---主,子程序连接:子程序的编译
1 主程序 rootlocalhost:~/testc/testlink3# cat thanks.c #include <stdio.h> // 声明子程序 void thanks_2(void); int main(void) {printf("Hello World\n");thanks_2(); }2 子程序 rootlocalhost:~/testc/testlink3# cat thanks_2.c #include <stdio.…...
如何根据设计稿进行移动端适配:全面详解
如何根据设计稿进行移动端适配:全面详解 文章目录 如何根据设计稿进行移动端适配:全面详解1. **理解设计稿**1.1 设计稿的尺寸1.2 设计稿的单位 2. **移动端适配的核心技术**2.1 使用 viewport 元标签2.1.1 代码示例2.1.2 参数说明 2.2 使用相对单位2.2.…...
【Kafka基础】Kafka 2.8以下版本的安装与配置指南:传统ZooKeeper依赖版详解
对于仍在使用Kafka 2.8之前版本的团队来说,需要特别注意其强依赖外部ZooKeeper的特性。本文将完整演示传统架构下的安装流程,并对比新旧版本差异。 1 版本特性差异说明 1.1 2.8 vs 2.8-核心区别 特性 2.8版本 2.8-版本 协调服务 可选内置KRaft模式 …...
Redis-x64-3.2.100.msi : Windows 安装包(MSI 格式)安装步骤
Redis-x64-3.2.100.msi 是 Redis 的 Windows 安装包(MSI 格式),适用于 64 位系统。 在由于一些环境需要低版本的Redis的安装包。 Redis-x64-3.2.100.msi 安装包下载:https://pan.quark.cn/s/cc4d38262a15 Redis 是一个开源的 内…...
ZoomCharts使用方法
本篇没有讲解,只是自己的小笔记,有看到的网友想明白具体用法的可以来私信我 <div class"zoomChartsComponent"><div id"zoomCharts-demo"></div></div> var ZoomChartsLicense ; var ZoomChartsLicenseKey…...
【云计算】打造高效容器云平台:规划、部署与架构设计
引言 随着移动互联网时代的大步跃进,互联网公司业务的爆炸式增长发展给传统行业带来了巨大的冲击和挑战,被迫考虑转型和调整。对于我们传统的航空行业来说,还存在传统的思维、落后的技术。一项新业务从提出需求到立项审批、公开招标、项目实…...
DeepSeek底层揭秘——《推理时Scaling方法》内容理解
4月初,DeepSeek 提交到 arXiv 上的最新论文正在 AI 社区逐渐升温。 论文核心内容理解 DeepSeek与清华大学联合发布的论文《奖励模型的推理时Scaling方法及其在大规模语言模型中的应用》,核心在于提出一种新的推理时Scaling方法,即通过动态调…...
JavaScript之Json数据格式
介绍 JavaScript Object Notation, js对象标注法,是轻量级的数据交换格式完全独立于编程语言文本字符集必须用UTF-8格式,必须用“”任何支持的数据类型都可以用JSON表示JS内内置JSON解析JSON本质就是字符串 Json对象和JS对象互相转化 前端…...
OBS 中如何设置固定码率(CBR)与可变码率(VBR)?
在使用 OBS 进行录制或推流时,设置“码率控制模式”(Rate Control)是非常重要的一步。常见的控制模式包括: CBR(固定码率):保持恒定的输出码率,适合直播场景。 VBR(可变码率):在允许的范围内动态调整码率,适合本地录制、追求画质。 一、CBR vs. VBR 的差异 项目CBR…...
使用 Rsync + Lsyncd 实现 CentOS 7 实时文件同步
文章目录 🌀使用 Rsync Lsyncd 实现 CentOS 7 实时文件同步前言介绍架构图🧱系统环境🔧Rsync配置(两台都需安装)关闭SELinux(两台都需) 📦配置目标端(client)…...
C# 多线程并发编程基础
1. 线程基础 1.1 线程简介 C# 中的线程是操作系统能够进行运算调度的最小单位,它被包含在进程中,是进程中的实际运作单位。一个进程可以包含多个线程,这些线程可以并发执行不同的任务。 1.2 线程的创建与启动 在 C# 中,可以使…...
RAG(检索增强生成)系统,提示词(Prompt)表现测试(数据说话)
在RAG(检索增强生成)系统中,评价提示词(Prompt)设计是否优秀,必须通过量化测试数据来验证,而非主观判断。以下是系统化的评估方法、测试指标和具体实现方案: 一、提示词优秀的核心标准 优秀的提示词应显著提升以下指标: 维度量化指标测试方法事实一致性Faithfulness …...
QML和C++交互
目录 1 QML与C交互基础1.1 全局属性1.2 属性私有化(提供接口访问) 2 QT与C交互(C创建自定义对象,qml文件直接访问)3 QT与C交互(qml直接访问C中的函数)4 QT与C交互(qml端发送信号 C端实现槽函数)…...
Android studio学习之路(六)--真机的调试以及多媒体照相的使用
多媒体应用(语言识别,照相,拍视频)在生活的各个方面都具有非常大的作用,所以接下来将会逐步介绍多媒体的使用,但是在使用多媒体之前,使用模拟器肯定是不行的,所以我们必须要使用真机…...
解决 Lettuce 在 Redis 集群模式下的故障转移问题
引言 在高可用系统中,故障转移是确保服务不中断的重要机制。当我们使用 Lettuce 作为 Redis 的 Java 客户端时,如何高效地处理故障转移成为一项关键任务。本篇文章将探讨如何在 Redis 集群模式下配置 Lettuce 以优化故障转移。 背景 在初期设置 Lettu…...
Qt 资源文件(.qrc 文件)
Qt 资源文件(.qrc 文件)是 Qt 提供的一种机制,用来将文件(如图像、音频、文本文件等)嵌入到应用程序中,使得这些文件不需要依赖外部文件路径,而是直接打包到程序的可执行文件中。通过使用 Qt 资…...
Vue 组件命名及子组件接收参数命名
1. 对于单个单词的组件 方式一:首字母大写。如 <School></School>。在 vue 开发者工具中默认使用的是该种方式。 方式二: 首字母小写。如 <school></school> 2. 对于多个单词的组件 方式一:每个单词都是小写&…...
PandaAI:一个基于AI的对话式数据分析工具
PandaAI 是一个基于 Python 开发的自然语言处理和数据分析工具,支持问答式(ChatGPT)的数据分析和报告生成功能。PandaAI 提供了一个开源的框架,主要核心组件包含用于数据处理的数据准备层(Pandas)以及实现 …...
【C++算法】50.分治_归并_翻转对
文章目录 题目链接:题目描述:解法C 算法代码:图解 题目链接: 493. 翻转对 题目描述: 解法 分治 策略一:计算当前元素cur1后面,有多少元素的两倍比我cur1小(降序) 利用单…...
Github最新AI工具汇总2025年4月份第2周
根据GitHub官方动态及开发者生态最新进展,以下是2025年4月第二周(截至4月7日)值得关注的AI工具与技术更新汇总: 1. GitHub Copilot Agent Mode全量发布 核心功能:在VS Code中启用Agent模式后,Copilot可自主…...
