您的位置:首页 > 运维架构

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

2011-07-26 11:56 537 查看
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();
}
});
}
}


效果图片:









内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  算法 float input string class