背景
对于弯曲的三条平行线,一开始我以为只需要使用中心线,然后复制两条,一个向右下角平移,一个向左上角平移,就能让三条线实现完全平行,每一处的距离都相等。后来仔细思考后,发现我想错了,因为弯曲处的平行距离是,x移动,y移动的平方根。后来想使用曲线的缩放加上平移来实现三条线段弯曲平行,曲线部分依然无法达到完全平行。
最后请教了ChatGPT,对于曲线的平行线,要使用切线加法线的方式来确定。法线的距离就是平行距离。
具体就是获取曲线部分上的每一个点,然后求出该点的切线向量,然后再求出切线的法线,法线延长平行距离,就能确定平移后的点。
以下是效果图及完整的实现代码。
案例还有一些小问题,就是末尾的封口时,细节没有处理,只是简单地做作x轴,加减。理想情况是,想中心线作垂直线。然后对最外层的线段连线。
附加一个弯曲的中心线度量尺。
垂直的井身
垂直的井身
<!DOCTYPE html>
<html>
<head>
<script src="./paper-full.min.js"></script>
<style>
canvas {
width: 900px;
height: 750px;
border: 1px solid #ccc;
display: block;
margin: 0 auto;
}
</style>
</head>
<body>
<canvas id="myCanvas" resize></canvas>
<script>
paper.setup('myCanvas');
const casingColor = "#1e88e5"
let initialHandlePointSize = null
// 创建一个包含直角的路径
var centerPath = new paper.Path({
segments: [[0, 0], [0, 500.0]],
strokeColor: '#ffc107',
strokeWidth: 1,
strokeScaling: false,
// fullySelected: true // 选中状态,便于查看路径详情
});
var length = centerPath.length;
var quarterPoint2 = centerPath.getPointAt(length * 0.5);
centerPath.insert(1, quarterPoint2);
const radius = 15.0
let innerPath = null
let outerPath = null
const casingData = [
{ name: '最内侧套管', color: '#5470c6', key: 'casing1', smallRadius: 15.0, bigRadius: 21.5, length: 400.0 },
{ name: '第二层套管', color: '#92cc76', key: 'casing2', smallRadius: 21.5, bigRadius: 31.5, length: 250.0 },
{ name: '第三层套管', color: '#fac858', key: 'casing3', smallRadius: 31.5, bigRadius: 44.5, length: 150.0 },
{ name: '第四层套管', color: '#ef6666', key: 'casing4', smallRadius: 44.5, bigRadius: 66.5, length: 50.0 }
]
initOutPath()
function addBaseLine() {
var center = centerPath.firstSegment.point; // 中心点位置
var extension = 200.0; // 向左右各扩展100单位
// 计算起点和终点
var startPoint = center.subtract([extension, 0]); // 向左扩展
var endPoint = center.add([extension, 0]); // 向右扩展
// 创建线段
var baseLine = new paper.Path({
name: 'fizzBase',
segments: [startPoint, endPoint],
strokeColor: 'black', // 线条颜色
strokeWidth: 2 // 线条宽度
});
}
function initOutPath() {
// 创建内侧和外侧路径
if (outerPath) {
outerPath.remove()
outerPath = null
}
if (innerPath) {
innerPath.remove()
innerPath = null
}
outerPath = new paper.Path({
strokeColor: casingColor,
strokeWidth: 1,
// fullySelected: true
});
innerPath = new paper.Path({
strokeColor: casingColor,
strokeWidth: 1
});
const startPoint = centerPath.segments[0].point
innerPath.add(startPoint.add(new paper.Point(-radius, 0)));
outerPath.add(startPoint.add(new paper.Point(radius, 0)));
var startLocationOffset = centerPath.getLocationOf(centerPath.segments[0].point).offset;
var endLocationOffset = centerPath.getLocationOf(centerPath.lastSegment.point).offset;
for (let i = startLocationOffset; i < endLocationOffset; i++) {
const point = centerPath.getPointAt(i)
// 获取法线向量
var normal = centerPath.getNormalAt(i);
// 计算内侧和外侧点的位置
var innerPoint = point.add(normal.multiply(-radius));
var outerPoint = point.add(normal.multiply(radius));
// 将计算出的点添加到相应的路径
innerPath.add(innerPoint);
outerPath.add(outerPoint);
}
innerPath.add(innerPath.lastSegment.point.add(new paper.Point(radius, 0)));
outerPath.add(outerPath.lastSegment.point.subtract(new paper.Point(radius, 0)));
// const endPoint = centerPath.lastSegment.point
// innerPath.add(endPoint.add(new paper.Point(0, radius)));
// outerPath.add(endPoint.add(new paper.Point(0, -radius)));
// 选中状态,便于查看路径详情
// innerPath.fullySelected = true;
// outerPath.fullySelected = true;
}
// 初始化套管
function initCasing(arr) {
for (let i = 0; i < arr.length; i++) {
const { key, name, smallRadius, bigRadius, length,color } = arr[i];
const outerCasingPath = new paper.Path({
name: `name-outer-${key}`,
strokeColor: color,
strokeWidth: 1,
strokeScaling: false
});
const innerCasingPath = new paper.Path({
name: `name-inner-${key}`,
strokeColor: color,
strokeWidth: 1,
strokeScaling: false
});
// const startPoint = centerPath.segments[0].point
// innerPath.add(startPoint.add(new paper.Point(-radius, 0)));
// outerPath.add(startPoint.add(new paper.Point(radius, 0)));
var startLocationOffset = centerPath.getLocationOf(centerPath.firstSegment.point).offset;
var endLocationOffset = centerPath.getLocationOf(centerPath.lastSegment.point).offset;
for (let i = startLocationOffset; i < length; i++) {
const point = centerPath.getPointAt(i)
// 获取法线向量
var normal = centerPath.getNormalAt(i);
// 计算内侧和外侧点的位置
var innerPoint = point.add(normal.multiply(-bigRadius));
var outerPoint = point.add(normal.multiply(bigRadius));
// 将计算出的点添加到相应的路径
innerCasingPath.add(innerPoint);
outerCasingPath.add(outerPoint);
}
innerCasingPath.add(innerCasingPath.lastSegment.point.add(new paper.Point(bigRadius - smallRadius, 0)));
outerCasingPath.add(outerCasingPath.lastSegment.point.subtract(new paper.Point(bigRadius - smallRadius, 0)));
}
}
function handleRuler({ originalPath, firstPath, secondPath, thirdPath }) {
var firstPathLength = firstPath.length;
var secondPathLength = secondPath.length;
var thirdPathLength = thirdPath.length;
var point1 = originalPath.segments[0].point;
var point2 = originalPath.segments[1].point;
var firstDistance = point1.getDistance(point2);
var secondDistance = originalPath.segments[1].point.getDistance(originalPath.segments[2].point);
var thirdDistance = originalPath.segments[1].point.getDistance(originalPath.segments[2].point);
const firstZm = firstPathLength / firstDistance
const secondZm = secondPathLength / secondDistance
const thirdZm = thirdPathLength / thirdDistance
drawScale(firstPath, firstZm);
drawScale(secondPath, secondZm, firstDistance);
drawScale(thirdPath, thirdZm);
}
// 更新外侧线位置的函数
function updateOuterLines(i, delta) {
if (i === 3) {
innerPath.lastSegment.point = innerPath.lastSegment.point.add(delta)
outerPath.lastSegment.point = outerPath.lastSegment.point.add(delta)
} else if (i === 0) {
innerPath.firstSegment.point = innerPath.firstSegment.point.add(delta)
outerPath.firstSegment.point = outerPath.firstSegment.point.add(delta)
}
}
function addHandle() {
// 计算路径的总长度
// var length = centerPath.length;
// // 计算四等分点的位置
// var quarterPoint1 = centerPath.getPointAt(length * 0.25);
// var quarterPoint2 = centerPath.getPointAt(length * 0.5);
// var quarterPoint3 = centerPath.getPointAt(length * 0.75);
const pointData = {
radius: 5.0,
fillColor: 'red',
data: {
isHandlePoint: true
}
}
// 在四等分点创建圆点
var circle1 = new paper.Path.Circle({
name: "fizz1",
center: centerPath.segments[0].point.clone(),
radius: 5.0,
fillColor: 'red',
data: {
isHandlePoint: true
}
// ...pointData
});
var circle2 = new paper.Path.Circle({
name: "fizz2",
center: centerPath.segments[1].point.clone(),
radius: 5.0,
fillColor: 'red',
data: {
isHandlePoint: true
}
});
var circle3 = new paper.Path.Circle({
name: "fizz3",
center: centerPath.segments[2].point.clone(),
radius: 5.0,
fillColor: 'red',
data: {
isHandlePoint: true
}
});
initialHandlePointSize = circle1.bounds.size.clone();
// circle1.onMouseDrag = function (event) {
// console.log(11)
// this.position = event.point;
// centerPath.segments[1].point = event.point;
// centerPath.segments[0].point.x = event.point.x;
// // if (index === 0) {
// // segment.point = segment.point.add(delta);
// // }
// // if (index === 3) {
// // segment.point = segment.point.add(delta);
// // }
// // initOutPath()
// // // updateOuterLines(index, event.delta); // 更新外侧线位置
// // handle.position = segment.point; // 确保操作点跟随移动
// };
// 为中心线的每个点添加拖动事件
// centerPath.segments.forEach((segment, index) => {
// var handle = new paper.Path.Circle({
// center: segment.point,
// radius: 3,
// strokeWidth: 2,
// fillColor: '#92cc76',
// strokeColor: '#92cc76'
// });
// handle.onMouseDrag = function (event) {
// const delta = event.delta
// if (index === 0) {
// segment.point = segment.point.add(delta);
// }
// if (index === 3) {
// segment.point = segment.point.add(delta);
// }
// initOutPath()
// // updateOuterLines(index, event.delta); // 更新外侧线位置
// handle.position = segment.point; // 确保操作点跟随移动
// };
// });
}
// 绘制刻度尺
function drawScale(path, scale, start = 0) {
const totalLength = path.length;
const tickInterval = 20 * scale;
const majorTickInterval = 100 * scale;
const tickSize = radius / 2;
const majorTickSize = 20;
for (let i = 0; i <= totalLength; i += tickInterval) {
const point = path.getPointAt(i);
const normal = path.getNormalAt(i).multiply(tickSize);
const tickEnd = point.add(normal);
var tick = new paper.Path.Line({
from: point,
to: tickEnd,
strokeColor: '#cbd5e1'
});
if (i % majorTickInterval === 0) {
tick.strokeWidth = 1;
tick.strokeColor = "#475569";
tick.segments[1].point = point.add(normal.multiply(1.5)); // 每到100,增长刻度
// Add labels for major ticks
var text = new paper.PointText({
point: tick.segments[0].point.add(new paper.Point(3, -5)), // Offset text a bit for visibility
content: (i / scale + start).toString(),
fillColor: 'black',
fontFamily: 'Courier New',
fontSize: 8
});
text.rotate(90, tick.segments[0].point); // Rotate text to align with tick
}
}
}
function initTool() {
var hitOptions = {
segments: true,
stroke: true,
fill: true,
tolerance: 5
};
var segment, path;
var movePath = false;
const tool = new paper.Tool();
tool.onMouseDown = (event) => {
segment = path = null;
var hitResult = paper.project.hitTest(event.point, hitOptions);
if (!hitResult)
return;
if (event.modifiers.shift) {
if (hitResult.type == 'segment') {
hitResult.segment.remove();
};
return;
}
if (hitResult) {
path = hitResult.item;
path.selected = true
if (hitResult.type == 'segment') {
segment = hitResult.segment;
}
}
}
tool.onMouseDrag = (event) => {
if (segment) {
segment.point = segment.point.add(event.delta);
// path.smooth();
} else if (path) {
path.position = path.position.add(event.delta);
}
}
}
paper.view.draw();
function initMoveTool() {
const tool = new paper.Tool();
var lastPoint = null; // 上一次鼠标位置
var dragging = false;
var lastViewCenter;
tool.onMouseDown = (event) => {
// 当鼠标按下时, 记录当前鼠标位置
lastPoint = event.point;
dragging = true;
};
tool.onMouseDrag = (event) => {
if (dragging && lastPoint) {
lastViewCenter = paper.view.center;
const delta = lastPoint.subtract(event.point);
paper.view.center = paper.view.center.add(delta);
lastPoint = event.point.add(paper.view.center.subtract(lastViewCenter));
}
};
tool.onMouseUp = () => {
// 结束拖动
dragging = false;
};
}
function initZoomTool() {
document.querySelector("#myCanvas").addEventListener("wheel", (event) => {
event.preventDefault(); // 阻止默认滚动行为
const delta = event.deltaY > 0 ? -0.02 : 0.02;
const view = paper.view;
const oldZoom = view.zoom;
const newZoom = Math.max(0.1, oldZoom * (1 + delta));
const mousePosition = new paper.Point(event.offsetX, event.offsetY);
// 计算缩放前的鼠标位置到视图中心的向量
const viewPosition = view.viewToProject(mousePosition);
// 设置新的缩放级别
view.zoom = newZoom;
// 重新计算并设置视图中心,以鼠标位置为缩放中心
const scaledPoint = view.viewToProject(mousePosition);
const deltaPoint = viewPosition.subtract(scaledPoint);
view.center = view.center.add(deltaPoint);
});
}
// 重置操作点
function resetHandlePoint() {
const handlePoints = paper.project.getItems({
data: {
isHandlePoint: true
}
});
handlePoints.forEach(x => {
x.bounds.size = initialHandlePointSize.divide(paper.view.zoom);
})
}
function fitCanvas() {
// 获取画布父元素的大小
var parent = paper.view.element.parentNode
var rect = parent.getBoundingClientRect();
const bounds = paper.project.activeLayer.bounds;
const { width, height } = bounds;
var widthScale = (rect.width * 0.9) / width;
var heightScale = (rect.height * 0.9) / height;
const minScale = Math.min(widthScale, heightScale);
const currentScale = paper.view.zoom;
// 重新设置 Paper.js 画布的大小
paper.view.viewSize = new paper.Size(rect.width, rect.height);
var scaleDelta = minScale / currentScale;
paper.view.scale(scaleDelta);
const center = new paper.Point(width / 2, height / 2);
paper.view.center = center;
}
function initDragHandleTool() {
var lastPoint = null; // 上一次鼠标位置
var dragging = false;
var lastViewCenter;
var hitOptions = {
segments: false, // 点
// stroke: true, // 边
fill: true, // 内部
// tolerance: 2,
tolerance: 2,
match: (res) => {
console.log(res)
return res.item.data.isHandlePoint;
},
};
const tool = new paper.Tool();
tool.onMouseDown = (event) => {
path = null;
var hitResult = paper.project.hitTest(event.point, hitOptions);
if (!hitResult) {
lastPoint = event.point;
dragging = true;
return;
}
if (hitResult) {
console.log(hitResult)
path = hitResult.item;
path.selected = true
}
}
tool.onMouseDrag = (event) => {
console.log(11)
if (path) {
path.position = path.position.add(event.delta);
// this.position = event.point;
centerPath.segments[1].point = event.point;
centerPath.segments[0].point.x = event.point.x;
return
}
if (dragging && lastPoint) {
lastViewCenter = paper.view.center;
const delta = lastPoint.subtract(event.point);
paper.view.center = paper.view.center.add(delta);
lastPoint = event.point.add(paper.view.center.subtract(lastViewCenter));
}
}
tool.onMouseUp = (event) => {
path = null
dragging = false;
}
}
initZoomTool()
initCasing(casingData)
addBaseLine()
// fitCanvas()
// initMoveTool()
addHandle()
initDragHandleTool()
const rulerPath = centerPath.clone().translate(new paper.Point(-200,0))
rulerPath.strokeColor = "red"
drawScale(rulerPath, 1);
</script>
</body>
</html>
弯曲的井身
弯曲的井身
<!DOCTYPE html>
<html>
<head>
<script src="./paper-full.min.js"></script>
<style>
canvas {
width: 900px;
height: 750px;
border: 1px solid #ccc;
display: block;
margin: 0 auto;
}
</style>
</head>
<body>
<canvas id="myCanvas" resize></canvas>
<script>
paper.setup('myCanvas');
const casingColor = "#1e88e5"
let initialHandlePointSize = null
// 创建一个包含直角的路径
var centerPath = new paper.Path({
segments: [[0, 0], [0, 200], [50, 250], [400, 250.0]],
strokeColor: '#ffc107',
strokeWidth: 1,
strokeScaling: false,
// fullySelected: true // 选中状态,便于查看路径详情
});
centerPath.segments[1].handleOut = new paper.Point(0, 30)
centerPath.segments[2].handleIn = new paper.Point(-30, 0)
// var length = centerPath.length;
// var quarterPoint2 = centerPath.getPointAt(length * 0.5);
// centerPath.insert(1, quarterPoint2);
const radius = 15.0
let innerPath = null
let outerPath = null
const casingData = [
{ name: '最内侧套管', color: '#5470c6', key: 'casing1', smallRadius: 15.0, bigRadius: 21.5, length: 400.0 },
{ name: '第二层套管', color: '#92cc76', key: 'casing2', smallRadius: 21.5, bigRadius: 31.5, length: 250.0 },
{ name: '第三层套管', color: '#fac858', key: 'casing3', smallRadius: 31.5, bigRadius: 44.5, length: 150.0 },
{ name: '第四层套管', color: '#ef6666', key: 'casing4', smallRadius: 44.5, bigRadius: 66.5, length: 50.0 }
]
initOutPath()
function addBaseLine() {
var center = centerPath.firstSegment.point; // 中心点位置
var extension = 200.0; // 向左右各扩展100单位
// 计算起点和终点
var startPoint = center.subtract([extension, 0]); // 向左扩展
var endPoint = center.add([extension, 0]); // 向右扩展
// 创建线段
var baseLine = new paper.Path({
name: 'fizzBase',
segments: [startPoint, endPoint],
strokeColor: 'black', // 线条颜色
strokeWidth: 2 // 线条宽度
});
}
function initOutPath() {
// 创建内侧和外侧路径
if (outerPath) {
outerPath.remove()
outerPath = null
}
if (innerPath) {
innerPath.remove()
innerPath = null
}
outerPath = new paper.Path({
strokeColor: casingColor,
strokeWidth: 1,
// fullySelected: true
});
innerPath = new paper.Path({
strokeColor: casingColor,
strokeWidth: 1
});
const startPoint = centerPath.segments[0].point
innerPath.add(startPoint.add(new paper.Point(-radius, 0)));
outerPath.add(startPoint.add(new paper.Point(radius, 0)));
var startLocationOffset = centerPath.getLocationOf(centerPath.segments[0].point).offset;
var endLocationOffset = centerPath.getLocationOf(centerPath.lastSegment.point).offset;
for (let i = startLocationOffset; i < endLocationOffset; i++) {
const point = centerPath.getPointAt(i)
// 获取法线向量
var normal = centerPath.getNormalAt(i);
// 计算内侧和外侧点的位置
var innerPoint = point.add(normal.multiply(-radius));
var outerPoint = point.add(normal.multiply(radius));
// 将计算出的点添加到相应的路径
innerPath.add(innerPoint);
outerPath.add(outerPoint);
}
innerPath.add(innerPath.lastSegment.point.add(new paper.Point(radius, 0)));
outerPath.add(outerPath.lastSegment.point.subtract(new paper.Point(radius, 0)));
// const endPoint = centerPath.lastSegment.point
// innerPath.add(endPoint.add(new paper.Point(0, radius)));
// outerPath.add(endPoint.add(new paper.Point(0, -radius)));
// 选中状态,便于查看路径详情
// innerPath.fullySelected = true;
// outerPath.fullySelected = true;
}
// 初始化套管
function initCasing(arr) {
for (let i = 0; i < arr.length; i++) {
const { key, name, smallRadius, bigRadius, length, color } = arr[i];
const outerCasingPath = new paper.Path({
name: `name-outer-${key}`,
strokeColor: color,
strokeWidth: 1,
strokeScaling: false
});
const innerCasingPath = new paper.Path({
name: `name-inner-${key}`,
strokeColor: color,
strokeWidth: 1,
strokeScaling: false
});
// const startPoint = centerPath.segments[0].point
// innerPath.add(startPoint.add(new paper.Point(-radius, 0)));
// outerPath.add(startPoint.add(new paper.Point(radius, 0)));
var startLocationOffset = centerPath.getLocationOf(centerPath.firstSegment.point).offset;
var endLocationOffset = centerPath.getLocationOf(centerPath.lastSegment.point).offset;
for (let i = startLocationOffset; i < length; i++) {
const point = centerPath.getPointAt(i)
// 获取法线向量
var normal = centerPath.getNormalAt(i);
// 计算内侧和外侧点的位置
var innerPoint = point.add(normal.multiply(-bigRadius));
var outerPoint = point.add(normal.multiply(bigRadius));
// 将计算出的点添加到相应的路径
innerCasingPath.add(innerPoint);
outerCasingPath.add(outerPoint);
}
innerCasingPath.add(innerCasingPath.lastSegment.point.add(new paper.Point(bigRadius - smallRadius, 0)));
outerCasingPath.add(outerCasingPath.lastSegment.point.subtract(new paper.Point(bigRadius - smallRadius, 0)));
}
}
function handleRuler({ originalPath, firstPath, secondPath, thirdPath }) {
var firstPathLength = firstPath.length;
var secondPathLength = secondPath.length;
var thirdPathLength = thirdPath.length;
var point1 = originalPath.segments[0].point;
var point2 = originalPath.segments[1].point;
var firstDistance = point1.getDistance(point2);
var secondDistance = originalPath.segments[1].point.getDistance(originalPath.segments[2].point);
var thirdDistance = originalPath.segments[1].point.getDistance(originalPath.segments[2].point);
const firstZm = firstPathLength / firstDistance
const secondZm = secondPathLength / secondDistance
const thirdZm = thirdPathLength / thirdDistance
drawScale(firstPath, firstZm);
drawScale(secondPath, secondZm, firstDistance);
drawScale(thirdPath, thirdZm);
}
// 更新外侧线位置的函数
function updateOuterLines(i, delta) {
if (i === 3) {
innerPath.lastSegment.point = innerPath.lastSegment.point.add(delta)
outerPath.lastSegment.point = outerPath.lastSegment.point.add(delta)
} else if (i === 0) {
innerPath.firstSegment.point = innerPath.firstSegment.point.add(delta)
outerPath.firstSegment.point = outerPath.firstSegment.point.add(delta)
}
}
function addHandle() {
// 计算路径的总长度
// var length = centerPath.length;
// // 计算四等分点的位置
// var quarterPoint1 = centerPath.getPointAt(length * 0.25);
// var quarterPoint2 = centerPath.getPointAt(length * 0.5);
// var quarterPoint3 = centerPath.getPointAt(length * 0.75);
const pointData = {
radius: 5.0,
fillColor: 'red',
data: {
isHandlePoint: true
}
}
// 在四等分点创建圆点
var circle1 = new paper.Path.Circle({
name: "fizz1",
center: centerPath.segments[0].point.clone(),
radius: 5.0,
fillColor: 'red',
data: {
isHandlePoint: true
}
// ...pointData
});
var circle2 = new paper.Path.Circle({
name: "fizz2",
center: centerPath.segments[1].point.clone(),
radius: 5.0,
fillColor: 'red',
data: {
isHandlePoint: true
}
});
var circle3 = new paper.Path.Circle({
name: "fizz3",
center: centerPath.segments[2].point.clone(),
radius: 5.0,
fillColor: 'red',
data: {
isHandlePoint: true
}
});
initialHandlePointSize = circle1.bounds.size.clone();
// circle1.onMouseDrag = function (event) {
// console.log(11)
// this.position = event.point;
// centerPath.segments[1].point = event.point;
// centerPath.segments[0].point.x = event.point.x;
// // if (index === 0) {
// // segment.point = segment.point.add(delta);
// // }
// // if (index === 3) {
// // segment.point = segment.point.add(delta);
// // }
// // initOutPath()
// // // updateOuterLines(index, event.delta); // 更新外侧线位置
// // handle.position = segment.point; // 确保操作点跟随移动
// };
// 为中心线的每个点添加拖动事件
// centerPath.segments.forEach((segment, index) => {
// var handle = new paper.Path.Circle({
// center: segment.point,
// radius: 3,
// strokeWidth: 2,
// fillColor: '#92cc76',
// strokeColor: '#92cc76'
// });
// handle.onMouseDrag = function (event) {
// const delta = event.delta
// if (index === 0) {
// segment.point = segment.point.add(delta);
// }
// if (index === 3) {
// segment.point = segment.point.add(delta);
// }
// initOutPath()
// // updateOuterLines(index, event.delta); // 更新外侧线位置
// handle.position = segment.point; // 确保操作点跟随移动
// };
// });
}
// 绘制刻度尺
function drawScale(path, scale, start = 0) {
const totalLength = path.length;
const tickInterval = 20 * scale;
const majorTickInterval = 100 * scale;
const tickSize = radius / 2;
const majorTickSize = 20;
const rulerPath = new paper.Path({
name: `rulerPath`,
strokeColor: 'black',
strokeWidth: 1,
strokeScaling: false
});
var startLocationOffset = centerPath.getLocationOf(centerPath.firstSegment.point).offset;
var endLocationOffset = centerPath.getLocationOf(centerPath.lastSegment.point).offset;
for (let i = startLocationOffset; i < centerPath.length; i++) {
const point = centerPath.getPointAt(i)
var normal = centerPath.getNormalAt(i);
var rulerPathPoint = point.add(normal.multiply(-100));
rulerPath.add(rulerPathPoint);
if (i % tickInterval === 0) {
// const point = rulerPathPoint.clone()
const normal = centerPath.getNormalAt(i).multiply(tickSize);
const tickEnd = rulerPathPoint.add(normal);
var tick = new paper.Path.Line({
from: rulerPathPoint,
to: tickEnd,
strokeColor: '#cbd5e1'
});
if (i % majorTickInterval === 0) {
tick.strokeWidth = 1;
tick.strokeColor = "#475569";
// 使用 rulerPathPoint 或 point 大不相同,可以进行调试
tick.segments[1].point = rulerPathPoint.add(normal.multiply(1.5)); // 每到100,增长刻度
// Add labels for major ticks
var text = new paper.PointText({
point: tick.segments[0].point.add(new paper.Point(3, -5)), // Offset text a bit for visibility
content: (i / scale + start).toString(),
fillColor: 'black',
fontFamily: 'Courier New',
fontSize: 8
});
text.rotate(90, tick.segments[0].point); // Rotate text to align with tick
}
}
}
// 在中心线画标尺
for (let i = 0; i <= totalLength; i += tickInterval) {
const point = path.getPointAt(i);
const normal = path.getNormalAt(i).multiply(tickSize);
const tickEnd = point.add(normal);
var tick = new paper.Path.Line({
from: point,
to: tickEnd,
strokeColor: '#cbd5e1'
});
if (i % majorTickInterval === 0) {
tick.strokeWidth = 1;
tick.strokeColor = "#475569";
tick.segments[1].point = point.add(normal.multiply(1.5)); // 每到100,增长刻度
// Add labels for major ticks
var text = new paper.PointText({
point: tick.segments[0].point.add(new paper.Point(3, -5)), // Offset text a bit for visibility
content: (i / scale + start).toString(),
fillColor: 'black',
fontFamily: 'Courier New',
fontSize: 8
});
text.rotate(90, tick.segments[0].point); // Rotate text to align with tick
}
}
}
function initTool() {
var hitOptions = {
segments: true,
stroke: true,
fill: true,
tolerance: 5
};
var segment, path;
var movePath = false;
const tool = new paper.Tool();
tool.onMouseDown = (event) => {
segment = path = null;
var hitResult = paper.project.hitTest(event.point, hitOptions);
if (!hitResult)
return;
if (event.modifiers.shift) {
if (hitResult.type == 'segment') {
hitResult.segment.remove();
};
return;
}
if (hitResult) {
path = hitResult.item;
path.selected = true
if (hitResult.type == 'segment') {
segment = hitResult.segment;
}
}
}
tool.onMouseDrag = (event) => {
if (segment) {
segment.point = segment.point.add(event.delta);
// path.smooth();
} else if (path) {
path.position = path.position.add(event.delta);
}
}
}
paper.view.draw();
function initMoveTool() {
const tool = new paper.Tool();
var lastPoint = null; // 上一次鼠标位置
var dragging = false;
var lastViewCenter;
tool.onMouseDown = (event) => {
// 当鼠标按下时, 记录当前鼠标位置
lastPoint = event.point;
dragging = true;
};
tool.onMouseDrag = (event) => {
if (dragging && lastPoint) {
lastViewCenter = paper.view.center;
const delta = lastPoint.subtract(event.point);
paper.view.center = paper.view.center.add(delta);
lastPoint = event.point.add(paper.view.center.subtract(lastViewCenter));
}
};
tool.onMouseUp = () => {
// 结束拖动
dragging = false;
};
}
function initZoomTool() {
document.querySelector("#myCanvas").addEventListener("wheel", (event) => {
event.preventDefault(); // 阻止默认滚动行为
const delta = event.deltaY > 0 ? -0.02 : 0.02;
const view = paper.view;
const oldZoom = view.zoom;
const newZoom = Math.max(0.1, oldZoom * (1 + delta));
const mousePosition = new paper.Point(event.offsetX, event.offsetY);
// 计算缩放前的鼠标位置到视图中心的向量
const viewPosition = view.viewToProject(mousePosition);
// 设置新的缩放级别
view.zoom = newZoom;
// 重新计算并设置视图中心,以鼠标位置为缩放中心
const scaledPoint = view.viewToProject(mousePosition);
const deltaPoint = viewPosition.subtract(scaledPoint);
view.center = view.center.add(deltaPoint);
});
}
// 重置操作点
function resetHandlePoint() {
const handlePoints = paper.project.getItems({
data: {
isHandlePoint: true
}
});
handlePoints.forEach(x => {
x.bounds.size = initialHandlePointSize.divide(paper.view.zoom);
})
}
function fitCanvas() {
// 获取画布父元素的大小
var parent = paper.view.element.parentNode
var rect = parent.getBoundingClientRect();
const bounds = paper.project.activeLayer.bounds;
const { width, height } = bounds;
var widthScale = (rect.width * 0.9) / width;
var heightScale = (rect.height * 0.9) / height;
const minScale = Math.min(widthScale, heightScale);
const currentScale = paper.view.zoom;
// 重新设置 Paper.js 画布的大小
paper.view.viewSize = new paper.Size(rect.width, rect.height);
var scaleDelta = minScale / currentScale;
paper.view.scale(scaleDelta);
const center = new paper.Point(width / 2, height / 2);
paper.view.center = center;
}
function initDragHandleTool() {
var lastPoint = null; // 上一次鼠标位置
var dragging = false;
var lastViewCenter;
var hitOptions = {
segments: false, // 点
// stroke: true, // 边
fill: true, // 内部
// tolerance: 2,
tolerance: 2,
match: (res) => {
console.log(res)
return res.item.data.isHandlePoint;
},
};
const tool = new paper.Tool();
tool.onMouseDown = (event) => {
path = null;
var hitResult = paper.project.hitTest(event.point, hitOptions);
if (!hitResult) {
lastPoint = event.point;
dragging = true;
return;
}
if (hitResult) {
console.log(hitResult)
path = hitResult.item;
path.selected = true
}
}
tool.onMouseDrag = (event) => {
console.log(11)
if (path) {
path.position = path.position.add(event.delta);
// this.position = event.point;
centerPath.segments[1].point = event.point;
centerPath.segments[0].point.x = event.point.x;
return
}
if (dragging && lastPoint) {
lastViewCenter = paper.view.center;
const delta = lastPoint.subtract(event.point);
paper.view.center = paper.view.center.add(delta);
lastPoint = event.point.add(paper.view.center.subtract(lastViewCenter));
}
}
tool.onMouseUp = (event) => {
path = null
dragging = false;
}
}
initZoomTool()
initCasing(casingData)
addBaseLine()
// fitCanvas()
// initMoveTool()
// addHandle()
initDragHandleTool()
drawScale(centerPath, 1);
</script>
</body>
</html>