正多面体,是指多面体的各个面都是全等的正多边形,并且各个多面角都是全等的多面角。其中面数最少的是正四面体,面数最多的是正二十面体。有些化学物质的结晶体呈正多面体的形状,如食盐的结晶体是正六面体,明矾的结晶体是正八面体。

仅有的五种正多面体,即是正四面体、正六面体、正八面体、正十二面体和正二十面体。
正多面体的各种参数如下表所示。

下面我们用js来绘制这些神奇的3D正多面体图形,效果图如下:

在线demo地址

代码:index.js

const { cos, sin, acos, atan, sqrt, PI } = Math
const TAU = 2 * PI
const canvas = document.getElementsByTagName('canvas')[0]
const _ = canvas.getContext('2d')const width = canvas.width = window.innerWidth
const height = canvas.height = window.innerHeight
const nPolyhedron = Math.round(24 * width / 1920)const backgroundColor = '#3f51b5'
const verticeColor = 'white'
const faceColor = '#3f51b5'const oOrigin = { x: 0, y: 0, z: 0 }
const forward = { x: 0, y: 0, z: 1 }const nameToScale = {hexahedron: [48, 64],tetraedron: [48, 64],octahedron: [48, 64],icosahedron: [32, 48],dodecahedron: [12, 32],
}const patrons = [createPolyhedron(4, PI / 2, 'hexahedron'),createPolyhedron(3, acos(1 / 3), 'tetraedron'),createPolyhedron(3, acos(-1 / 3), 'octahedron'),createPolyhedron(3, acos(-sqrt(5) / 3), 'icosahedron'),createPolyhedron(5, 2 * atan((1 + sqrt(5)) / 2), 'dodecahedron'),
]const polyhedrons = []for (let i = 0; i < nPolyhedron; i++) {polyhedrons.push(createPolyhedronInstance(randomArray(patrons)))
}function draw() {_.fillStyle = backgroundColor_.fillRect(0, 0, width, height)polyhedrons.forEach(polyhedron => {_.strokeStyle = verticeColor_.fillStyle = faceColorpolyhedron.faces.map(({ nodes, center }) => ({ nodes, center: applyRotations(center, polyhedron) })).sort((a, b) => a.center.z < b.center.z ? -1 : 1).map(({ nodes }) => nodes.map(node => applyRotations(node, polyhedron))).forEach(nodes => {_.beginPath()_.moveTo(nodes[0].x + polyhedron.x, nodes[0].y + polyhedron.y)for (let i = 1; i < nodes.length; i++) {_.lineTo(nodes[i].x + polyhedron.x, nodes[i].y + polyhedron.y)}_.closePath()_.stroke()_.fill()})})
}/* ---Update
--- */function update() {const newPolyhedrons = []polyhedrons.forEach((polyhedron, i) => {polyhedron.a += polyhedron.dapolyhedron.b += polyhedron.dbpolyhedron.c += polyhedron.dcpolyhedron.x += polyhedron.dxpolyhedron.y += polyhedron.dyif (polyhedron.x > width + polyhedron.scaleFactor ||polyhedron.x < -polyhedron.scaleFactor ||polyhedron.y > height + polyhedron.scaleFactor ||polyhedron.y < -polyhedron.scaleFactor) {polyhedrons.splice(i, 1)const nextPolyhedron = createPolyhedronInstance(randomArray(patrons))if (Math.random() < 0.5) {nextPolyhedron.x = nextPolyhedron.dx < 0 ? width + nextPolyhedron.scaleFactor : -nextPolyhedron.scaleFactor}else {nextPolyhedron.y = nextPolyhedron.dy < 0 ? height + nextPolyhedron.scaleFactor : -nextPolyhedron.scaleFactor}newPolyhedrons.push(nextPolyhedron)}})polyhedrons.push(...newPolyhedrons)
}/* ---Polyhedron creation
--- */function createPolyhedron(nSides, dihedralAngle, name) {let faces = [createPolygonNodes(nSides, oOrigin, forward)]const centers = [oOrigin]const queue = [{center: oOrigin,nodes: faces[0],},]while (true) {if (!queue.length) breakconst { center, nodes } = queue.shift()for (let i = 0; i < nSides; i++) {const a = nodes[i]const b = nodes[i === nSides - 1 ? 0 : i + 1]const pivot = createCenter(a, b)const p = createVector(center, pivot)const nextCenter = translatePoint(pivot, p)const rotatedCenter = rotatePointAroundAxis(nextCenter, a, b, PI - dihedralAngle)if (centers.every(o => norm(createVector(o, rotatedCenter)) > 0.01)) {const normalVector = crossProduct(p, createVector(a, b))const polygonNodes = createPolygonNodes(nSides, nextCenter, normalVector, a).map(node => rotatePointAroundAxis(node, a, b, PI - dihedralAngle))faces.push(polygonNodes)centers.push(rotatedCenter)queue.push({center: rotatedCenter,nodes: polygonNodes,})}}}const centersVector = centers.reduce((accumulator, node) => addVectors(accumulator, node), { x: 0, y: 0, z: 0 })const polyhedronCenterTranslation = scaleVector(centersVector, -1 / centers.length)faces = faces.map((nodes, i) => ({center: centers[i],nodes: nodes.map(node => translatePoint(node, polyhedronCenterTranslation)),}))faces.name = namereturn faces
}function createPolygonNodes(nSides, origin, normalVector, firstNode) {const angle = TAU / nSidesconst distanceFromCenter = sqrt(1 / 2 / (1 - cos(angle)))const nodes = [firstNode || { x: distanceFromCenter + origin.x, y: origin.y, z: origin.z }]for (let i = 1; i < nSides; i++) {nodes.push(rotatePointAroundAxis(nodes[i - 1],origin,addVectors(origin, normalVector),angle))}return nodes
}function createPolyhedronInstance(patron) {// eslint-disable-next-line prefer-spreadconst scaleFactor = randomRange.apply(null, nameToScale[patron.name])const params = suffle([0, randomRange(0, TAU), randomRange(0, TAU)])const dParams = suffle([0, randomRange(0, PI / 64), randomRange(0, PI / 64)])return {faces: patron.map(({ center, nodes }) => ({center,nodes: nodes.map(node => scaleVector(node, scaleFactor)),})),scaleFactor,a: params[0],b: params[1],c: params[2],da: dParams[0],db: dParams[1],dc: dParams[2],x: randomInteger(0, width),y: randomInteger(0, height),dx: randomArray([-3, -2, -1, 1, 2, 3]),dy: randomArray([-3, -2, -1, 1, 2, 3]),}
}/* ---Math helpers
--- */function randomArray(a) {return a[Math.floor(Math.random() * a.length)]
}function randomRange(a, b) {return Math.random() * (b - a) + a
}function randomInteger(a, b) {return Math.floor(randomRange(a, b))
}function suffle(a) {return a.sort(() => Math.random() < 0.5 ? -1 : 1)
}function multiplyMatrices(a, b) {const c = []for (let i = 0; i < a.length; i++) {const row = []for (let j = 0; j < b[0].length; j++) {let sum = 0for (let k = 0; k < b.length; k++) {sum += a[i][k] * b[k][j]}row.push(sum)}c.push(row)}return c
}function createCenter(a, b) {return {x: (b.x + a.x) / 2,y: (b.y + a.y) / 2,z: (b.z + a.z) / 2,}
}function createVector(a, b) {return {x: b.x - a.x,y: b.y - a.y,z: b.z - a.z,}
}function translatePoint(p, v) {return addVectors(p, v)
}function addVectors(u, v) {return {x: u.x + v.x,y: u.y + v.y,z: u.z + v.z,}
}function scaleVector({ x, y, z }, factor) {return {x: x * factor,y: y * factor,z: z * factor,}
}function norm({ x, y, z }) {return sqrt(x * x + y * y + z * z)
}function normalizeVector(v) {const n = norm(v)return {x: v.x / n,y: v.y / n,z: v.z / n,}
}function dotProduct(a, b) {return a.x * b.x + a.y * b.y + a.z * b.z
}function crossProduct(a, b) {return {x: a.y * b.z - a.z * b.y,y: a.z * b.x - a.x * b.z,z: a.x * b.y - a.y * b.x,}
}function projectPointOnAxis(p, a, b) {const ab = createVector(a, b)const lambda = dotProduct(ab, createVector(a, p)) / dotProduct(ab, ab)return addVectors(a, scaleVector(ab, lambda))
}function rotatePointAroundAxis(p, a, b, angle) {const { x, y, z } = normalizeVector(createVector(a, b))const translationVector = projectPointOnAxis(p, a, b)const pp = createVector(translationVector, p)const c = cos(angle)const s = sin(angle)const R = [[c + x * x * (1 - c), x * y * (1 - c) - z * s, x * z * (1 - c) + y * s],[y * x * (1 - c) + z * s, c + y * y * (1 - c), y * z * (1 - c) - x * s],[z * x * (1 - c) - y * s, z * y * (1 - c) + x * s, c + z * z * (1 - c)],]const X = [[pp.x],[pp.y],[pp.z],]const Y = multiplyMatrices(R, X)return {x: Y[0][0] + translationVector.x,y: Y[1][0] + translationVector.y,z: Y[2][0] + translationVector.z,}
}function applyRotations({ x, y, z }, { a, b, c }) {const ca = cos(a)const sa = sin(a)const cb = cos(b)const sb = sin(b)const cc = cos(c)const sc = sin(c)const rotateX = [[1, 0, 0],[0, ca, -sa],[0, sa, ca],]const rotateY = [[cb, 0, -sb],[0, 1, 0],[sb, 0, cb],]const rotateZ = [[cc, -sc, 0],[sc, cc, 0],[0, 0, 1],]const X = [[x], [y], [z]]const Y = multiplyMatrices(rotateZ, multiplyMatrices(rotateY, multiplyMatrices(rotateX, X)))return {x: Y[0][0],y: Y[1][0],z: Y[2][0],}
}/* ---Visualization loop
--- */function step() {update()draw()requestAnimationFrame(step)
}requestAnimationFrame(step)

