百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

Android绘制函数图象及正弦函数的介绍

toyiye 2024-07-06 23:31 12 浏览 0 评论

零、前言

这篇是为了下一篇做点铺垫,也是来复习一些数学基础,本篇属于休闲娱乐,不要太较真,小科普一下,不喜勿喷。本文知识点前 4 点你可以随便看看,但 第5点非常重要,本文源码见 捷文规范

本文知识点:

  • 数学函数的概念
  • 直角坐标系的下函数图形
  • 极坐标下的函数图象
  • 参数方程下的函数图形
  • 正弦函数的详细分析(为下一篇文章做铺垫)

一、数学函数的概念:

1.高中数学必修1:

设A,B为非空的数集,如果按照某种确定的对应关系f, 
使对于集合A中的任意的任意一个数x,在集合B中都有唯一确定的数f(x)和它对应,
那么就称"f:A→B"为从集合A到集合B的一个函数,记作:
y=f(x),x∈A
其中,x叫做自变量,x的取值范围叫做函数的[定义域]
与x的值对应的y值叫做函数值,函数值的集合{f(x)|x∈A}叫做函数的[值域]

2.大学高等数学

设数集D? R,则称映射f:D→R为定义在D上的函数,通常简记为
y=f(x),x∈ D
其中x称自变量,y称因变量,D称定义域,记作Df,即Df=D.
值域:Rf=f(D)={y|y=f(x),x∈ D}

3.映射:

设X,Y是两个非集合,如果存在一个法则f,使的对X中的每个元素x,
按法则f,在Y中有唯一确定的元素y与之对应,则称f为X到Y的映射,记作
f:X→Y
其中y称为元素x(在映射f下)的像,并记作f(x),即y=f(x)
而元素x称为元素y(在映射f下)的原像

二、直角坐标系的下函数图形

这里只是模拟函数,然后绘制出可视的图象

数学中的实数是连续的,这里在屏幕中将像素作为基本的单元

绘图核心:点集成线,单点半径1px

自变量:x

定义域:Df用集合Set表示

函数关系:函数f(x)

点集用Map表示,x→y


0.网格与坐标系的绘制

网格和坐标系我已经封装,初始View如下:

