情人節(jié)的由來可以追溯到古羅馬時期。
當時會舉行一個叫做“Lupercalia”的節(jié)日,這個節(jié)日是為了紀念羅馬神話中的牧羊神Lupercus。
在這個節(jié)日里,男性會赤膊跑到街上,用皮鞭抽打女性,以此來祈求豐收和健康。
后來,羅馬天主教會將這個節(jié)日改稱為“圣瓦倫丁節(jié)”,以紀念瓦倫丁大主教,他曾經(jīng)禁止這種抽打女性的習俗。
現(xiàn)在的情人節(jié),則是從這個節(jié)日演變而來,它是一個紀念愛情的節(jié)日。在情人節(jié),男女方會在這一天互贈禮物(玫瑰,巧克力或者賀卡)以表達愛意或者友好。
今天我們就來看看那些浪漫的程序員們是如何表達自己的愛意的吧!
在坐標軸上畫愛心
笛卡爾與心形函數(shù)的故事始于17世紀,當時笛卡爾正在研究函數(shù)的性質(zhì),他發(fā)現(xiàn)了一個特殊的函數(shù),它的圖像是一個心形,他稱之為“心形函數(shù)”。笛卡爾把這個函數(shù)命名為“愛情函數(shù)”,以此來表達他對愛情的熱愛。后來,這個函數(shù)被用來表示愛情,成為情人節(jié)的象征,也成為現(xiàn)代數(shù)學(xué)中的一個重要概念。
現(xiàn)在,我們可以在python中使用matplotlib來把這個函數(shù)畫出來:
import matplotlib.pyplot as plt
import numpy as np
#生成從-1到1的以為數(shù)組作為x軸的數(shù)據(jù)
x_data1 = np.linspace(-1,1,1000)
#根據(jù)心形公式,得到y(tǒng)的表達式,由于有正負之分,故分開表示
y_data1 = np.sqrt(1 - x_data1**2) + pow(np.abs(x_data1),float(2)/float(3))
y_data2 = -np.sqrt(1 - x_data1**2)+ pow(np.abs(x_data1),float(2)/float(3))
#設(shè)置空圖表,并把兩組數(shù)據(jù)已散點圖的形式畫在空圖表上
fig = plt.figure()
plt.scatter(x_data1, y_data1,color = 'red')
plt.scatter(x_data1, y_data2,color = 'red')
#設(shè)置坐標軸的顯示范圍
plt.xlim(-1.25,1.25)
plt.ylim(-1.2,1.7)
#得到坐標軸信息
ax = plt.gca()
#設(shè)置坐標軸顏色,把右邊和頂部的坐標設(shè)置為沒有顏色
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
ax.spines['bottom'].set_color('white')
ax.spines['left'].set_color('white')
#設(shè)置坐標軸的位置
ax.spines['left'].set_position(('data',0))
ax.spines['bottom'].set_position(('data',0))
#設(shè)置刻度的位置
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('left')
#設(shè)置刻度的相關(guān)參數(shù),依次包括:坐標軸名稱,字體大小,字體傾斜角度,字體顏色
ax.tick_params(axis="x", labelsize=18, labelrotation=0, labelcolor="white")
ax.tick_params(axis="y", labelsize=18, labelrotation=0, labelcolor="white")
#設(shè)置坐標軸線的寬度
ax.spines['bottom'].set_linewidth(3)
ax.spines['left'].set_linewidth(3)
#設(shè)置圖片的背景
ax.set_facecolor('xkcd:black')
#設(shè)置圖表的長寬
fig.set_figheight(15)
fig.set_figwidth(15)
#設(shè)置
ax.grid(True, linestyle='-.')
plt.show()
#保存繪制的圖形到默認的目錄
#fig.savefig('heart.png', dpi=500)
代碼的運行結(jié)果如下所示:
另外,關(guān)于心形函數(shù)還有3D版本,代碼如下所示:
import bbox as bbox
import matplotlib.pyplot as plt
import numpy as np
def heart_3d(x, y, z):
return (x ** 2 + (9 / 4) * y ** 2 + z ** 2 - 1) ** 3 - x ** 2 * z ** 3 - (9 / 80) * y ** 2 * z ** 3
def heart_3d_2(x, y, z):
return (2 * x ** 2 + 2 * y ** 2 + z ** 2 - 1) ** 3 - 0.1 * x ** 2 * z ** 3 - y ** 2 * z ** 3
def plot_implicit(fn, bbox=(-1.5, 1.5)):
xmin, xmax, ymin, ymax, zmin, zmax = bbox * 3
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
A = np.linspace(xmin, xmax, 100)
B = np.linspace(xmin, xmax, 40)
A1, A2 = np.meshgrid(A, A)
for z in B:
X, Y = A1, A2
Z = fn(X, Y, z)
cset = ax.contour(X, Y, Z + z, [z], zdir='z', colors=('r',))
for y in B:
X, Z = A1, A2
Y = fn(X, y, Z)
cset = ax.contour(X, Y + y, Z, [y], zdir='y', colors=('red',))
for x in B:
Y, Z = A1, A2
X = fn(x, Y, Z)
cset = ax.contour(X + x, Y, Z, [x], zdir='x', colors=('red',))
ax.set_zlim3d(zmin, zmax)
ax.set_xlim3d(xmin, xmax)
ax.set_ylim3d(ymin, ymax)
#改變視角
ax.view_init(elev=5., azim=-76)
# 取消坐標軸顯示
plt.axis('off')
plt.show()
if __name__ == '__main__':
plot_implicit(heart_3d)
運行結(jié)果如下所示:
使用matplotlib繪制漂亮的花花
matplotlib不止可以用來畫愛心,如果對3D建模感興趣的,可以學(xué)一些python繪制3D花的原理,以下是使用python在matplotlib中繪制玫瑰花的代碼案例:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
# 將相位向后移動了6*pi
[x, t] = np.meshgrid(np.array(range(25)) / 24.0, np.arange(0, 575.5, 0.5) / 575 * 20 * np.pi + 4*np.pi)
p = (np.pi / 2) * np.exp(-t / (8 * np.pi))
# 添加邊緣擾動
change = np.sin(15*t)/150
# 將t的參數(shù)減少,使花瓣的角度變大
u = 1 - (1 - np.mod(3.3 * t, 2 * np.pi) / np.pi) ** 4 / 2 + change
y = 2 * (x ** 2 - x) ** 2 * np.sin(p)
r = u * (x * np.sin(p) + y * np.cos(p))
h = u * (x * np.cos(p) - y * np.sin(p))
c= plt.get_cmap('Reds')
surf = ax.plot_surface(r * np.cos(t), r * np.sin(t), h, rstride=1, cstride=1,
cmap= c, linewidth=0, antialiased=True)
plt.show()
運行結(jié)果如下所示:
使用turtle繪制圖案
turtle是一個python繪圖庫,我們可以通過代碼來控制turtle來繪制一些圖形,比如一個簡單的愛心或者一朵美麗的玫瑰花,愛心的繪制比較簡單:
import turtle #導(dǎo)入turtle庫
turtle.pensize(4)#設(shè)置畫筆像素為4像素
turtle.pencolor("red")#設(shè)置畫筆顏色為紅色
turtle.fillcolor("pink")#設(shè)置填充顏色為粉紅色
turtle.begin_fill()#開始填充
#開始繪制愛心
turtle.left(135)
turtle.forward(100)
turtle.circle(-50,180)#第一個半圓
turtle.left(90)
turtle.circle(-50,180)#第二個半圓
turtle.forward(100)
turtle.end_fill()#結(jié)束填充
turtle.done()
運行結(jié)果如下所示:
玫瑰花的繪制比較復(fù)雜:
from turtle import *
import time
#初始化玫瑰
#畫布大小
setup(600,800,0,0)
speed(0)
penup() # 提起畫筆
seth(90) #朝向90度
fd(340) #向前移動指定的距離
seth(0)
pendown() #放下畫筆
#開始畫
speed(5) #畫筆移動速度為5秒
begin_fill() #開始填充
fillcolor('red') #為紅色
circle(50,30) #畫一個半徑為50,弧度為30的圓
for i in range(10):
fd(1)
left(10) #逆時針轉(zhuǎn)動畫筆10度
circle(40,40)
for i in range(6):
fd(1)
left(3)
circle(80,40)
for i in range(20):
fd(0.5)
left(5)
circle(80,45)
for i in range(10):
fd(2)
left(1)
circle(80,25)
for i in range(20):
fd(1)
left(4)
circle(50,50)
time.sleep(0.1)
circle(120,55)
speed(3)
seth(-90)
fd(70)
right(150) #順時針轉(zhuǎn)動畫筆150度
fd(20)
left(140)
circle(140,90)
left(30)
circle(160,100)
left(130)
fd(25)
penup()
right(150)
circle(40,80)
pendown()
left(115)
fd(60)
penup()
left(180)
fd(60)
pendown()
end_fill()
right(120)
circle(-50,50)
circle(-20,90)
speed(1)
fd(75)
speed(1)
circle(90,110)
penup()
left(162)
fd(185)
left(170)
pendown()
circle(200,10)
circle(100,40)
circle(-52,115)
left(20)
circle(100,20)
circle(300,20)
speed(1)
fd(250)
penup()
speed(2)
left(180)
fd(250)
circle(-300,7)
right(80)
circle(200,5)
pendown()
left(60)
begin_fill()
fillcolor('green')
circle(-80,100)
right(90)
fd(10)
left(20)
circle(-63,127)
end_fill()
penup()
left(50)
fd(20)
left(180)
pendown()
circle(200,25)
penup()
right(150)
fd(180)
right(40)
pendown()
begin_fill()
fillcolor('green')
circle(-100,80)
right(150)
fd(10)
left(60)
circle(-80,98)
end_fill()
penup()
left(60)
fd(13)
left(180)
pendown()
speed(1)
circle(-200,23)
exitonclick() #當點擊時退出
運行結(jié)果如下:
使用canvas繪制愛心
在如何使用canvas繪制李峋同款愛心?一文中,我們介紹了如何使用HTML的canvas結(jié)合JavaScript來繪制一個跳動的愛心,現(xiàn)在我們來重溫一下這段代碼:
<html lang="">
<head>
<meta charset="utf-8" />
<title>愛心代碼</title>
<style>
html, body {
height: 100%;
padding: 0;
margin: 0;
background: #000;
}
canvas {
position: absolute;
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<canvas id="pinkboard" width="1873" height="218"></canvas>
<script>
var settings = {
particles: {
length: 500,
duration: 2,
velocity: 100,
effect: -0.75,
size: 32,
},
};
(function () {
var b = 0;
var c = ["ms", "moz", "webkit", "o"];
for (var a = 0; a < c.length && !window.requestAnimationFrame; ++a) {
window.requestAnimationFrame = window[c[a] + "RequestAnimationFrame"];
window.cancelAnimationFrame =
window[c[a] + "CancelAnimationFrame"] ||
window[c[a] + "CancelRequestAnimationFrame"];
}
if (!window.requestAnimationFrame) {
window.requestAnimationFrame = function (h, e) {
var d = new Date().getTime();
var f = Math.max(0, 16 - (d - b));
var g = window.setTimeout(function () {
h(d + f);
}, f);
b = d + f;
return g;
};
}
if (!window.cancelAnimationFrame) {
window.cancelAnimationFrame = function (d) {
clearTimeout(d);
};
}
})();
var Point = (function () {
function Point(x, y) {
this.x = typeof x !== "undefined" ? x : 0;
this.y = typeof y !== "undefined" ? y : 0;
}
Point.prototype.clone = function () {
return new Point(this.x, this.y);
};
Point.prototype.length = function (length) {
if (typeof length == "undefined")
return Math.sqrt(this.x * this.x + this.y * this.y);
this.normalize();
this.x *= length;
this.y *= length;
return this;
};
Point.prototype.normalize = function () {
var length = this.length();
this.x /= length;
this.y /= length;
return this;
};
return Point;
})();
var Particle = (function () {
function Particle() {
this.position = new Point();
this.velocity = new Point();
this.acceleration = new Point();
this.age = 0;
}
Particle.prototype.initialize = function (x, y, dx, dy) {
this.position.x = x;
this.position.y = y;
this.velocity.x = dx;
this.velocity.y = dy;
this.acceleration.x = dx * settings.particles.effect;
this.acceleration.y = dy * settings.particles.effect;
this.age = 0;
};
Particle.prototype.update = function (deltaTime) {
this.position.x += this.velocity.x * deltaTime;
this.position.y += this.velocity.y * deltaTime;
this.velocity.x += this.acceleration.x * deltaTime;
this.velocity.y += this.acceleration.y * deltaTime;
this.age += deltaTime;
};
Particle.prototype.draw = function (context, image) {
function ease(t) {
return --t * t * t + 1;
}
var size = image.width * ease(this.age / settings.particles.duration);
context.globalAlpha = 1 - this.age / settings.particles.duration;
context.drawImage(
image,
this.position.x - size / 2,
this.position.y - size / 2,
size,
size
);
};
return Particle;
})();
var ParticlePool = (function () {
var particles,
firstActive = 0,
firstFree = 0,
duration = settings.particles.duration;
function ParticlePool(length) {
// create and populate particle pool
particles = new Array(length);
for (var i = 0; i < particles.length; i++)
particles[i] = new Particle();
}
ParticlePool.prototype.add = function (x, y, dx, dy) {
particles[firstFree].initialize(x, y, dx, dy);
// handle circular queue
firstFree++;
if (firstFree == particles.length) firstFree = 0;
if (firstActive == firstFree) firstActive++;
if (firstActive == particles.length) firstActive = 0;
};
ParticlePool.prototype.update = function (deltaTime) {
var i;
// update active particles
if (firstActive < firstFree) {
for (i = firstActive; i < firstFree; i++)
particles[i].update(deltaTime);
}
if (firstFree < firstActive) {
for (i = firstActive; i < particles.length; i++)
particles[i].update(deltaTime);
for (i = 0; i < firstFree; i++) particles[i].update(deltaTime);
}
// 移除非活性粒子
while (
particles[firstActive].age >= duration &&
firstActive != firstFree
) {
firstActive++;
if (firstActive == particles.length) firstActive = 0;
}
};
ParticlePool.prototype.draw = function (context, image) {
// 繪制活性粒子
if (firstActive < firstFree) {
for (i = firstActive; i < firstFree; i++)
particles[i].draw(context, image);
}
if (firstFree < firstActive) {
for (i = firstActive; i < particles.length; i++)
particles[i].draw(context, image);
for (i = 0; i < firstFree; i++) particles[i].draw(context, image);
}
};
return ParticlePool;
})();
(function (canvas) {
var context = canvas.getContext("2d"),
particles = new ParticlePool(settings.particles.length),
particleRate =
settings.particles.length / settings.particles.duration, // particles/sec
time;
// 用-PI<=t<=PI獲得心臟點
function pointOnHeart(t) {
return new Point(
160 * Math.pow(Math.sin(t), 3),
130 * Math.cos(t) -
50 * Math.cos(2 * t) -
20 * Math.cos(3 * t) -
10 * Math.cos(4 * t) +
25
);
}
// 使用虛擬畫布創(chuàng)建粒子圖像
var image = (function () {
var canvas = document.createElement("canvas"),
context = canvas.getContext("2d");
canvas.width = settings.particles.size;
canvas.height = settings.particles.size;
// helper函數(shù)創(chuàng)建路徑
function to(t) {
var point = pointOnHeart(t);
point.x =
settings.particles.size / 2 +
(point.x * settings.particles.size) / 350;
point.y =
settings.particles.size / 2 -
(point.y * settings.particles.size) / 350;
return point;
}
// 創(chuàng)建路徑
context.beginPath();
var t = -Math.PI;
var point = to(t);
context.moveTo(point.x, point.y);
while (t < Math.PI) {
t += 0.01;
point = to(t);
context.lineTo(point.x, point.y);
}
context.closePath();
// 創(chuàng)建填充
context.fillStyle = "#ea80b0";
context.fill();
// 創(chuàng)建圖像
var image = new Image();
image.src = canvas.toDataURL();
return image;
})();
// 渲染
function render() {
// 下一動畫幀
requestAnimationFrame(render);
var newTime = new Date().getTime() / 1000,
deltaTime = newTime - (time || newTime);
time = newTime;
// 清除畫布
context.clearRect(0, 0, canvas.width, canvas.height);
// 創(chuàng)建新粒子
var amount = particleRate * deltaTime;
for (var i = 0; i < amount; i++) {
var pos = pointOnHeart(Math.PI - 2 * Math.PI * Math.random());
var dir = pos.clone().length(settings.particles.velocity);
particles.add(
canvas.width / 2 + pos.x,
canvas.height / 2 - pos.y,
dir.x,
-dir.y
);
}
// 更新和繪制粒子
particles.update(deltaTime);
particles.draw(context, image);
}
// 處理(重新)畫布的大小
function onResize() {
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
}
window.onresize = onResize;
//延遲渲染
setTimeout(function () {
onResize();
render();
}, 10);
})(document.getElementById("pinkboard"));
</script>
</body>
</html>
他的運行結(jié)果也是比較好看的:
小結(jié)
編程的世界雖然美好,但也不要忘了在佳節(jié)陪伴佳人哦。