300分!!! 求算法! (300分)

  • 主题发起人 scorpions
  • 开始时间
S

scorpions

Unregistered / Unconfirmed
GUEST, unregistred user!
我做了个像流程图的东西,窗体上有几个可以拖动的动态创建的button 它们之间有连线。
当拖动button时 连线也跟着重新画。有时连线会和其他button重合,
我的画线函数如下怎样判断连线是否与其他button重合?
怎样使和button重合的连线可以绕开所有重合的button???
procedure NewLine(p1,p2:TBitBtn);
var
dp1,dp2:TPoint;
begin
Canvas.Pen.Color :=clred;
dp1:=point(p1.Left+p1.Width div 2,p1.Top+p1.Height);
dp2:=point(p2.Left+p2.Width div 2,p2.top);
if (dp2.Y - dp1.Y) > 15 then
begin
Canvas.Polyline([dp1,point(dp1.X,dp2.Y-10),point(dp2.X,dp2.Y-10),dp2]);
end else
begin
Canvas.PolyLine([dp1,point(dp1.X,dp1.Y+10),point(dp1.X+50,dp1.Y+10),
point(dp1.X+50,dp2.Y-10),point(dp2.X,dp2.Y-10),dp2]);
end;
end;


还有当鼠标指针放在连线上hint连接信息。怎样判断指针移动的位置有无连线?

请各位大侠指点!
 
此种类控件在“天星”ERP中见过,我也很想知道.
 
可不可以判断一下canvas上的点的颜色来判断是不是此点某条线上
那个点是不是有button只要取得那个点处的窗口的句柄如果它的类名是Tbutton就说明它是
在 button上
以上是我的猜想,实际没做过


 
给你JAVA的一段代码,该APPLET实现了你需要的功能

/*
* Copyright (c) 2002 Sun Microsystems, Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* -Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* -Redistribution in binary form must reproduct the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
* ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT
* BE LIABLE FOR ANY DAMAGES OR LIABILITIES SUFFERED BY LICENSEE AS A RESULT
* OF OR RELATING TO USE, MODIFICATION OR DISTRIBUTION OF THE SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
* REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
* INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
* OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE SOFTWARE, EVEN
* IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that Software is not designed, licensed or intended for
* use in the design, construction, operation or maintenance of any nuclear
* facility.
*/

/*
* @(#)Graph.java 1.12 02/06/13
*/

import java.util.*;
import java.awt.*;
import java.applet.Applet;
import java.awt.event.*;


class Node {
double x;
double y;

double dx;
double dy;

boolean fixed;

String lbl;
}


class Edge {
int from;
int to;

double len;
}