js绘制3D正多面体(正六面体,正四面体,正八面体,正十二面体,正二十面体)相关推荐

  1. Django开发数据可视化大屏-JS绘制大屏动态背景-(视图模板制作)

    查看本文前请先查看 Django开发数据可视化大屏-项目启动配置 通过前面的文章,我们已经创建了一个Django简单项目,并且做了相关的配置,今天我们来制作视图模板,通过JS绘制3D动态背景效果. 我 ...

  2. 【圣诞节限定】今天教你如何用Html+JS+CSS绘制3D动画圣诞树

    一.前言 应CSDN的邀请,这次给大家展示一波,如何用H5技术绘制3D圣诞树. 二.创意名 只采用简单的Html+JS+CSS 技术绘制. 三.效果展示 圣诞树修过如下: 四.编码实现 将源码复制保存 ...

  3. js绘制超炫酷3D化学元素周期表

    化学元素周期表(Periodic table of elements)是根据原子量从小至大排序的化学元素列表.列表大体呈长方形,某些元素周期中留有空格,使特性相近的元素归在同一族中,如碱金属元素.碱土 ...

  4. 3d饼图 vue_在Vue中如何使用highCharts绘制3d饼图

    本篇文章主要介绍了在Vue中使用highCharts绘制3d饼图的方法,现在分享给大家,也给大家做个参考. highcharts是国外知名基于javascript的图表库.由于中文官网的vue中使用h ...

  5. 【视觉高级篇】20 # 如何用WebGL绘制3D物体?

    说明 [跟月影学可视化]学习笔记. 如何用 WebGL 绘制三维立方体 我们知道立方体有8个顶点,6个面,在 WebGL 中,需要用 12 个三角形来绘制它.把每个面的顶点分开,需要 24 个顶点. ...

  6. 当python遇上echarts (三)绘制3D图表

    当python遇上echarts (二)绘制基本图表 文章目录 前言 3D图形配置项及方法 Grid3DOpts:三维笛卡尔坐标系配置项 Axis3DOpts:三维坐标轴配置项 add(): 共有的方 ...

  7. highcharts绘制3D图表

    最近做项目中需求要求3D图表展现,开始比较懵逼,后来网上找的了highcharts图表,之前都是用的百度的echarts做平面图 一.基本介绍 1.3d官网案例 2.参数的配置 3.官方的其他案例 二 ...

  8. P5.JS绘制动态图形

    P5.JS绘制动态图形 一.平台 第一次使用p5.js进行码绘,此次直接使用P5.JS官网的在线编辑器进行编写,完成后点击file->download即可保存到本地. 在正式绘制之前,我经过小小 ...

  9. echart 广州3d_echarts绘制3D城市地图

    使用echarts 绘制 中国地图/各省地图/市级地图 的3D地图 先上效果图javascript 中国 html 四川省 java 成都市 git 3D地图说明 经过使用 series-map3D ...

  10. echarts绘制3D地图

    使用echarts和echarts-gl绘制3D地图,下面是一个demo演示,echarts版本4.6.0,echarts-gl版本1.1.2,demo启动前先自行安装,至于背景嘛,大家就自行放个背景 ...

