前言
分形是一种具有自相似性的几何图形或数学对象。它的特点是无论在任何放大或缩小的尺度下,都能够看到与整体相似的图形。分形的形状可以非常复杂,常常具有分支、重复的图案,以及细节层次丰富的结构。
分形在自然界中广泛存在,如云朵、树枝、山脉、海岸线等,它们都展现了分形的特性。分形也在科学和艺术中得到广泛应用。在科学中,分形可以用来描述复杂的自然现象,如心电图、地理地貌、经济市场的波动等。在艺术中,分形可以用来生成艺术作品,如绘画、音乐、电影等,通过分形的自相似性和美学特性,创造出独特的视觉和听觉体验。
分形理论的发展起源于20世纪70年代,由数学家贝诺瓦·曼德布罗特和两个波兰数学家兹比格涅夫·尼科拉斯·普鲁斯金斯基及尼科拉斯·尼可夫发现和研究。分形理论为我们提供了一种新的视角来理解自然界和艺术中的复杂性,并且对人类的认知和创造力产生了深远的影响。
科赫雪花(也叫科赫雪花曲线)是一种分形图形,由瑞典数学家科尔·科赫(Helge von Koch)于1904年提出的。它是从一个等边三角形开始构建的,然后将每条边分成三等分,并在其中一等分边上构建一个新的等边三角形。不断重复这个过程,每次都在已经构建好的图形的每条边上重复。最终,会得到一个形状复杂且具有自相似性的图形。
科赫雪花具有许多有趣的属性。首先,它有无限的边界长度,也就是说,无论你如何继续细分边界,都无法得到一个封闭的图形。其次,科赫雪花的面积虽然有限,但无法通过传统的几何方法来计算。最后,科赫雪花是自相似的,即它的任何一部分都与整体具有相似的形状。
科赫雪花是分形几何学的一个经典例子,它展示了分形的一些基本概念和性质。
分形是一种具有自相似性的图形,无论在任何尺度上观察,都能看到相似的形状。这种自相似性是分形图形与传统几何图形的重要区别之一,使得分形图形在自然界和科学中的许多领域都有广泛的应用。
用three.js实现科赫雪花
<!DOCTYPE html>
<html class="fullscreen" lang="zh-CN">
<head>
<title>snow flake</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" href="">
<style>
.fullscreen {
margin: 0px;
padding: 0px;
width: 100vw;
height: 100vh;
overflow: hidden;
}
html, body {
overflow: hidden;
font-family: '微软雅黑', sans-serif;
color: #2f2f2f;
}
/* Page Loader */
.page-loader {
width: calc(100vw - 15px);
height: calc(100vh - 15px);
background-color: #f5e6d3;
position: absolute;
z-index: 100;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
}
.loader-container {
width: 200px;
height: 200px;
display: flex;
justify-content: center;
align-items: center;
}
.base {
position: absolute;
width: 90px;
height: 51px;
}
.base-two {
transform: rotate(60deg)
}
.base-three {
transform: rotate(-60deg)
}
.loader-hex-edge {
position: absolute;
width: 4px;
height: 0%;
background: #00738B;
z-index: 101;
}
.h1 {
left: 0;
animation: AniOne 7.2s ease infinite;
}
.h2 {
right: 0;
animation: AniTwo 7.2s ease .6s infinite;
}
.h3 {
right: 0;
animation: AniTwo 7.2s ease 1.2s infinite;
}
.h4 {
right: 0;
animation: AniTwo 7.2s ease 1.8s infinite;
}
.h5 {
left: 0;
animation: AniOne 7.2s ease 2.4s infinite;
}
.h6 {
left: 0;
animation: AniOne 7.2s ease 3s infinite;
}
@keyframes AniOne {
0% {
bottom: 0;
height: 0;
}
6.944444444% {
bottom: 0;
height: 100%;
}
50% {
top: 0;
height: 100%;
}
59.944444433% {
top: 0;
height: 0;
}
}
@keyframes AniTwo {
0% {
top: 0;
height: 0;
}
6.944444444% {
top: 0;
height: 100%;
}
50% {
bottom: 0;
height: 100%;
}
59.944444433% {
bottom: 0;
height: 0;
}
}
/* Canvas */
.container {
width: calc(100vw - 1px);
height: calc(100vh - 1px);
position: absolute;
z-index: 0;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.canvas {
border-radius: 1px;
}
</style>
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.162.0/+esm",
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.162.0/examples/jsm/",
"lil-gui": "https://threejsfundamentals.org/3rdparty/dat.gui.module.js",
"@tweenjs/tween.js": "https://cdn.jsdelivr.net/npm/@tweenjs/tween.js@23.1.1/dist/tween.esm.js"
}
}
</script>
</head>
<body class="fullscreen">
<div class="page-loader">
<div class="loader-container">
<div class="base">
<span class="loader-hex-edge h6"></span>
<span class="loader-hex-edge h3"></span>
</div>
<div class="base base-two">
<span class="loader-hex-edge h1"></span>
<span class="loader-hex-edge h4"></span>
</div>
<div class="base base-three">
<span class="loader-hex-edge h5"></span>
<span class="loader-hex-edge h2"></span>
</div>
</div>
</div>
<div class="fullscreen container">
<canvas class="fullscreen canvas"></canvas>
</div>
<script type="module">
import * as THREE from 'three';
import * as TWEEN from '@tweenjs/tween.js';
import { GUI } from 'lil-gui';
const _changeEvent = { type: 'change' };
const _startEvent = { type: 'start' };
const _endEvent = { type: 'end' };
const _ray = new THREE.Ray();
const _plane = new THREE.Plane();
const TILT_LIMIT = Math.cos( 70 * THREE.MathUtils.DEG2RAD );
class OrbitControls extends THREE.EventDispatcher {
constructor( object, domElement ) {
super();
this.object = object;
this.domElement = domElement;
this.domElement.style.touchAction = 'none'; // disable touch scroll
// Set to false to disable this control
this.enabled = true;
// "target" sets the location of focus, where the object orbits around
this.target = new THREE.Vector3();
// Sets the 3D cursor (similar to Blender), from which the maxTargetRadius takes effect
this.cursor = new THREE.Vector3();
// How far you can dolly in and out ( PerspectiveCamera only )
this.minDistance = 0;
this.maxDistance = Infinity;
// How far you can zoom in and out ( OrthographicCamera only )
this.minZoom = 0;
this.maxZoom = Infinity;
// Limit camera target within a spherical area around the cursor
this.minTargetRadius = 0;
this.maxTargetRadius = Infinity;
// How far you can orbit vertically, upper and lower limits.
// Range is 0 to Math.PI radians.
this.minPolarAngle = 0; // radians
this.maxPolarAngle = Math.PI; // radians
// How far you can orbit horizontally, upper and lower limits.
// If set, the interval [ min, max ] must be a sub-interval of [ - 2 PI, 2 PI ], with ( max - min < 2 PI )
this.minAzimuthAngle = - Infinity; // radians
this.maxAzimuthAngle = Infinity; // radians
// Set to true to enable damping (inertia)
// If damping is enabled, you must call controls.update() in your animation loop
this.enableDamping = false;
this.dampingFactor = 0.05;
// This option actually enables dollying in and out; left as "zoom" for backwards compatibility.
// Set to false to disable zooming
this.enableZoom = true;
this.zoomSpeed = 1.0;
// Set to false to disable rotating
this.enableRotate = true;
this.rotateSpeed = 1.0;
// Set to false to disable panning
this.enablePan = true;
this.panSpeed = 1.0;
this.screenSpacePanning = true; // if false, pan orthogonal to world-space direction camera.up
this.keyPanSpeed = 7.0; // pixels moved per arrow key push
this.zoomToCursor = false;
// Set to true to automatically rotate around the target
// If auto-rotate is enabled, you must call controls.update() in your animation loop
this.autoRotate = false;
this.autoRotateSpeed = 2.0; // 30 seconds per orbit when fps is 60
// The four arrow keys
this.keys = { LEFT: 'ArrowLeft', UP: 'ArrowUp', RIGHT: 'ArrowRight', BOTTOM: 'ArrowDown' };
// Mouse buttons
this.mouseButtons = { LEFT: THREE.MOUSE.ROTATE, MIDDLE: THREE.MOUSE.DOLLY, RIGHT: THREE.MOUSE.PAN };
// Touch fingers
this.touches = { ONE: THREE.TOUCH.ROTATE, TWO: THREE.TOUCH.DOLLY_PAN };
// for reset
this.target0 = this.target.clone();
this.position0 = this.object.position.clone();
this.zoom0 = this.object.zoom;
// the target DOM element for key events
this._domElementKeyEvents = null;
//
// public methods
//
this.getPolarAngle = function () {
return spherical.phi;
};
this.getAzimuthalAngle = function () {
return spherical.theta;
};
this.getDistance = function () {
return this.object.position.distanceTo( this.target );
};
this.listenToKeyEvents = function ( domElement ) {
domElement.addEventListener( 'keydown', onKeyDown );
this._domElementKeyEvents = domElement;
};
this.stopListenToKeyEvents = function () {
this._domElementKeyEvents.removeEventListener( 'keydown', onKeyDown );
this._domElementKeyEvents = null;
};
this.saveState = function () {
scope.target0.copy( scope.target );
scope.position0.copy( scope.object.position );
scope.zoom0 = scope.object.zoom;
};
this.reset = function () {
scope.target.copy( scope.target0 );
scope.object.position.copy( scope.position0 );
scope.object.zoom = scope.zoom0;
scope.object.updateProjectionMatrix();
scope.dispatchEvent( _changeEvent );
scope.update();
state = STATE.NONE;
};
// this method is exposed, but perhaps it would be better if we can make it private...
this.update = function () {
const offset = new THREE.Vector3();
// so camera.up is the orbit axis
const quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) );
const quatInverse = quat.clone().invert();
const lastPosition = new THREE.Vector3();
const lastQuaternion = new THREE.Quaternion();
const lastTargetPosition = new THREE.Vector3();
const twoPI = 2 * Math.PI;
return function update( deltaTime = null ) {
const position = scope.object.position;
offset.copy( position