class GraphPanel extends Panel
implements Runnable, MouseListener, MouseMotionListener {
Graph graph;
int nnodes;
Node nodes[] = new Node[100];

int nedges;
Edge edges[] = new Edge[200];

Thread relaxer;
boolean stress;
boolean random;

GraphPanel(Graph graph) {
this.graph = graph;
addMouseListener(this);
}

int findNode(String lbl) {
for (int i = 0 ; i < nnodes ; i++) {
if (nodes.lbl.equals(lbl)) {
return i;
}
}
return addNode(lbl);
}
int addNode(String lbl) {
Node n = new Node();
n.x = 10 + 380*Math.random();
n.y = 10 + 380*Math.random();
n.lbl = lbl;
nodes[nnodes] = n;
return nnodes++;
}
void addEdge(String from, String to, int len) {
Edge e = new Edge();
e.from = findNode(from);
e.to = findNode(to);
e.len = len;
edges[nedges++] = e;
}

public void run() {
Thread me = Thread.currentThread();
while (relaxer == me) {
relax();
if (random &amp;&amp; (Math.random() < 0.03)) {
Node n = nodes[(int)(Math.random() * nnodes)];
if (!n.fixed) {
n.x += 100*Math.random() - 50;
n.y += 100*Math.random() - 50;
}
graph.play(graph.getCodeBase(), "audio/drip.au");
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
break;
}
}
}

synchronized void relax() {
for (int i = 0 ; i < nedges ; i++) {
Edge e = edges;
double vx = nodes[e.to].x - nodes[e.from].x;
double vy = nodes[e.to].y - nodes[e.from].y;
double len = Math.sqrt(vx * vx + vy * vy);
len = (len == 0) ? .0001 : len;
double f = (edges.len - len) / (len * 3);
double dx = f * vx;
double dy = f * vy;

nodes[e.to].dx += dx;
nodes[e.to].dy += dy;
nodes[e.from].dx += -dx;
nodes[e.from].dy += -dy;
}

for (int i = 0 ; i < nnodes ; i++) {
Node n1 = nodes;
double dx = 0;
double dy = 0;

for (int j = 0 ; j < nnodes ; j++) {
if (i == j) {
continue;
}
Node n2 = nodes[j];
double vx = n1.x - n2.x;
double vy = n1.y - n2.y;
double len = vx * vx + vy * vy;
if (len == 0) {
dx += Math.random();
dy += Math.random();
} else if (len < 100*100) {
dx += vx / len;
dy += vy / len;
}
}
double dlen = dx * dx + dy * dy;
if (dlen > 0) {
dlen = Math.sqrt(dlen) / 2;
n1.dx += dx / dlen;
n1.dy += dy / dlen;
}
}

Dimension d = getSize();
for (int i = 0 ; i < nnodes ; i++) {
Node n = nodes;
if (!n.fixed) {
n.x += Math.max(-5, Math.min(5, n.dx));
n.y += Math.max(-5, Math.min(5, n.dy));
}
if (n.x < 0) {
n.x = 0;
} else if (n.x > d.width) {
n.x = d.width;
}
if (n.y < 0) {
n.y = 0;
} else if (n.y > d.height) {
n.y = d.height;
}
n.dx /= 2;
n.dy /= 2;
}
repaint();
}

Node pick;
boolean pickfixed;
Image offscreen;
Dimension offscreensize;
Graphics offgraphics;

final Color fixedColor = Color.red;
final Color selectColor = Color.pink;
final Color edgeColor = Color.black;
final Color nodeColor = new Color(250, 220, 100);
final Color stressColor = Color.darkGray;
final Color arcColor1 = Color.black;
final Color arcColor2 = Color.pink;
final Color arcColor3 = Color.red;

public void paintNode(Graphics g, Node n, FontMetrics fm) {
int x = (int)n.x;
int y = (int)n.y;
g.setColor((n == pick) ? selectColor : (n.fixed ? fixedColor : nodeColor));
int w = fm.stringWidth(n.lbl) + 10;
int h = fm.getHeight() + 4;
g.fillRect(x - w/2, y - h / 2, w, h);
g.setColor(Color.black);
g.drawRect(x - w/2, y - h / 2, w-1, h-1);
g.drawString(n.lbl, x - (w-10)/2, (y - (h-4)/2) + fm.getAscent());
}

public synchronized void update(Graphics g) {
Dimension d = getSize();
if ((offscreen == null) || (d.width != offscreensize.width) || (d.height != offscreensize.height)) {
offscreen = createImage(d.width, d.height);
offscreensize = d;
if (offgraphics != null) {
offgraphics.dispose();
}
offgraphics = offscreen.getGraphics();
offgraphics.setFont(getFont());
}

offgraphics.setColor(getBackground());
offgraphics.fillRect(0, 0, d.width, d.height);
for (int i = 0 ; i < nedges ; i++) {
Edge e = edges;
int x1 = (int)nodes[e.from].x;
int y1 = (int)nodes[e.from].y;
int x2 = (int)nodes[e.to].x;
int y2 = (int)nodes[e.to].y;
int len = (int)Math.abs(Math.sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)) - e.len);
offgraphics.setColor((len < 10) ? arcColor1 : (len < 20 ? arcColor2 : arcColor3)) ;
offgraphics.drawLine(x1, y1, x2, y2);
if (stress) {
String lbl = String.valueOf(len);
offgraphics.setColor(stressColor);
offgraphics.drawString(lbl, x1 + (x2-x1)/2, y1 + (y2-y1)/2);
offgraphics.setColor(edgeColor);
}
}

FontMetrics fm = offgraphics.getFontMetrics();
for (int i = 0 ; i < nnodes ; i++) {
paintNode(offgraphics, nodes, fm);
}
g.drawImage(offscreen, 0, 0, null);
}

//1.1 event handling
public void mouseClicked(MouseEvent e) {
}

public void mousePressed(MouseEvent e) {
addMouseMotionListener(this);
double bestdist = Double.MAX_VALUE;
int x = e.getX();
int y = e.getY();
for (int i = 0 ; i < nnodes ; i++) {
Node n = nodes;
double dist = (n.x - x) * (n.x - x) + (n.y - y) * (n.y - y);
if (dist < bestdist) {
pick = n;
bestdist = dist;
}
}
pickfixed = pick.fixed;
pick.fixed = true;
pick.x = x;
pick.y = y;
repaint();
e.consume();
}