public class MathView extends View {
 private Point mCoo = new Point(500, 700);//坐标系
 private Picture mCooPicture;//坐标系canvas元件
 private Picture mGridPicture;//网格canvas元件
 private Paint mHelpPint;//辅助画笔
 private Paint mPaint;//主画笔
 private Path mPath;//主路径
 public MathView(Context context) {
 this(context, null);
 }
 public MathView(Context context, @Nullable AttributeSet attrs) {
 super(context, attrs);
 init();//初始化
 }
 private void init() {
 //初始化主画笔
 mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
 mPaint.setColor(Color.BLUE);
 mPaint.setStrokeWidth(2);
 mPaint.setStyle(Paint.Style.STROKE);
 mPaint.setStrokeCap(Paint.Cap.ROUND);
 //初始化主路径
 mPath = new Path();
 //初始化辅助
 mHelpPint = HelpDraw.getHelpPint(Color.RED);
 mCooPicture = HelpDraw.getCoo(getContext(), mCoo);
 mGridPicture = HelpDraw.getGrid(getContext());
 }
 @Override
 protected void onDraw(Canvas canvas) {
 super.onDraw(canvas);
 HelpDraw.draw(canvas, mGridPicture, mCooPicture);
 canvas.save();
 canvas.translate(mCoo.x, mCoo.y);
 canvas.scale(1, -1);//y轴向上
 canvas.restore();
 }

具体细节这里不说了,详见:Android关于Canvas你所知道的和不知道的一切,或源码


1.一次函数:y=x,定义域[-200,300]


1.1:几个成员变量

private TreeSet<Float> Df = new TreeSet<>();//定义域
private Map<Float, Float> funMap = new HashMap<>();//映射表
private Paint mTextPaint;//文字画笔

1.2:初始化定义域

/**
 * 初始化定义域
 */
private void initDf() {
 for (float i = -200; i <= 300; i++) {
 Df.add(i);//初始化定义域
 }
}

1.3:对应法则fx

/**
 * 对应法则
 * @param x 原像(自变量)
 * @return 像(因变量)
 */
private float f(Float x) {
 float y = x;
 return y;
}

1.4:遍历定义域,将原像x和像f(x)加入映射表

/**
 * 遍历定义域,将原像x和像f(x)加入映射表
 */
private void map() {
 Df.forEach(x -> {
 funMap.put(x, f(x));
 });
 //添加所有点
}

1.5:绘制映射表

/**
 * 绘制映射表
 * @param canvas 画笔
 * @param map 点集映射表
 */
private void drawMap(Canvas canvas, Map<Float, Float> map) {
 map.forEach((k, v) -> {
 canvas.drawPoint(k, v, mPaint);
 });
}

2.绝对值函数:y=|x|,定义域[-200,300]

只需改一点

/**
 * 对应法则
 * @param x 原像(自变量)
 * @return 像(因变量)
 */
private float f(Float x) {
 float y=Math.abs(x);
 return y;
}

3.二次函数,定义域[-200,300]

/**
 * 对应法则
 * @param x 原像(自变量)
 * @return 像(因变量)
 */
private float f(Float x) {
 float y=(x - 100) * (x - 100) / 200 + 100;
 return y;
}

4.对数函数:log10(x)为例,定义域[1,1000]

/**
 * 初始化定义域
 */
private void initDf() {
 for (float i = 1; i <= 1000; i++) {
 Df.add(i);//初始化定义域
 }
}
/**
 * 对应法则
 *
 * @param x 原像(自变量)
 * @return 像(因变量)
 */
private float f(Float x) {
 float y = (float) (100.f * Math.log10(x));
 return y;
}

5.指数函数:定义域[-400,500]

/**
 * 初始化定义域
 */
private void initDf() {
 for (float i = -400; i <= 500; i++) {
 Df.add(i);//初始化定义域
 }
}
/**
 * 对应法则
 *
 * @param x 原像(自变量)
 * @return 像(因变量)
 */
private float f(Float x) {
 float y= 100*(float) Math.pow(Math.E,x/300f);
 return y;
}

6.正弦函数:定义域[-360°,450°]

/**
 * 初始化定义域
 */
private void initDf() {
 for (float i =-360; i <= 450; i++) {
 Df.add(i);//初始化定义域
 }
}
/**
 * 对应法则
 *
 * @param x 原像(自变量)
 * @return 像(因变量)
 */
private float f(Float x) {
 float y= (float) (100*Math.sin(Math.PI/180*x));
 return y;
}

经历过上面几个函数的绘制,不难发现,只有更改对应法则,即函数关系式就可以了


三、极坐标下的函数图象

1).寻找角度thta和长度p的函数关系

2).使用极坐标与直角坐标系的转换关系来绘制点集


1.笛卡尔心型线: ρ=100*(1-cosθ)

/**
 * 初始化定义域
 */
private void initDf() {
 for (float i = 1; i <= 360; i++) {
 Df.add(i);//初始化定义域
 }
}
/**
 * 绘制映射表
 *
 * @param canvas 画笔
 * @param map 点集映射表
 */
private void drawMap(Canvas canvas, Map<Float, Float> map) {
 map.forEach((thta, p) -> {
 Log.e(TAG, "drawMap: "+p+thta);
 canvas.drawPoint((float) (p * Math.cos(thta)), (float) (p * Math.sin(thta)), mPaint);
 });
}
/**
 * 对应法则
 *
 * @param thta 原像(自变量)
 * @return 像(因变量)
 */
private float f(Float thta) {
 float p = (float) (100 * (1 - Math.cos(thta)));
 return p;
}
/**
 * 遍历定义域,将原像x和像f(x)加入映射表
 */
private void map() {
 Df.forEach(x -> {
 float thta = (float) (Math.PI / 180 * x);
 funMap.put(thta, f(thta));
 });
 //添加所有点
}

2.四叶草: ρ=100*(1-4*sinθ)

