어떻게 자바 script 을 이용 하여 부 드 러 운 곡선 을 생 성 하 는 지 상세 하 게 설명 합 니 다.
부 드 러 운 곡선 생 성 은 매우 실 용적 인 기술 이다.
우 리 는 접 는 선 을 그 려 서 컴퓨터 를 부 드 럽 게 연결 시 켜 야 할 때 가 많다.
먼저 최종 효과(빨간색 은 우리 가 입력 한 직선,파란색 은 의합 후의 곡선)의 앞 뒤 를 특수 처리 하여 도형 을 더욱 좋아 보이 게 할 수 있다.)
사 고 를 실현 하 는 것 은 베 세 르 곡선 을 이용 하여 적합 하 게 하 는 것 이다.
베 세 르 곡선 안내
베 어 셀 곡선(영어:B é zier curve)은 컴퓨터 그래 픽 에서 상당히 중요 한 매개 변수 곡선 이다.
이차 베 세 르 곡선
2 차방 베 세 르 곡선의 경 로 는 정점 P0,P1,P2 의 함수 B(t)에 의 해 추적 된다.
세 번 베 세 르 곡선
3 차 곡선 에 대해 선형 베 세 르 곡선 이 묘사 한 중개 점 Q0,Q1,Q2 와 2 차 곡선 이 묘사 한 점 R0,R1 로 구성 할 수 있다.
베 어 셀 곡선 계산 함수
위의 공식 에 의 하면 우 리 는 계산 함 수 를 얻 을 수 있다.
2 급
/**
*
*
* @param {number} p0
* @param {number} p1
* @param {number} p2
* @param {number} t
* @return {*}
* @memberof Path
*/
bezier2P(p0: number, p1: number, p2: number, t: number) {
const P0 = p0 * Math.pow(1 - t, 2);
const P1 = p1 * 2 * t * (1 - t);
const P2 = p2 * t * t;
return P0 + P1 + P2;
}
/**
*
*
* @param {Point} p0
* @param {Point} p1
* @param {Point} p2
* @param {number} num
* @param {number} tick
* @return {*} {Point}
* @memberof Path
*/
getBezierNowPoint2P(
p0: Point,
p1: Point,
p2: Point,
num: number,
tick: number,
): Point {
return {
x: this.bezier2P(p0.x, p1.x, p2.x, num * tick),
y: this.bezier2P(p0.y, p1.y, p2.y, num * tick),
};
}
/**
*
*
* @param {Point} p0
* @param {Point} p1
* @param {Point} p2
* @param {number} [num=100]
* @param {number} [tick=1]
* @return {*}
* @memberof Path
*/
create2PBezier(
p0: Point,
p1: Point,
p2: Point,
num: number = 100,
tick: number = 1,
) {
const t = tick / (num - 1);
const points = [];
for (let i = 0; i < num; i++) {
const point = this.getBezierNowPoint2P(p0, p1, p2, i, t);
points.push({x: point.x, y: point.y});
}
return points;
}
삼계
/**
*
*
* @param {number} p0
* @param {number} p1
* @param {number} p2
* @param {number} p3
* @param {number} t
* @return {*}
* @memberof Path
*/
bezier3P(p0: number, p1: number, p2: number, p3: number, t: number) {
const P0 = p0 * Math.pow(1 - t, 3);
const P1 = 3 * p1 * t * Math.pow(1 - t, 2);
const P2 = 3 * p2 * Math.pow(t, 2) * (1 - t);
const P3 = p3 * Math.pow(t, 3);
return P0 + P1 + P2 + P3;
}
/**
*
*
* @param {Point} p0
* @param {Point} p1
* @param {Point} p2
* @param {Point} p3
* @param {number} num
* @param {number} tick
* @return {*}
* @memberof Path
*/
getBezierNowPoint3P(
p0: Point,
p1: Point,
p2: Point,
p3: Point,
num: number,
tick: number,
) {
return {
x: this.bezier3P(p0.x, p1.x, p2.x, p3.x, num * tick),
y: this.bezier3P(p0.y, p1.y, p2.y, p3.y, num * tick),
};
}
/**
*
*
* @param {Point} p0 { x : number, y : number}
* @param {Point} p1 1 { x : number, y : number}
* @param {Point} p2 2 { x : number, y : number}
* @param {Point} p3 { x : number, y : number}
* @param {number} [num=100]
* @param {number} [tick=1]
* @return {Point []}
* @memberof Path
*/
create3PBezier(
p0: Point,
p1: Point,
p2: Point,
p3: Point,
num: number = 100,
tick: number = 1,
) {
const pointMum = num;
const _tick = tick;
const t = _tick / (pointMum - 1);
const points = [];
for (let i = 0; i < pointMum; i++) {
const point = this.getBezierNowPoint3P(p0, p1, p2, p3, i, t);
points.push({x: point.x, y: point.y});
}
return points;
}
의합 알고리즘문 제 는 어떻게 통제 점 을 얻 느 냐 하 는 것 이다.우 리 는 비교적 간단 한 방법 으로
p1-pt-p2 의 각 이등분선 c1c 2 를 취하 여 이 각 이등분선 c2 를 p2 로 하 는 투영 점 에서 짧 은 변 을 c1-pt c2-pt 의 길이 로 크기 를 조정 하면 이 길 이 는 곡선의 굴곡 정도 로 대충 이해 할 수 있다.
ab 라인 은 여기 서 간단하게 처리 하고 2 단계 곡선 생 성 만 사 용 했 습 니 다->🌈 여 기 는 개인 적 인 생각 대로 처리 할 수 있 습 니 다.
bc 라인 은 abc 로 계 산 된 제어 점 c2 와 bcd 로 계 산 된 제어 점 c3 를 사용 하여 유추 합 니 다.
/**
*
*
* @param {Vector2D} p1
* @param {Vector2D} pt
* @param {Vector2D} p2
* @param {number} [ratio=0.3]
* @return {*}
* @memberof Path
*/
createSmoothLineControlPoint(
p1: Vector2D,
pt: Vector2D,
p2: Vector2D,
ratio: number = 0.3,
) {
const vec1T: Vector2D = vector2dMinus(p1, pt);
const vecT2: Vector2D = vector2dMinus(p1, pt);
const len1: number = vec1T.length;
const len2: number = vecT2.length;
const v: number = len1 / len2;
let delta;
if (v > 1) {
delta = vector2dMinus(
p1,
vector2dPlus(pt, vector2dMinus(p2, pt).scale(1 / v)),
);
} else {
delta = vector2dMinus(
vector2dPlus(pt, vector2dMinus(p1, pt).scale(v)),
p2,
);
}
delta = delta.scale(ratio);
const control1: Point = {
x: vector2dPlus(pt, delta).x,
y: vector2dPlus(pt, delta).y,
};
const control2: Point = {
x: vector2dMinus(pt, delta).x,
y: vector2dMinus(pt, delta).y,
};
return {control1, control2};
}
/**
*
*
* @param {Point []} points
* @param {number} ratio
* @return {*}
* @memberof Path
*/
createSmoothLine(points: Point[], ratio: number = 0.3) {
const len = points.length;
let resultPoints = [];
const controlPoints = [];
if (len < 3) return;
for (let i = 0; i < len - 2; i++) {
const {control1, control2} = this.createSmoothLineControlPoint(
new Vector2D(points[i].x, points[i].y),
new Vector2D(points[i + 1].x, points[i + 1].y),
new Vector2D(points[i + 2].x, points[i + 2].y),
ratio,
);
controlPoints.push(control1);
controlPoints.push(control2);
let points1;
let points2;
//
if (i === 0) {
points1 = this.create2PBezier(points[i], control1, points[i + 1], 50);
} else {
console.log(controlPoints);
points1 = this.create3PBezier(
points[i],
controlPoints[2 * i - 1],
control1,
points[i + 1],
50,
);
}
//
if (i + 2 === len - 1) {
points2 = this.create2PBezier(
points[i + 1],
control2,
points[i + 2],
50,
);
}
if (i + 2 === len - 1) {
resultPoints = [...resultPoints, ...points1, ...points2];
} else {
resultPoints = [...resultPoints, ...points1];
}
}
return resultPoints;
}
사례 코드
const input = [
{ x: 0, y: 0 },
{ x: 150, y: 150 },
{ x: 300, y: 0 },
{ x: 400, y: 150 },
{ x: 500, y: 0 },
{ x: 650, y: 150 },
]
const s = path.createSmoothLine(input);
let ctx = document.getElementById('cv').getContext('2d');
ctx.strokeStyle = 'blue';
ctx.beginPath();
ctx.moveTo(0, 0);
for (let i = 0; i < s.length; i++) {
ctx.lineTo(s[i].x, s[i].y);
}
ctx.stroke();
ctx.beginPath();
ctx.moveTo(0, 0);
for (let i = 0; i < input.length; i++) {
ctx.lineTo(input[i].x, input[i].y);
}
ctx.strokeStyle = 'red';
ctx.stroke();
document.getElementById('btn').addEventListener('click', () => {
let app = document.getElementById('app');
let index = 0;
let move = () => {
if (index < s.length) {
app.style.left = s[index].x - 10 + 'px';
app.style.top = s[index].y - 10 + 'px';
index++;
requestAnimationFrame(move)
}
}
move()
})
부록:Vector2D 관련 코드
/**
*
*
* @class Vector2D
* @extends {Array}
*/
class Vector2D extends Array {
/**
* Creates an instance of Vector2D.
* @param {number} [x=1]
* @param {number} [y=0]
* @memberof Vector2D
* */
constructor(x: number = 1, y: number = 0) {
super();
this.x = x;
this.y = y;
}
/**
*
* @param {number} v
* @memberof Vector2D
*/
set x(v) {
this[0] = v;
}
/**
*
* @param {number} v
* @memberof Vector2D
*/
set y(v) {
this[1] = v;
}
/**
*
*
* @readonly
* @memberof Vector2D
*/
get x() {
return this[0];
}
/**
*
*
* @readonly
* @memberof Vector2D
*/
get y() {
return this[1];
}
/**
*
*
* @readonly
* @memberof Vector2D
*/
get length() {
return Math.hypot(this.x, this.y);
}
/**
*
*
* @readonly
* @memberof Vector2D
*/
get dir() {
return Math.atan2(this.y, this.x);
}
/**
*
*
* @return {*}
* @memberof Vector2D
*/
copy() {
return new Vector2D(this.x, this.y);
}
/**
*
*
* @param {*} v
* @return {*}
* @memberof Vector2D
*/
add(v) {
this.x += v.x;
this.y += v.y;
return this;
}
/**
*
*
* @param {*} v
* @return {*}
* @memberof Vector2D
*/
sub(v) {
this.x -= v.x;
this.y -= v.y;
return this;
}
/**
*
*
* @param {*} a
* @return {Vector2D}
* @memberof Vector2D
*/
scale(a) {
this.x *= a;
this.y *= a;
return this;
}
/**
*
*
* @param {*} rad
* @return {*}
* @memberof Vector2D
*/
rotate(rad) {
const c = Math.cos(rad);
const s = Math.sin(rad);
const [x, y] = this;
this.x = x * c + y * -s;
this.y = x * s + y * c;
return this;
}
/**
*
*
* @param {*} v
* @return {*}
* @memberof Vector2D
*/
cross(v) {
return this.x * v.y - v.x * this.y;
}
/**
*
*
* @param {*} v
* @return {*}
* @memberof Vector2D
*/
dot(v) {
return this.x * v.x + v.y * this.y;
}
/**
*
*
* @return {*}
* @memberof Vector2D
*/
normalize() {
return this.scale(1 / this.length);
}
}
/**
*
*
* @param {*} vec1
* @param {*} vec2
* @return {Vector2D}
*/
function vector2dPlus(vec1, vec2) {
return new Vector2D(vec1.x + vec2.x, vec1.y + vec2.y);
}
/**
*
*
* @param {*} vec1
* @param {*} vec2
* @return {Vector2D}
*/
function vector2dMinus(vec1, vec2) {
return new Vector2D(vec1.x - vec2.x, vec1.y - vec2.y);
}
export {Vector2D, vector2dPlus, vector2dMinus};
총결산자 바스 크 립 트 를 이용 해 매 끄 러 운 곡선 을 만 드 는 방법 에 관 한 글 은 여기까지 입 니 다.더 많은 JS 생 성 매 끄 러 운 곡선 내용 은 예전 의 글 을 검색 하거나 아래 의 관련 글 을 계속 찾 아 보 세 요.앞으로 도 많은 응원 부 탁 드 리 겠 습 니 다!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
[2022.04.19] 자바스크립트 this - 생성자 함수와 이벤트리스너에서의 this18일에 this에 대해 공부하면서 적었던 일반적인 함수나 객체에서의 this가 아닌 오늘은 이벤트리스너와 생성자 함수 안에서의 this를 살펴보기로 했다. new 키워드를 붙여 함수를 생성자로 사용할 때 this는...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.