最新文章

  1. deepsort原理快速弄懂——时效比最高的
  2. ambari初始化登陆账号/密码假如不是admin/admin
  3. python编程 语言-Python——最美丽的编程语言
  4. 吴恩达 coursera ML 第八课总结+作业答案
  5. ios签名软件_苹果企业签名常常掉怎样处理【苹果签名吧】
  6. 病毒c语言代码大全,谁有C语言编写的病毒源代码?
  7. java 异步上传一张图片,java异步上传图片示例
  8. 浅谈K短路算法(KSP)之二(YEN .J算法求解)
  9. surfer画世界频率分布图(等高线、地点标注)
  10. Java对比两个json 的数据结构和内容是否一样
  11. redis主从故障自动切换IP漂移
  12. Altium Designer使用--出现的错误报告
  13. 题解1205汉诺塔问题
  14. 大巧不工Web前端设计修炼之道——(8)浅谈Web发展的未来
  15. docker日志显示时间时区错误,时区UST问题/群晖docker日志时间不正确 寻找解答过程
  16. ME21N采购订单增强-税码字段默认值
  17. Vue返回上一页保留数据
  18. Kronecker积及其等式性质
  19. 第12课 习题讲解
  20. java字符串校验,过滤筛选中英文符号

热门文章

  1. 全国计算机二级ps考什么,计算机二级ps考试内容有哪些
  2. SQL数据库学习总结(一)
  3. 把rmvb格式转化为avi格式
  4. 移动端开发旅游预约_套餐列表页面动态展示_套餐详情页面动态展示
  5. codesmith mysql 注释_代码生成工具:CodeSmith 安装、改造适配Mysql 字段注释、DLL修改 及批量生成实体类代码...
  6. Ubuntu软件商店下载速度慢
  7. 在线购物系统1.1设计类图
  8. AspNetPager分页控制
  9. 计算机网络管理员期末,计算机网络管理员期中考试统一试题(A)
  10. 计算机网络管理员下午试题答案,(完整版)计算机网络管理员考试试题库和答案(13页)-原创力文档...