搜索了一下笛卡尔和瑞典公主的浪漫爱情故事,其中提到了一个心形函数,但是看起来像极坐标系的函数。以下借助大佬的数学公式和其他大佬的代码实现,移植java Swing和android平台进行实现。马上就要2.14号了,祝有情人终成眷属,举案齐眉,相敬如宾,白头偕老。
https://mathworld.wolfram.com/HeartCurve.html
x = 16 sin 3 t x=16,sin^{3}t x=16sin3t
y = 13 cos t − 5 cos ( 2 t ) − 2 cos ( 3 t ) − cos ( 4 t ) y=13,cos t - 5cos(2t)-2cos(3t)-cos(4t) y=13cost−5cos(2t)−2cos(3t)−cos(4t)
效果图如下:
可惜swing的graphics画图方法参数只支持int类型,导致曲线不太平滑,可以在android里面绘制更平滑的曲线,android的画线方法支持浮点数。此曲线的绘制参考了不少大佬的博客代码,在android和swing里面均相应转化成功。后面的坐标轴可以去掉,加上只是为了参考。
如果只是个静态图也有些乏味,尝试让曲线绘制过程变的可见,加个延时。
从顶部中间同时向下绘制
import java.awt.*import javax.swing.JButtonimport javax.swing.Jframeimport javax.swing.JPanel// 动画版的画心,从上边顺时针绘制class HeartframeKt2 : Jframe() { init { val panel = HeartPanel() val jButton = JButton("refresh") add(panel, BorderLayout.CENTER) add(jButton, BorderLayout.SOUTH) var thread = Thread { while (true) { var refresh = true EventQueue.invokeLater { refresh = panel.refresh() } Thread.sleep(25) } } thread.start() jButton.addActionListener { panel.clear() } } inner class HeartPanel : JPanel() { var i = 0.0 var list = mutableListOf
Android版本
public class ValentineView extends View { Paint paint; int w, h; Bitmap flower; Canvas hearCanvas; Bitmap bmpHeart; float leftAngle = (float)( 2*Math.PI); float rightAngle = 0; double inc = Math.PI / 45; int zoom = 1; float finalTxSize; public ValentineView(Context context, AttributeSet attrs) { super(context, attrs); float density = getResources().getDisplayMetrics().density; paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setColor(Color.parseColor("#FFDB9C")); paint.setStyle(Paint.Style.FILL_AND_STROKE); paint.setTextSize(0); paint.setStrokeWidth(2); paint.setDither(true); flower = BitmapFactory.decodeResource(getResources(), R.mipmap.heart); finalTxSize = density * 22; } public ValentineView(Context context) { this(context, null); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int wM = MeasureSpec.getMode(widthMeasureSpec); int hM = MeasureSpec.getMode(heightMeasureSpec); if (wM == MeasureSpec.EXACTLY) { w = MeasureSpec.getSize(widthMeasureSpec); } if (hM == MeasureSpec.EXACTLY) { h = MeasureSpec.getSize(heightMeasureSpec); } setMeasuredDimension(w, h); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); hearCanvas = new Canvas(); bmpHeart = Bitmap.createBitmap(w, h, Config.ARGB_8888); hearCanvas.setBitmap(bmpHeart); // 计算需要放大的倍数,铺满屏幕宽度 zoom = (int) Math.ceil(w / 2.0 / 16.0 - 4); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); drawHeart(canvas); canvas.drawBitmap(bmpHeart, 0, 0, paint); if (rightAngle > Math.PI || leftAngle < Math.PI){ Log.d("xxx", "draw hear ok"); paint.setTextAlign(Paint.Align.CENTER); canvas.drawText("情人节快乐", w/2, h/2, paint); paint.setTextAlign(Paint.Align.LEFT); if (paint.getTextSize() <= finalTxSize) { paint.setTextSize(paint.getTextSize()+1); postInvalidateDelayed(25); }else{ Log.d("xxx", "draw txt ok"); } } } private void drawHeart(Canvas canvas) { hearCanvas.save(); hearCanvas.translate(w / 2, h / 2);// float leftX = (float) getX(zoom, leftAngle); float leftY = (float) getY(zoom, leftAngle); float rightX = (float) getX(zoom, rightAngle); float rightY = (float) getY(zoom, rightAngle); //Log.d("xxx", String.format("x:%s, y:%s", x, y)); hearCanvas.save(); hearCanvas.translate(-flower.getWidth()/2, -flower.getHeight()/2); hearCanvas.drawBitmap(flower, leftX, leftY, paint); hearCanvas.drawBitmap(flower, rightX, rightY, paint); hearCanvas.restore(); hearCanvas.restore(); // right half if(rightAngle <= Math.PI){ rightAngle += inc; //postInvalidateDelayed(50); } // left half if(leftAngle >= Math.PI){ leftAngle -= inc; postInvalidateDelayed(50); } } double getX(int zoom, float theta) { return zoom * (16 * Math.pow(Math.sin(theta), 3)); } double getY(int zoom, float theta) { return -zoom * (13 * Math.cos(theta) - 5 * Math.cos(2 * theta) - 2 * Math.cos(3 * theta) - Math.cos(4 * theta)); }}
更多代码,请参考github:https://github.com/ximen502/SwingLearn,Swing中用kotlin还挺不错,kotlin确实简洁而强大,和java形成很好的互补。
Swing中的JPanel有2个作用一个是作为子面板容器,另一个是可以用于自定义组件(view),重写方法实现自定义绘制。
参考了用户qq_32250025的心形曲线公式博客,感谢。