/**
 * 对应法则
 *
 * @param thta 原像(自变量)
 * @return 像(因变量)
 */
private float f(Float thta) {
 float p = (float) (100 * (1 - Math.sin(4 * thta)));
 return p;
}

3.画着玩: ρ=(e^(cosθ)-2cos(4θ)+[sin(θ/12)]^5)*100

/**
 * 对应法则
 *
 * @param thta 原像(自变量)
 * @return 像(因变量)
 */
private float f(Float thta) {
 float p = (float) (100f*(Math.pow(Math.E,Math.cos(thta)) - 2 * Math.cos(4 * thta) + Math.pow(Math.sin(thta / 12), 5)));;
 return p;
}

4.涡旋线: ρ=a*θ

/**
 * 对应法则
 *
 * @param thta 原像(自变量)
 * @return 像(因变量)
 */
private float f(Float thta) {
 float p = 30*thta;
 return p;
}

5.极坐标下的圆

/**
 * 对应法则
 *
 * @param thta 原像(自变量)
 * @return 像(因变量)
 */
private float f(Float thta) {
 float p = 200;
 return p;
}

四、参数方程下的函数图象

1.双曲线: x=a/cosα, y=btanα

 /**
 * 初始化定义域
 */
 private void initDf() {
 for (float i = 0; i <= 360 ; i++) {
 Df.add(i);//初始化定义域
 }
 }
 /**
 * 绘制映射表
 *
 * @param canvas 画笔
 * @param map 点集映射表
 */
 private void drawMap(Canvas canvas, Map<Float, Float> map) {
 map.forEach((k, v) -> {
 canvas.drawPoint(k, v, mPaint);
 });
 }
 /**
 * 对应法则
 *
 * @param thta 原像(自变量)
 * @return y像(因变量)
 */
 private float y(Float thta) {
 float y = (float) (100 * Math.tan(thta));
 return y;
 }
 /**
 * 对应法则
 *
 * @param thta 原像(自变量)
 * @return x像(因变量)
 */
 private float x(Float thta) {
 float x = (float) (200 / Math.cos(thta));
 return x;
 }
 /**
 * 遍历定义域,将原像x和像f(x)加入映射表
 */
 private void map() {
 Df.forEach(x -> {
 float thta = (float) (Math.PI / 180 * x);
 funMap.put(x(thta), y(thta));
 });
 //添加所有点
 }

2.椭圆: x=a*cosα, y=bsinα

/**
 * 对应法则
 *
 * @param thta 原像(自变量)
 * @return y像(因变量)
 */
private float y(Float thta) {
 float y = (float) (300 * Math.sin(thta));
 return y;
}
/**
 * 对应法则
 *
 * @param thta 原像(自变量)
 * @return x像(因变量)
 */
private float x(Float thta) {
 float x = (float) (400 * Math.cos(thta));
 return x;
}

3.参数方程:双钮线 x=a√(cos2θ)cosθ, y=a√(cos2θ)sinθ

/**
 * 对应法则:y=a√(cos2θ)sinθ
 *
 * @param thta 原像(自变量)
 * @return y像(因变量)
 */
private float y(Float thta) {
 float y = (float) (200 * Math.sqrt(Math.cos(2*thta))*Math.sin(thta));
 return y;
}
/**
 * 对应法则:x=a√(cos2θ )cosθ
 *
 * @param thta 原像(自变量)
 * @return x像(因变量)
 */
private float x(Float thta) {
 float x = (float) (200 * Math.sqrt(Math.cos(2*thta))*Math.cos(thta));
 return x;
}

五、分析与优化

1.分析

你可能已经吐槽了:什么鬼,怎么后面都是断断续续的点拼成的

等等...先别急,我们来看看这幅图能说明什么?

先看一下定义域: [-360,450],共810个点,每个点半径1px,每个点横向距离1px

点密集则说明相邻两点间的dy很小,相反,稀疏则说明相邻两点间的dy很大

也就是密集说明函数变化的幅度小,稀疏说明函数变化的幅度大

当相邻两点距离大于圆的直径(2px)时,视觉上会看出两个点,即不连续。