public void mouseReleased(MouseEvent e) {
removeMouseMotionListener(this);
if (pick != null) {
pick.x = e.getX();
pick.y = e.getY();
pick.fixed = pickfixed;
pick = null;
}
repaint();
e.consume();
}

public void mouseEntered(MouseEvent e) {
}

public void mouseExited(MouseEvent e) {
}

public void mouseDragged(MouseEvent e) {
pick.x = e.getX();
pick.y = e.getY();
repaint();
e.consume();
}

public void mouseMoved(MouseEvent e) {
}

public void start() {
relaxer = new Thread(this);
relaxer.start();
}

public void stop() {
relaxer = null;
}

}


public class Graph extends Applet implements ActionListener, ItemListener {

GraphPanel panel;
Panel controlPanel;

Button scramble = new Button("Scramble");
Button shake = new Button("Shake");
Checkbox stress = new Checkbox("Stress");
Checkbox random = new Checkbox("Random");

public void init() {
setLayout(new BorderLayout());

panel = new GraphPanel(this);
add("Center", panel);
controlPanel = new Panel();
add("South", controlPanel);

controlPanel.add(scramble); scramble.addActionListener(this);
controlPanel.add(shake); shake.addActionListener(this);
controlPanel.add(stress); stress.addItemListener(this);
controlPanel.add(random); random.addItemListener(this);

String edges = getParameter("edges");
for (StringTokenizer t = new StringTokenizer(edges, ",") ; t.hasMoreTokens() ; ) {
String str = t.nextToken();
int i = str.indexOf('-');
if (i > 0) {
int len = 50;
int j = str.indexOf('/');
if (j > 0) {
len = Integer.valueOf(str.substring(j+1)).intValue();
str = str.substring(0, j);
}
panel.addEdge(str.substring(0,i), str.substring(i+1), len);
}
}
Dimension d = getSize();
String center = getParameter("center");
if (center != null){
Node n = panel.nodes[panel.findNode(center)];
n.x = d.width / 2;
n.y = d.height / 2;
n.fixed = true;
}
}

public void destroy() {
remove(panel);
remove(controlPanel);
}

public void start() {
panel.start();
}

public void stop() {
panel.stop();
}

public void actionPerformed(ActionEvent e) {
Object src = e.getSource();

if (src == scramble) {
play(getCodeBase(), "audio/computer.au");
Dimension d = getSize();
for (int i = 0 ; i < panel.nnodes ; i++) {
Node n = panel.nodes;
if (!n.fixed) {
n.x = 10 + (d.width-20)*Math.random();
n.y = 10 + (d.height-20)*Math.random();
}
}
return;
}

if (src == shake) {
play(getCodeBase(), "audio/gong.au");
Dimension d = getSize();
for (int i = 0 ; i < panel.nnodes ; i++) {
Node n = panel.nodes;
if (!n.fixed) {
n.x += 80*Math.random() - 40;
n.y += 80*Math.random() - 40;
}
}
}

}

public void itemStateChanged(ItemEvent e) {
Object src = e.getSource();
boolean on = e.getStateChange() == ItemEvent.SELECTED;
if (src == stress) panel.stress = on;
else if (src == random) panel.random = on;
}

public String getAppletInfo() {
return "Title: GraphLayout /nAuthor: <unknown>";
}

public String[][] getParameterInfo() {
String[][] info = {
{"edges", "delimited string", "A comma-delimited list of all the edges. It takes the form of 'C-N1,C-N2,C-N3,C-NX,N1-N2/M12,N2-N3/M23,N3-NX/M3X,...' where C is the name of center node (see 'center' parameter) and NX is a node attached to the center node. For the edges connecting nodes to eachother (and not to the center node) you may (optionally) specify a length MXY separated from the edge name by a forward slash."},
{"center", "string", "The name of the center node."}
};
return info;
}

}
 
谢了,我还是需要Delphi的代码阿。
 
看看这个对你有没有帮助

判断一点是否在矩形内:
function ptInPolygon(x,y : integer; Points: array of TPoint) : boolean;
var
rgn:HRGN;
bmp : Tbitmap;
begin
bmp := Tbitmap.create;
BeginPath(bmp.canvas.handle); //开始记录路径
bmp.Canvas.Polygon(points);
EndPath(bmp.canvas.handle); // 闭合路径
rgn:= PathToRegion(bmp.canvas.handle); // 简切视窗
result := PtInRegion(rgn,x,y); // 判断
bmp.free;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
if ptinpolygon(1000,1000,[Point(10, 10), Point(30, 10),
Point(130, 30), Point(240, 120)]) then showmessage('In!')
else showmessage('out!');
end;

