opengl绘制固定宽度多边形圆角边框算法(历时周半,撰博文以志纪念)

CGPoint.java

package org.bruce.roundcorner.core; /** * @author BruceYang */ public class CGPoint { public float x; public float y; public CGPoint() { } public CGPoint(float x, float y) { this.x = x; this.y = y; } @Override public String toString() { return "x=" + this.x + ", y=" + this.y; } }


Line.java

package org.bruce.roundcorner.core; /** * @author BruceYang * 这个是对通用一次直线方程 A*x + B*y + C = 0 的封装~ * 本来封装的是斜截式,不过发现当斜率k不存在的时候会比较麻烦,因此该用一般式 * 再个就是接着用一般式的演变方式 x + B/A*y + C/A = 0,但是考虑到可能存在 x == 0 的情况,因此又舍弃~ * * 一般式还是无济于事,改回斜截式,多提供两个成员变量: * 一个 boolean 表示 k 是否存在,一个额外的 float 表示 k 不存在的时候直线方程 x=***, *** 等于多少~ */ public class Line { // 特别声明为 public 类型,免得到时候访问的时候麻烦,到时候直接点就行了 private boolean kExists; // 大部分情况下k都应该是存在的,因此提供一个true 的默认值~ public float k = 77885.201314f; public float b = 13145.207788f; public float extraX = 52077.881314f; public Line() { } /** * @param k * @param b * 这是当 k 存在时的构造方法~ */ public Line(float k, float b) { this.kExists = true; this.k = k; this.b = b; } /** * @param extraX * 这是当 k 不存在时的构造方法~ */ public Line(float extraX) { this.kExists = false; this.extraX = extraX; } @Override public String toString() { return "Line.toString()方法被调用,y = k*x + b斜截式, k=" + this.k + ", b=" + this.b +", kExists=" + this.kExists + ", extraX=" + this.extraX; } public boolean iskExists() { return kExists; } public void setkExists(boolean kExists) { this.kExists = kExists; } }

GenericBase.java

保密,有意者请与某联系,^_^!!

PointsGeneratorGeneric.java

package org.bruce.roundcorner.core; /** * @author BruceYang * 假设传入的参数为一个CGPoint的数组,这个数组包含了1个凸五边形的顶点(按顺序,顺时针方向), * CGPoint[] points = new CGPoint[5]; * 首先,利用 points[4],points[0],points[1]这3个顶点做文章 * 将该3点连接成一个3角形,这里按逆时针方向取,即 points[0], points[4], points[1] * 思路(遵循1个原则,将核心方法分步骤放在多个子方法里面,这样逻辑会比较清晰): * 首先算出 线段points[0]_points[4]、线段points[0]_points[1] 分别所在的直线方程, * */ // 需要的一些附加功能: // 0.传入2个点,得到经过这两个点的直线方程(斜截式 y = kx + b); // 1.传入两个直线方程,一个圆角边框的宽度,得到同时满足与这两条直线距离最短的两个点(只有1个点才是符合要求的,依赖2步骤)~ // 2.传入直线方程(斜截式 y = kx + b,封装1个类,包含k,b两个参数,代表直线)以及两个点的参数,返回距离这条直线较近的1个点 // 3.至此已经成功获取该点圆角的圆心点,之后需要得到两个点,这两个点是圆心点距离这两条直线最短距离的点,要求一个新的附加功能方法,求得一条直线的垂直线的斜率 // 4.至此,生成圆角边框的4个最基本的点已经齐备了, public class PointsGeneratorGeneric { /** * @param p * @return * 该方法是本类的核心方法,传入的参数为一个凸多边形的顶点数组 * 输出的顶点数组的长度有讲究,详情对着下面的1行代码遐想 * int outPCount = inPCount*11 + 2 + inPCount; * * 末尾,(inPCount + 1) 数量的存储着内框的点 * (inPCount*7 + 1) 数量的存储着外框的点 * 内外框的点是为圆角边框从内到外生成“梯度”做准备的,由于这种操作是非常频繁的 * 因此,只在有需要的时候调用合适的方法 * 还有些只用生成纯色圆角边框的情况,内外框的点就不用再做额外的输出了 * 为此,我准备提供两个方法,让用户能够进行灵活的选择~ * * 另外,这些代码到时候都是要转换成obj-c代码的,因此我决定不用数组的length属性 * (因为obj-c里面的数组对自己的长度是不自知的,到时候调整代码的时候方便一点)~ */ // 需保证传入的顶点是按顺时针方向排布的~ public static CGPoint[] genPolygonPoints(CGPoint[] p, int inPCount) { // 每个凸多边形顶点要绘制出一个扇形至少需要11个点,将所有这些扇形连结起来还需要额外的两个顶点~ int outPCount = inPCount*11 + 2; CGPoint[] target = new CGPoint[outPCount]; // 这是一个标识,记录着target已经被填充到哪个下标的元素了~ int targetCurrentIndex = 0; CGPoint p0, p1, p2; for(int i = 0; i < inPCount; i ++) { // 用临时变量接收一下数组中的元素,下面使用的时候可以少些一组中括号下标 p0 = p[i]; p1 = (i-1 == -1) ? p[inPCount-1] : p[i-1]; p2 = (i+1 < inPCount) ? p[i+1] : p[0]; CGPoint[] pGroup = handleUnit(p0, p1, p2); // 算出一组构成圆角曲线的点数组就其加入到目标数组里面~ for(int j = 0; j < 11; j ++) { target[targetCurrentIndex + j] = pGroup[j]; } targetCurrentIndex += 11; } target[outPCount-2] = target[0]; target[outPCount-1] = target[1]; return target; } /** * @return * 每次传入凸多边形的3个顶点,算出核心点附近的圆角曲线(共11个顶点)~ */ public static CGPoint[] handleUnit(CGPoint p0, CGPoint p1, CGPoint p2) { // 得到可能是圆心点的4个点 Line l_p0_p1 = GenericBase.getLine(p0, p1); Line l_p0_p2 = GenericBase.getLine(p0, p2); CGPoint[] circleCenters = GenericBase.getCircleCenter(l_p0_p1, l_p0_p2); // 确定得到的这4个圆心点中哪个是真正的圆心点 Line l_p1_p2 = GenericBase.getLine(p1, p2); CGPoint circleCenter = GenericBase.getNearestPoint(circleCenters, l_p1_p2); // 求出圆心点在两条通过多边形2个相邻顶点的直线的投影点 CGPoint projectivePoint1 = GenericBase.getProjectivePoint(circleCenter, l_p0_p1); CGPoint projectivePoint2 = GenericBase.getProjectivePoint(circleCenter, l_p0_p2); // 通过3个点生成圆角外边框的曲线~ CGPoint[] pGroup = GenericBase.genGroupPoints(circleCenter, projectivePoint1, projectivePoint2); return pGroup; } }


用例:

JFrameGeneric3.java

package org.bruce.roundcorner.test; import java.awt.Graphics; import javax.swing.JPanel; import javax.swing.SwingUtilities; import javax.swing.UIManager; import org.bruce.roundcorner.core.CGPoint; import org.bruce.roundcorner.core.PointsGeneratorGeneric; import org.bruce.roundcorner.test.component.BaseJFrame; /** * @author BruceYang */ public class JFrameGeneric3 extends BaseJFrame { private static final long serialVersionUID = 526700305514755530L; // 设置属性~ private static final int OVAL_RADIUS = 3; // 该值为1的时候什么都看不见(3比较漂亮,清晰而不显粗)~ private static final boolean DRAW_OVAL = true; private static final boolean DRAW_LINE = true; private static final boolean DRAW_STRING = false; // 对于不同定点数的图形,这个数值都要进行重新设置,规律为:顶点数*11 + 2 ~ private static final int vertexCount = 35; private static P[] ps = new P[vertexCount]; private InnerPanel ip; // 构造方法 public JFrameGeneric3() { super(); title = "生成点检视工具"; bAtCenter = true; bAlwaysDisplay = false; bFastQuitConfirm = true; bResizable = true; WIDTH = 500; HEIGHT = 500; super.initialize(); } public void loadPoints(P[] ps, CGPoint[] points) { for(int i = 0; i < points.length; i ++) { ps[i] = new P(); int tmpX = (int)(points[i].x); int tmpY = (int)(points[i].y); ps[i].setX(tmpX); ps[i].setY(tmpY); System.out.println(tmpX + " @@@" + i + "@@@ " + tmpY); System.out.println(); } } /** * @param input * @return * 该方法用于将所有点的y值进行取反变换 */ protected CGPoint[] yAxisConvert(CGPoint[] input) { for(int i = 0; i < input.length; i ++) { input[i].y = -input[i].y; } return input; } @Override protected void personalize() { CGPoint[] input = new CGPoint[3]; input[0] = new CGPoint(0, 0); input[1] = new CGPoint(250, (float)(500*Math.sqrt(3)/2)); input[2] = new CGPoint(500, 0); CGPoint[] target = PointsGeneratorGeneric.genPolygonPoints(input, 3); loadPoints(ps, target); ip = new InnerPanel(); this.add(ip); } class InnerPanel extends JPanel { private static final long serialVersionUID = 4614854384387952672L; @Override public void paint(Graphics g) { for(int i = 0 ; i < vertexCount; i ++) { if(DRAW_STRING) { g.drawString("p" + i, ps[i].x + 10, ps[i].y - 5); } if(DRAW_OVAL) { g.fillOval(ps[i].x, ps[i].y, OVAL_RADIUS, OVAL_RADIUS); } if(DRAW_LINE) { if(i != vertexCount-1) { g.drawLine(ps[i].x, ps[i].y, ps[i + 1].x, ps[i + 1].y); } } } } } class P { private int x; private int y; P() { } P(int x, int y){ this.x = x; this.y = y; } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } } public static void main(String[] args) { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (Exception e) { e.printStackTrace(); } SwingUtilities.invokeLater(new Runnable() { public void run() { new JFrameGeneric3(); } }); } }


效果图片:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要使用OpenGL绘制多边形,需要使用顶点数组来定义多边形的形状,并使用glDrawArrays或glDrawElements函数来渲染多边形。 下面是一个使用glDrawArrays函数绘制多边形的示例代码: ```c++ GLfloat vertices[] = { -0.5f, -0.5f, 0.0f, // 左下角顶点 0.5f, -0.5f, 0.0f, // 右下角顶点 0.0f, 0.5f, 0.0f // 顶部顶点 }; glGenVertexArrays(1, &vao); // 创建顶点数组对象 glGenBuffers(1, &vbo); // 创建顶点缓冲区对象 glBindVertexArray(vao); // 绑定顶点数组对象 glBindBuffer(GL_ARRAY_BUFFER, vbo); // 绑定顶点缓冲区对象 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // 将顶点数据复制到缓冲区 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0); // 设置顶点属性指针 glEnableVertexAttribArray(0); // 启用顶点属性 glBindVertexArray(0); // 解绑顶点数组对象 ... glUseProgram(shaderProgram); // 使用着色器程序 glBindVertexArray(vao); // 绑定顶点数组对象 glDrawArrays(GL_TRIANGLES, 0, 3); // 绘制三角形 glBindVertexArray(0); // 解绑顶点数组对象 ``` 在上面的代码中,我们使用glGenVertexArrays和glGenBuffers函数创建了顶点数组对象和顶点缓冲区对象,并使用glBindVertexArray和glBindBuffer函数将它们绑定到OpenGL上下文中。然后,我们使用glBufferData函数将顶点数据复制到顶点缓冲区中,并使用glVertexAttribPointer和glEnableVertexAttribArray函数来设置顶点属性指针和启用顶点属性。最后,我们使用glDrawArrays函数绘制多边形。 注意,上面的代码使用了一个着色器程序来将顶点数据转换为屏幕上的像素。如果你还没有了解过OpenGL着色器编程,可以先学习一下。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值