IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> 情人节程序员的浪漫 -> 正文阅读

[游戏开发]情人节程序员的浪漫

??搜索了一下笛卡尔和瑞典公主的浪漫爱情故事,其中提到了一个心形函数,但是看起来像极坐标系的函数。以下借助大佬的数学公式和其他大佬的代码实现,移植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 - 5\cos(2t)-2\cos(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.JButton
import javax.swing.JFrame
import 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<OkPoint>()
        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<String>) {
    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(/*density*22*/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的心形曲线公式博客,感谢。

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2022-02-14 21:31:23  更:2022-02-14 21:32:10 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/27 16:29:17-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码