2.分析总结

为了方便描述,这里定义了几个概念

如果把一条完美的函数曲线看作P,
那所有现实中(纸、屏幕)的函数图象P'都是对P的取点模拟,
从P上取点的行为称为[取样],
采样的个数称为[取样总数],
取样的相邻两点xn,xn+1间的距离称为[取样距离dxn]
当每个dxn值都相等的时,称为[等距采样]
两个样本点pn,pn+1之间的距离称为[样本距离dpn]

3.看一下连续的点有哪些

在加入点集时过滤掉相邻两点间距离大于直径的点

/**
 * 两点间的距离
 * @return
 */
private float dis(float x0, float y0, float x1, float y1) {
 return (float) Math.sqrt((x0 - x1) * (x0 - x1) + (y0 - y1) * (y0 - y1));
}
/**
 * 遍历定义域,将原像x和像f(x)加入映射表
 */
private void map() {
 Df.forEach(x -> {
 float dis = dis(x, f(x), x + 1, f(x + 1));//每相邻两点间距离
 if (dis < mLineWidth && dis > mLineWidth / 2) {
 funMap.put(x, f(x));
 }
 });

4.不行连续的点处理思路:

思路也就是在间距处再取样

/**
 * 遍历定义域,将原像x和像f(x)加入映射表
 */
private void map() {
 Df.forEach(x -> {
 float dis = dis(x, f(x), x + 1, f(x + 1));//每相邻两点间距离
 if (dis < mLineWidth && dis > mLineWidth / 2) {
 funMap.put(x, f(x));
 } else if (dis > mLineWidth) {
 float num = dis / mLineWidth;//在切割数
 for (float di = 0; di <= num; di += (1.f / num)) {
 x += di;
 funMap.put(x, f(x));
 }
 }
 });
 //添加所有点
}

六、正弦函数的详细分析

1.正弦函数简介

其中A,ω,φ,k是常数,且ω≠0
振幅:A
角频率:ω
周期:T=2π/ω 
频率:f=1/T=ω/2π
相位:ωx+φ
初相:φ
平衡线:y=k
波峰:最大值|A|
波谷:最小值-|A|

2.振幅A: 离开平衡位置的最大距离

下面横轴的每格代表90°,化为弧度制表示即:π/2,每四格是360°,即2π

2.1: A=300


2.2: A=100


2.3:振幅的作用

决定正弦曲线的波峰与波谷,形象来说就是"高矮" 
振幅越大,波峰越高,波谷越低,每个周期的图象显得"高"

3.角频率ω: 单位时间内变化的相角弧度值

3.1: ω=2


3.2: ω=5


3.3:角频率的作用

决定正弦曲线的周期,形象来说就是"胖瘦" 
角频率越大,周期越小,每个周期的图象显得"瘦"
ω=2 周期:T = 2π/ω = π 从图中看,每两格一周期,即π 
频率:f = 1/T = 1/π

4.初相φ: x=0时的相位

4.1: φ=π/6


4.2: φ=π/2


4.3:振幅的作用

相位决定了标准正弦函数的左右偏移:正左偏,负右偏,偏移量:φ/ω

5.平衡值k: 决定平衡线的位置

5.1: k=100


5.2: k=200

5.3:平衡值的作用

平衡值决定标准正弦函数的上下偏移:正上偏,负下偏,偏移量:k

现在对于几个正弦函数的参数值已经有了一点了解,本篇结束


附录:一些常用符号:

←↑→↓∪∩??∈∝??∞θρφπαβγημζΩ

后记:捷文规范

1.本文成长记录及勘误表

项目源码日期备注V0.1-github2018-1-2Android绘制函数图象及正弦函数的介绍

2.更多关于我

笔名QQ微信爱好张风捷特烈1981462002zdl1994328语言我的github我的简书我的掘金个人网站

3.声明

1----本文由张风捷特烈原创,转载请注明

2----欢迎广大编程爱好者共同交流

3----个人能力有限,如有不正之处欢迎大家批评指证,必定虚心改正

4----看到这里,我在此感谢你的喜欢与支持



相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码