***********************************
下面是真正的实现的算法:
huizhang (1998-12-31 8:01:35)
实在对不起, 那个算法确实有问题, 缺少一点判断。
现在给你另外一个算法,叫做“跳栅栏算法”。其原理如下:

假设有一个栅栏,你从现在所站地点沿着一个方向跑过去,遇到栅栏时就跳过去,记住你跳了几次。如果次数是单数,则你在栅栏内;如果是双数,则你在栅栏外。以下算法是向东跑过去的实现方法。该算法虽然用了一点除法,但是由于排除了不相关的栅栏,故速度也很快。此外它的优点是与多边形的排列方向无关。

function TForm1.PointInFence(p: TPoint; Fence: Array of TPoint): boolean;
var
p1: TPoint;
ar: integer;
i, j: integer;
begin
ar:=0;
for i:=0 to PtNos-1 do
begin
if i=PtNos-1 then j:=0 else j:=i+1;
//如果在所站点的水平方向上有栅栏存在
if ((((p.y>=Fence.y) and (p.y<fence[j].y)) or
((p.y>=fence[j].y) and (p.y<fence.y))) and
//且栅栏在我的右方
(p.x<(fence[j].x-fence.x)*(p.y-fence.y)/(fence[j].y-fence.y)+fence.x)) then
ar := Ar+1;
end;
result:= (Ar and $1) > 0;
end;
 
我的画线函数如下怎样判断连线是否与其他button重合?
怎样使和button重合的连线可以绕开所有重合的button???
procedure NewLine(p1,p2:TBitBtn);
var
dp1,dp2:TPoint;
begin
Canvas.Pen.Color :=clred;
dp1:=point(p1.Left+p1.Width div 2,p1.Top+p1.Height);
dp2:=point(p2.Left+p2.Width div 2,p2.top);
if (dp2.Y - dp1.Y) > 15 then
begin
Canvas.Polyline([dp1,point(dp1.X,dp2.Y-10),point(dp2.X,dp2.Y-10),dp2]);
end else
begin
Canvas.PolyLine([dp1,point(dp1.X,dp1.Y+10),point(dp1.X+50,dp1.Y+10),
point(dp1.X+50,dp2.Y-10),point(dp2.X,dp2.Y-10),dp2]);
end;
end;
 
1) 判断连线是否与按钮重合:把按钮看成长方形,知道四个角的坐标,就是知道了
四条边的方程,与连线的方程联立,看看有没有交点,有就是重合了.(注意是线
段方程,不是直线方程) 流程图里的按钮应该不会太多(<100),速度应该不成问题.

2) 鼠标与连线位置:
a)直接判断当前鼠标坐标是否符合某条连线的方程.同上,要注意是线段,所以得
留意定义域和值域.(循环判断所有的连线)
b)用点到直线距离公式算出鼠标到某连线的距离,如果距离小于某个范围,则显示
说明.这种做法用户只要把鼠标放到连线附近就可以激活HINTS,但速度相对慢许多。

3) 绕开重合的按钮:
如果你用来连接两个按钮的是直线而不是折线,当你发现线与按钮重合了,为该按钮作一个
外接圆(圆心为按钮中点,半径为按钮对角线的一半)。求出连线与圆的交点,求出两个
交点到圆心的角度。这样就可以用画弧方法画出一个绕过按钮的弧,用这个弧取代两个交
点之间的那一部分直线就可以了。

建议:
1)连线也应该作成对象,属性包括:
a)起点,终点
b)线段队列([起点,终点]),如果没有重合,则队列中只有一个元素,就是原来的连线。
如果发生重合,则队列中包括连接起点,弧,终点的所有线段。
c)弧队列,里面保存绕过按钮的弧对象。
2)弧对象属性包括:圆心,圆心角
3)连线对象和它包含的弧对象的属性应该在连线创建和终点发生变化时修改。重画时则直接使用。

这样重画时能以最快速度重画没有修改的连线
 
感谢kidneyball给我那么多建议! 只在研究。^_^

在判断连线与按钮重合并绕开重合的按钮 连线可能会重合多个按钮。
我的连线都是曲折行的(Canvas.Polyline)
能否给点进一步的提示。
 
