欢迎您访问365答案网,请分享给你的朋友!
生活常识 学习资料

情人节程序员的浪漫

时间:2023-06-19

  搜索了一下笛卡尔和瑞典公主的浪漫爱情故事,其中提到了一个心形函数,但是看起来像极坐标系的函数。以下借助大佬的数学公式和其他大佬的代码实现,移植java Swing和android平台进行实现。马上就要2.14号了,祝有情人终成眷属,举案齐眉,相敬如宾,白头偕老。
https://mathworld.wolfram.com/HeartCurve.html

1.心形曲线的绘制

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里面均相应转化成功。后面的坐标轴可以去掉,加上只是为了参考。

2.心形曲线动画 2.1效果1

如果只是个静态图也有些乏味,尝试让曲线绘制过程变的可见,加个延时。

2.2效果2

从顶部中间同时向下绘制

2.3效果3把画线改为绘制图片

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() override fun paintComponent(g: Graphics?) { super.paintComponent(g) var g2: Graphics2D = g as Graphics2D g2.translate(width / 2, height / 2) drawAxis(g) //修线条粗细 g2.stroke = BasicStroke(3.0f) g2.color = Color.RED val inc = Math.PI / 90 if (i <= 2 * Math.PI) { var x = getX(20, i.toFloat()) var y = getY(20, i.toFloat()) var p = OkPoint(x, y) list.add(p) i += inc } for (i in list.indices) { if (i < list.size - 1) { var p0 = list.get(i) var p1 = list.get(i + 1) g2.drawLine(p0.x.toInt(), p0.y.toInt(), p1.x.toInt(), p1.y.toInt()) } } g2.translate(-width / 2, -height / 2) } fun getX(zoom: Int, theta: Float): Double { return zoom * (16 * Math.pow(Math.sin(theta.toDouble()), 3.0)) } fun getY(zoom: Int, theta: Float): Double { return (-zoom * (13 * Math.cos(theta.toDouble()) - 5 * Math.cos((2 * theta).toDouble()) - (2 * Math.cos((3 * theta).toDouble())) - Math.cos((4 * theta).toDouble()))) } fun drawAxis(g: Graphics?) { var g2: Graphics2D = g as Graphics2D g2.stroke = BasicStroke(1.0f) g2.color = Color.BLACK g2.drawLine(-width / 2, 0, width / 2, 0) g2.drawLine(0, -height / 2, 0, height / 2) //unit=10,vertical line,x1,y1,x2,y2 // short line, long line val sl = 5 val ll = 10 //x axis for (i in 0..width / 2 step 10) { if (i % 50 == 0) { g2.drawLine(i, 0, i, -ll) g2.drawLine(-i, 0, -i, -ll) } else { g2.drawLine(i, 0, i, -sl) g2.drawLine(-i, 0, -i, -sl) } } //y axis for (i in 0..height / 2 step 10) { if (i % 50 == 0) { g2.drawLine(0, i, ll, i) g2.drawLine(0, -i, ll, -i) } else { g2.drawLine(0, i, sl, i) g2.drawLine(0, -i, sl, -i) } } } fun refresh():Boolean { return if (i <= 2 * Math.PI) { repaint() true } else { false } } fun clear() { i = 0.0 list.clear() } } inner class OkPoint(var x: Double, var y: Double)}fun main(args: Array) { var frame = HeartframeKt2() frame.apply { setSize(1500, 800) title = "Kotlin heart" setLocationRelativeTo(null) // Center the frame defaultCloseOperation = Jframe.EXIT_ON_CLOSE isVisible = true }}

  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的心形曲线公式博客,感谢。

Copyright © 2016-2020 www.365daan.com All Rights Reserved. 365答案网 版权所有 备案号:

部分内容来自互联网,版权归原作者所有,如有冒犯请联系我们,我们将在三个工作时内妥善处理。