如果你用的折线的话,那么任意两点间的最简单的连线就应该看成是以这两点为对角线的矩形的相邻
两边。这样的话,你会有两种选择(一个矩形有两对邻边)。如果发现折线与某按钮重合,
首选方法应该是尝试使用另外的一对邻边,但如果也出现重合,则需要在连线上添加转折点。

添加转折点最方便安全的做法是当发现线与一个按钮重合时,做一个仅仅能绕过这个按钮的
转折(思路和上面的外接圆差不多)。设有一个刚刚比按钮大一点的能包围按钮的矩形,求出
原来连线和这个矩形的交点,这时会出现两种情况:
1)
+-------+
|MMMMMMM|
@___*....MMM|
|MM .MMM|
#---*---+
|
&amp; (*交点, . 连线原来的走向, #增加的端点,@,&amp;原来的端点,M 按钮位置)

交点在两条邻边上(原来转折点在矩形内部):取这两条邻边的交点(#)取代原来在矩形
内部的转折点,这时新增的#点与原来的端点会构成两对新端点 @-#与#-&amp;,递归调用上面说的
画折线的方法(任取两邻边),就可以得出以下四种可能的连线:
I
MMMMMMMMMM
@___*MMMMMMMMMM
. |MMMMMMMMMM
....#---*
. |
....&amp;

I @....MMMMMMMMM
| .MMMMMMMMM
+---#---*
. |
....&amp;

III
MMMMMMMMMM
@___*MMMMMMMMMM
. |MMMMMMMMMM
....#....
| .
+---&amp;

IV
MMMMMMMMM
@....MMMMMMMMM
| .MMMMMMMMM
+---#....
| .
+---&amp;


若不与其他按钮重合的话,以II和III最美观,你可以采取适当算法以便优先选择构成II或III的组合。
但如果出现与其他按钮重合的情况,按照最初选另一组邻边的原则,会出现I,II,III中的
任一情况。VI的情况最不美观,但如果算法设计正确,可以完全避免出现VI的情况

2)连线通过矩形对边(矩形内部没有折点)
@
|
+---*----#
|MMMMMMMM|
+---*----#
|
&amp;
这时,可以如图选择两个邻角作为转折点,构成@-#,#-#,#-&amp;三段连线。在它们之间使用
以上的连线策略,就能得到以下四种图形之一:
I:
@.....
| .
*----#
MMMMMMMM |
*----#
| .
&amp;.....
II
@----+
. |
.....#
MMMMMMMMMM|
*----#
|
&amp;
III
@
|
*----#
MMMMMMMMMM|
.....#
. |
&amp;----+
IV
@----+
. |
.....#
MMMMMMMMMM|
.....#
. |
&amp;----+
(小数点为端点间构成矩形的假想线)
若不与其他按钮重合的话,以IV最美观,你可以采取适当算法以便优先选择构成IV的组合。
但如果出现与其他按钮重合的情况,按照最初选另一组邻边的原则,会出现I,II,III中的
任一情况。

这个方案适用于连线与多个按钮重合的情况,只要在判断时对所有按钮作循环判断就可以了。

这里假设新增的折线不会再和其他按钮重合,这个假设是合理的,因为一般流程图里两个矩形
之间不会太密。你可以在移动按钮时判断如果按钮重合了就适当拉开它们的位置。但如果真
的要考虑按钮重合,这时有两种选择:
1)构造绕过按钮的矩形时,使它足够大能包括所有重叠的按钮。但还要考虑作为端点的按钮
与目标按钮直接或间接重叠的情况。这是连线的某个或两个端点在矩形内部,情况就比较复杂了。
2)对于新增的折线,递归上面的过程,使它绕过新遇到的按钮。这种方法比较方便,但最后的
连线可能会转折太多。
 
如果使用以上方案,你的连线对象里应有以下属性:
1)起点,终点
2)转折点列表。列表中的转折点应保证两两间连线是水平或垂直的,可以直接画线。
不要在重画时来选择矩形的邻边
3)与这条连线重合的按钮的列表。(原因见下面)

在你的按钮对象中应包含一个与之重叠的连线的列表。在按钮发生移动或删除时,通知受
其影响的连线重新构造转折点。而连线对象中也应包含与之重叠的按钮对象列表,在连线
被删除或改变位置时而不再与某按钮重叠时,通知该按钮在其连线列表中删除对这条连线
的引用。
 
顶部