環球新動態:蒙特利爾的麥吉爾大學:計算幾何課程資料
蒙特利爾的麥吉爾大學的計算幾何課程資料:
(資料圖片)
1. Introduction
When talking about distances, we usually mean the shortest : for instance, if a point X is said to be at distance D of a polygon P, we generally assume that D is the distance from X to the nearest point of P. The same logic applies for polygons : if two polygons A and B are at some distance from each other, we commonly understand that distance as the shortest one between any point of A and any point of B. Formally, this is called a miniminfunction, because the distance D between A and B is given by :
(eq. 1)
This equation reads like a computer program : ? for every point a of A, find its smallest distance to any point b of B ; finally, keep the smallest distance found among all points a?.
That definition of distance between polygons can become quite unsatisfactory for some applications ; let"s see for example fig. 1. We could say the triangles are close to each other considering their shortest distance, shown by their red vertices. However, we would naturally expect that a small distance between these polygons means that no point of one polygon is far from the other polygon. In this sense, the two polygons shown in fig. 1 are not so close, as their furthest points, shown in blue, could actually be very far away from the other polygon. Clearly, the shortest distance is totally independent of each polygonal shape.
Figure 1 :The shortest distance doesn"t consider the whole shape.
Another example is given by fig. 2, where we have the same two triangles at the same shortest distance than in fig. 1, but in different position. It"s quite obvious that the shortest distance concept carries very low informative content, as the distance value did not change from the previous case, while somethingdid change with the objects.
Figure 2 :The shortest distance doesn"t account for the position of the objects.
As we"ll see in the next section, in spite of its apparent complexity, the Hausdorff distance does capture these subtleties, ignored by the shortest distance.
2. What is Hausdorff distance ?
Named after Felix Hausdorff (1868-1942), Hausdorff distance is the ? maximum distance of a set to the nearest point in the other set ? [Rote91]. More formally, Hausdorff distance from set A to set B is a maximinfunction, defined as
(eq. 2)
where a and b are points of sets A and B respectively, and d(a, b) is any metric between these points ; for simplicity, we"ll take d(a, b) as the Euclidian distance between a and b. If for instance A and B are two sets of points, a brute force algorithm would be :
Brute force algorithm :
1. h = 0 2. for every point ai of A, 2.1 shortest = Inf ; 2.2 for every point bj of B dij = d (ai , bj ) if dij < shortest then shortest = dij 2.3 if shortest > h then h = shortestFigure 3 :Hausdorff distance on point sets.
This is illustrated in fig. 3 : just click on the arrow to see the basic steps of this computation. This algorithm obviously runs in O(n m) time, with n and m the number of points in each set. It should be noted that Hausdorff distance is oriented (we couldsay asymmetricas well), which means that most of times h(A, B) is not equal to h(B, A). This general condition also holds for the example of fig. 3, as h(A, B) = d(a1, b1), while h(B, A) = d(b2, a1). This asymmetry is a property of maximin functions, while minimin functions are symmetric. A more general definition of Hausdorff distance would be :
H (A, B) = max { h (A, B), h (B, A) }(eq. 3)
which defines the Hausdorff distance betweenA and B, while eq. 2 applied to Hausdorff distance fromA toB (also called directedHausdorff distance). The two distances h(A, B) and h(B, A) are sometimes termed as forwardand backwardHausdorff distances of A to B. Although the terminology is not stable yet among authors, eq. 3 is usually meant when talking about Hausdorff distance. Unless otherwise mentionned, from now on we will also refer to eq. 3 when saying "Hausdorff distance".
If sets A and B are made of lines or polygons instead of single points, then H(A, B) applies to all defining points of these lines or polygons, and not only to their vertices. The brute force algorithm could no longer be used for computing Hausdorff distance between such sets, as they involve an infinite number of points.
So, what about the polygons of fig. 1 ?Remember, some of their points were close, but not all of them. Hausdorff distance gives an interesting measure of their mutual proximity, by indicating the maximal distance between anypoint of one polygon to the other polygon. Better than the shortest distance, which applied only to onepoint of each polygon, irrespective of all other points of the polygons.
Figure 4 :Hausdorff distance shown around extremum of each triangles of fig. 1. Each circle has a radius of H( P1 , P2).
The other concern was the insensitivity of the shortest distance to the position of the polygons. We saw that this distance doesn"t consider at all the disposition of the polygons. Here again, Hausdorff distance has the advantage of being sensitive to position, as shown in fig.5.
Figure 5 :Hausdorff distance for the triangles of fig. 4 at the same shortest distance, but in different position.
3. Computing Hausdorff distance between convex polygons
3.1 Assumptions
Throughout the rest of our discussion, we assume the following facts about polygons A and B :
Polygons A and B are simple convex polygons ; Polygons A and B are disjoint from each other, that is : - they don"t intersect together ; - no polygon contains the other.
3.2 Lemmas
The algorithm explained in the next section is based on three geometric observations, presented here. In order to simplify the text, we assume two points aand bthat belong respectively to polygons A and B, such that :
d (a, b) = h (A, B)
In simple words, ais the furthest point of polygon A relative to polygon B, while bis the closest point of polygon B relative to polygon A.
Lemma 1a :The perpendicular to abat ais a supporting line of A, and A is on the same side as B relative to that line.
Proof of lemma 1a
Lemma 1b :The perpendicular to abat bis a supporting line of B, and aand B are on different sides relative to that line.
Proof of lemma 1b
Lemma 2 :There is a vertex xof A such that the distance from xto B is equal to h (A, B).
Proof of lemma 2
Lemma 3 :Let bi be the closest point of B from a vertex a i of A. If μ is the moving direction (clockwise or counterclockwise) from bi to bi+1 then, for a complete cycle through all vertices of A, μ changes no more than twice.
Proof of lemma 3
3.3 Algorithm
The algorithm presented here was proposed by [Atallah83]. Its basic strategy is to compute successively h(A,B) and h(B, A) ; because of lemma 2, there is no need to query every point of the starting polygon, but only its vertices.
An important fact used by this algorithm is that a closest point can only be a vertex of the target polygon, or the foot zof a line perpendicular to one of its edges. This fact suggests a function to check for the existence of a possible closest point. Given a source point aand a target edge defined by a point b1 and a vertex b2 :
Function z = CheckForClosePoint (a, b1 , b2 ) :Compute the position zwhere the line that passes through b1 and b2 crosses its perpendicular through a; if zis between b1 b2 then return z; else compute at b2 a line P perpendicular to the line ab2 ; if P is a supporting line of B then return b2 ; else return NULL.
That function obviously uses lemma 1b to decide whether or not the closest point of B might be located on the target edge, that should be close to a. It also supposes that the source point aand b2 are not located on different sides of the perpendicular to [b1b2 ] at b1, accordingly to lemma 3. Now we are ready for the main algorithm ; the vertices of both polygons are presumed to be enumerated counterclockwise :
Algorithm for computing h(A, B) :
1. From a1, find the closest point b1 and compute d1 = d ( a1, b1 ) 2. h(A, B) = d1 3. for each vertex ai of A, 3.1 if ai+1 is to the left of aibi find bi+1 , scanning B counterclockwise with CheckForClosePoint from bi if ai+1 is to the right of aibi find bi+1 , scanning B clockwise with CheckForClosePoint from bi if ai+1 is anywhere on aibi bi+1 = bi 3.2 Compute di+1 = d (ai+1 , bi+1 ) 3.3 h (A, B) = max { h (A, B), di+1 }
3.4 Complexity
If polygons A and B respectively have n and m vertices, then :
Step 1 can clearly be done in O(m) time ;Step 2 takes constant time O(1) ;Step 3 will be executed (n-1) times, that is O(n) ;Step 3.1 will not be executed in totalmore than O(2m). This is a consequence of lemma 3, which guarantees that polygon B can not be scanned more than twice ;Steps 3.2 and 3.3 are done in constant time O(1) ;
So the algorithm for computing h(A, B) takes :
O(m) + O(n) + O(2m) = O(n+m)
To find H(A, B), the algorithm needs to executed twice ; the total complexity for computing Hausdorff distance then stays linear to O(n+m).
3.5 Interactive applet
This applet illustrates the algorithm for computing h(A,B). You only need to draw two polygons, and then press the "step" or "run" button. Left click to define a new vertex, and close the polygon by clicking near the first vertex. Polygon A is the first one you draw, in green, while polygon B appears next, in red. The algorithm was slightly modified to make it more appealing visually. Even if this algorithm is intended for two polygons totally separated from each other, it also works when B is inside A. However, it won"t work if A is inside of B, or when A and B are partially intersecting. You"re allowed anyway to try these cases to see what happens ! When defining your polygons, you will see a yellow area that indicates where you can add the next vertex, so the polygon keeps convex. The applet won"t let you define a non-convex polygon.
Please notice that the first time you draw the second half of a polygon, you will have to wait a few seconds until the Jama package loads.
import java.awt.*;
import java.applet.Applet;
import java.util.Vector;
import java.lang.Math;
import Jama.*;
public class Hausdorff extends Applet
{
PolyArea area;
Panel control;
Button runStop;
boolean running;
TextField comment;
public void init()
{
setLayout (new BorderLayout());
comment = new TextField ("", 60);
comment.setEditable (false);
area = new PolyArea (comment);
add ("Center", area);
control = new Panel();
control.add (new Button ("step"));
runStop = new Button ("run");
control.add (runStop);
control.add (new Button ("reset"));
control.add (comment);
add("South", control);
running = false;
}
public boolean action (Event evt, Object arg)
{
if ("step".equals(arg))
area.stepAlgo();
if ("run".equals(arg))
{
runStop.setLabel ("stop");
startAnim();
running = true;
}
if ("stop".equals(arg))
{
runStop.setLabel ("run");
stopAnim();
running = false;
}
if ("reset".equals(arg))
{
remove (area);
stopAnim();
area = new PolyArea (comment);
add ("Center", area);
runStop.setLabel ("run");
validate();
stopAnim();
running = false;
}
return true;
}
public void start()
{
if (running)
startAnim();
}
public void stop()
{
stopAnim();
}
public void startAnim()
{
if (area.animator == null)
{
area.animator = new Thread (area);
}
area.animator.start();
}
public void stopAnim()
{
area.animator = null;
}
public void paint(Graphics g) {
Dimension d = getSize();
g.setColor (Color.black);
g.drawRect(0,0, d.width - 1, d.height - 1);
}
/* This is used to leave room to the black box painted in
* the paint() method. If we don"t do that, it is overwritten.
*/
public Insets getInsets() {
return new Insets(3,3,3,3);
}
}
//=============================================================================
/*
* The PolyArea class defines an area that will hold our two polygons.
* It will first create them by catching mouse clicks events and adding
* the points to the polygons, and it will then run the algorithm on the
* polygons.
*/
class PolyArea extends Canvas implements Runnable
{
Dimension offDimension;
Image offImage;
Graphics offGraphics;
TextField comment;
public Thread animator;
FConvexPoly poly1, poly2;
int nextStep;
int currentV1;
FPoint bestV1, bestV2, currentV2;
double bestLength;
int currentRegBase;
boolean band;
Polygon currentRegion;
boolean trigo;
public PolyArea (TextField comment)
{
animator = null;
this.comment = comment;
setBackground (Color.white);
poly1 = new FConvexPoly (Color.green);
poly2 = new FConvexPoly (Color.red);
nextStep = -1;
currentV1 = currentRegBase = -1;
bestV1 = bestV2 = currentV2 = null;
band = false;
currentRegion = null;
bestLength = 0;
trigo = true;
comment.setText ("Please enter the first polygon");
comment.setBackground (new Color (220, 255, 230));
comment.setForeground (Color.black);
}
public boolean handleEvent (Event e)
{
switch (e.id)
{
case Event.MOUSE_DOWN:
if (nextStep == -1)
{
if (! poly1.isClosed())
{
poly1.addPoint (new FPoint (e.x, e.y));
if (poly1.isClosed())
{
comment.setText ("Please enter the second polygon");
comment.setBackground (new Color (255, 220, 220));
}
}
else
{
poly2.addPoint (new FPoint (e.x, e.y));
if (poly2.isClosed())
{
comment.setText ("Press step or run to see the algorithm");
comment.setBackground (new Color (255, 255, 220));
nextStep = 0;
}
}
}
repaint();
return true;
default:
return false;
}
}
/* This method performs a step in the algorithm. It is called either
* by the applet when the "step" button is clicked, or by the animator
* thread if this one is running. */
public void stepAlgo ()
{
Point p;
switch (nextStep)
{
case 0:
comment.setText ("Searching for the first vertex");
comment.setBackground (new Color (255, 235, 200));
currentV1 = 0;
currentRegBase = 0;
band = false;
makeRegion();
p = poly1.getPoint (currentV1);
if (currentRegion.inside (p.x, p.y))
nextStep = 2;
else
nextStep = 1;
repaint();
break;
case 1:
if (! band)
band = true;
else
{
currentRegBase++;
band = false;
}
makeRegion();
p = poly1.getPoint (currentV1);
if (currentRegion.inside (p.x, p.y))
nextStep = 2;
else
nextStep = 1;
repaint();
break;
case 2:
comment.setText ("Computing length");
comment.setBackground (new Color (255, 220, 255));
makeV2();
bestV1 = poly1.getFPoint (currentV1);
bestV2 = currentV2;
bestLength = new FVector (bestV1, bestV2).length();
nextStep = 3;
repaint();
break;
case 3:
comment.setText ("Searching for the next vertex");
comment.setBackground (new Color (255, 235, 200));
if (new FVector (currentV2, poly1.getFPoint (currentV1)).isTrigo
(new FVector (currentV2, poly1.getFPoint (currentV1 + 1))))
trigo = true;
else
trigo = false;
currentV1++;
currentV2 = null;
if (poly1.getFPoint (currentV1) == poly1.getFPoint (0))
nextStep = 7;
else
{
p = poly1.getPoint (currentV1);
if (currentRegion.inside (p.x, p.y))
nextStep = 5;
else
nextStep = 4;
}
repaint();
break;
case 4:
if ((trigo || !poly2.isTrigo()) && !(trigo && !poly2.isTrigo()))
if (! band)
band = true;
else
{
currentRegBase++;
band = false;
}
else
if (! band)
{
currentRegBase--;
band = true;
}
else
band = false;
makeRegion();
p = poly1.getPoint (currentV1);
if (currentRegion.inside (p.x, p.y))
nextStep = 5;
else
nextStep = 4;
repaint();
break;
case 5:
comment.setText ("Comparing this length with the previous best");
comment.setBackground (new Color (255, 220, 255));
makeV2();
if (new FVector (poly1.getFPoint (currentV1), currentV2).length() > bestLength)
nextStep = 6;
else
nextStep = 3;
repaint();
break;
case 6:
comment.setText ("This is the new best length");
comment.setBackground (new Color (220, 255, 220));
bestV1 = poly1.getFPoint (currentV1);
bestV2 = currentV2;
bestLength = new FVector (bestV1, bestV2).length();
nextStep = 3;
repaint();
break;
case 7:
comment.setText ("Hausdorff distance from poly 1 to poly 2");
comment.setBackground (new Color (220, 220, 255));
currentV1 = -1;
currentV2 = null;
currentRegion = null;
animator = null;
repaint();
break;
default:
break;
}
}
/* The paint method draws the current state of the algorithm in the
* given Graphics, including the two polygons, the yellow region and
* the important points. */
public void paint (Graphics g)
{
poly1.fill (g);
poly2.fill (g);
if (currentRegion != null)
{
g.setColor (Color.yellow);
g.fillPolygon (currentRegion);
}
poly1.draw (g);
poly2.draw (g);
if (currentV1 >= 0)
{
Point p = poly1.getPoint (currentV1);
g.setColor (Color.blue);
g.drawOval (p.x-5, p.y-5, 10, 10);
}
if (currentV2 != null)
{
Point p1 = poly1.getPoint (currentV1);
Point p2 = currentV2.getPoint();
g.setColor (Color.blue);
g.drawOval (p2.x-5, p2.y-5, 10, 10);
g.setColor (Color.blue);
g.drawLine (p1.x, p1.y, p2.x, p2.y);
}
if (bestV1 != null)
{
Point p1 = bestV1.getPoint();
Point p2 = bestV2.getPoint();
g.setColor (Color.magenta);
g.drawLine (p1.x, p1.y, p2.x, p2.y);
}
}
/* When called by the AWT, the update method clears its offscreen
* buffer, calls paint() to draw in it, and then copies the buffer to the
* screen. */
public void update (Graphics g)
{
Dimension d = size();
if ((offGraphics == null) ||
(d.width != offDimension.width) ||
(d.height != offDimension.height))
{
offDimension = d;
offImage = createImage(d.width, d.height);
offGraphics = offImage.getGraphics();
}
offGraphics.setColor (getBackground());
offGraphics.fillRect (0, 0, d.width, d.height);
paint (offGraphics);
g.drawImage (offImage, 0, 0, this);
}
/* This method is called in the algorithm to make the new
* yellow polygon that corresponds to the sweeping yellow
* region. */
void makeRegion()
{
Polygon region;
FPoint p1, p2;
FVector v1, v2, edge;
p1 = poly2.getFPoint (currentRegBase);
p2 = poly2.getFPoint (currentRegBase + 1);
edge = new FVector (p1, p2);
if (poly2.isTrigo())
v1 = edge.normal().mult(-1);
else
v1 = edge.normal();
if (band)
{
currentRegion = FConvexPoly.infiniteRegion (p1, v1, p2, v1);
}
else
{
p2 = poly2.getFPoint (currentRegBase - 1);
edge = new FVector (p2, p1);
if (poly2.isTrigo())
v2 = edge.normal().mult(-1);
else
v2 = edge.normal();
currentRegion = FConvexPoly.infiniteRegion (p1, v1, p1, v2);
}
}
/* This method creates the new end of the current smallest distance
* that"s on the red polygon. If the current vertex of the green polygon
* is in a yellow sector, it corresponds to the current vertex of the red
* polygon. If it is in a band, it is the foot of the perpendicular to the
* current edge of the red polygon that goes through the vertex. */
void makeV2()
{
if (! band)
currentV2 = poly2.getFPoint (currentRegBase);
else
{
FPoint p1, p2;
FVector vBase;
FLine baseLine, perpendicular;
p1 = poly2.getFPoint (currentRegBase);
p2 = poly2.getFPoint (currentRegBase + 1);
vBase = new FVector (p1, p2);
baseLine = new FLine (p1, vBase);
p2 = poly1.getFPoint (currentV1);
perpendicular = new FLine (p2, vBase.normal());
currentV2 = baseLine.intersect (perpendicular);
}
}
/* The run method is called by the virtual machine to perform the
* automated animation of the algorithm. It calls the stepAlgo() method
* three times per second to animate the algorithme. */
public void run()
{
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
long startTime = System.currentTimeMillis();
while (Thread.currentThread() == animator)
{
stepAlgo();
try
{
startTime += 300;
Thread.sleep (Math.max (0, startTime-System.currentTimeMillis()));
}
catch (InterruptedException e) { break; }
}
}
}
//=============================================================================
/*
* The FConvexPoly class represents a convex polygon.
*/
class FConvexPoly
{
public Vector v;
boolean closed;
boolean trigo;
Color polyColor;
public FConvexPoly (Color c)
{
v = new Vector();
closed = false;
trigo = false;
polyColor = c;
}
public boolean isClosed ()
{
return closed;
}
public boolean isTrigo ()
{
return trigo;
}
public Point getPoint (int i)
{
return ((FPoint) v.elementAt (MesMath.mod (i, v.size()))).getPoint();
}
public FPoint getFPoint (int i)
{
return (FPoint) v.elementAt (MesMath.mod (i, v.size()));
}
public void addPoint (FPoint p)
{
if (! closed)
{
if (v.size() < 3)
{
v.addElement (p);
if (v.size() == 3)
trigo = firstVector().isTrigo (lastVector());
}
else
{
if (p.equals ((FPoint) v.firstElement()))
closed = true;
else
{
if (isNextOK (p))
v.addElement (p);
}
}
}
}
public boolean isNextOK (FPoint p)
{
FVector next;
next = new FVector ((FPoint) v.firstElement(), p);
if (firstVector().isTrigo (next) != trigo)
return false;
next = new FVector ((FPoint) v.lastElement(), p);
if (lastVector().isTrigo (next) != trigo)
return false;
next = new FVector ((FPoint) v.firstElement(), p);
if (boundVector().isTrigo (next) == trigo)
return false;
else
return true;
}
public FVector firstVector()
{
return new FVector ((FPoint) v.elementAt (0),
(FPoint) v.elementAt (1));
}
public FVector lastVector()
{
return new FVector ((FPoint) v.elementAt (v.size()-2),
(FPoint) v.elementAt (v.size()-1));
}
public FVector boundVector()
{
return new FVector ((FPoint) v.elementAt (v.size()-1),
(FPoint) v.elementAt (0));
}
/* Draw the filled polygon on the given graphics. */
public void fill (Graphics g)
{
Polygon p = new Polygon();
Point pt, pt2;
FPoint fpt;
for (int a=0; a<V.SIZE(); a++)<="" code="">
{
pt = ((FPoint) v.elementAt (a)).getPoint();
p.addPoint (pt.x, pt.y);
}
g.setColor (polyColor);
g.fillPolygon (p);
if (!closed && v.size() >= 3)
{
Polygon nextReg = new Polygon();
if (lastVector().isTrigo (firstVector()) == trigo)
{
pt = ((FPoint) v.firstElement()).getPoint();
pt2 = ((FPoint) v.lastElement()).getPoint();
nextReg.addPoint (pt.x, pt.y);
nextReg.addPoint (pt2.x, pt2.y);
FLine l1 = new FLine ((FPoint) v.firstElement(),
firstVector());
FLine l2 = new FLine ((FPoint) v.lastElement(),
lastVector());
fpt = l1.intersect (l2);
pt = fpt.getPoint();
nextReg.addPoint (pt.x, pt.y);
}
else
{
FVector firstInv = firstVector().mult (-1);
nextReg = infiniteRegion ((FPoint) v.firstElement(),
firstInv,
(FPoint) v.lastElement(),
lastVector());
}
g.setColor (Color.yellow);
g.fillPolygon (nextReg);
}
}
/* Draw the outline of the polygon on the given graphics. */
public void draw (Graphics g)
{
Point pt, pt2;
g.setColor (Color.blue);
if (v.size() > 0)
{
pt = pt2 = ((FPoint) v.firstElement()).getPoint();
for (int a=0; a<V.SIZE()-1; a++)<="" code="">
{
pt2 = ((FPoint) v.elementAt (a+1)).getPoint();
g.drawLine (pt.x, pt.y, pt2.x, pt2.y);
pt = pt2;
}
pt = ((FPoint) v.firstElement()).getPoint();
if (closed)
g.drawLine (pt.x, pt.y, pt2.x, pt2.y);
else
{
g.setColor (Color.red);
g.drawOval (pt.x-5, pt.y-5, 10, 10);
}
}
}
/* This method returns a Polygon that will look infinite when
* drawn by the Graphics methods. This should not be in here, it should
* not be a static method, but it worked so I left it this way...*/
public static Polygon infiniteRegion (FPoint p1, FVector v1, FPoint p2, FVector v2)
{
Polygon p = new Polygon();
Point pt;
double length;
FVector middle;
pt = p1.getPoint();
p.addPoint (pt.x, pt.y);
if (! p1.equals (p2))
{
pt = p2.getPoint();
p.addPoint (pt.x, pt.y);
}
length = v2.length();
pt = new Point ((int) (p2.x + (v2.x / length * 2000)),
(int) (p2.y + (v2.y / length * 2000)));
p.addPoint (pt.x, pt.y);
middle = (v1.mult (1/v1.length())).add (v2.mult (1/v2.length()));
length = middle.length();
pt = new Point ((int) (middle.x + (middle.x / length * 2000)),
(int) (middle.y + (middle.y / length * 2000)));
p.addPoint (pt.x, pt.y);
length = v1.length();
pt = new Point ((int) (p1.x + (v1.x / length * 2000)),
(int) (p1.y + (v1.y / length * 2000)));
p.addPoint (pt.x, pt.y);
return p;
}
}
//=============================================================================
/*
* The FPoint class represents a point in the plane.
*/
class FPoint
{
static double EPSILON = 10;
double x;
double y;
public FPoint (double x, double y)
{
this.x = x;
this.y = y;
}
public FPoint (int x, int y)
{
this.x = (double) x;
this.y = (double) y;
}
public FPoint (Point p)
{
this.x = (double) p.x;
this.y = (double) p.y;
}
public boolean equals (FPoint p)
{
if (new FVector (this, p).length() < EPSILON)
return true;
else
return false;
}
public Point getPoint()
{
return new Point ((int) x, (int) y);
}
}
//=============================================================================
/*
* The FVector class provides a basic 2-dimensional vector type,
* along with some uselful methods.
*/
class FVector
{
double x;
double y;
public FVector (double x, double y)
{
this.x = x;
this.y = y;
}
public FVector (FPoint a, FPoint b)
{
x = b.x - a.x;
y = b.y - a.y;
}
/* Returns the dot product of the vector with v. */
public double dot (FVector v)
{
return x * v.x + y * v.y;
}
/* Returns the vector normal to this vector. */
public FVector normal()
{
return new FVector (-y, x);
}
/* Return true if v makes an angle with the vector in the
* trigonometric direction (a left turn). */
public boolean isTrigo (FVector v)
{
if (dot (v.normal()) <= 0)
return true;
else
return false;
}
public double length()
{
return Math.sqrt (x*x + y*y);
}
public FVector add (FVector v)
{
return new FVector (x + v.x, y + v.y);
}
public FVector mult (double a)
{
return new FVector (x*a, y*a);
}
}
//=============================================================================
/*
* The FLine class represents a line. Here, it is only used to call
* its intersect() method to compute the intersection of two lines.
*/
class FLine
{
/* The line is under the form ax+by=c */
double a;
double b;
double c;
/*
* Creates a new FLine that goes through p and is supported
* by v.
*/
public FLine (FPoint p, FVector v)
{
FVector vn = v.normal();
a = vn.x;
b = vn.y;
c = a * p.x + b * p.y;
}
/* Creates a new FLine that goes through both a and b. */
public FLine (FPoint a, FPoint b)
{
this (a, new FVector (a, b));
}
/*
* The methode computes the intersection point of two
* lines. We use the JAMA matrix package (see
*
* http://math.nist.gov/javanumerics/jama/
* for details).
*/
public FPoint intersect (FLine l)
{
double[][] tabA = {{ a, b},
{l.a, l.b}};
Matrix matA = new Matrix (tabA);
double[][] tabB = {{ c},
{l.c}};
Matrix matB = new Matrix (tabB);
double[][] tabX = (matA.solve (matB)).getArray();
return new FPoint (tabX[0][0], tabX[1][0]);
}
}
//=============================================================================
/*
* The MesMath class is an abstract class that is used to perform
* division related operations on integers. I haven"t been able to tell
* java that I needed my divisions to be rounded toward minus infinity
* rather than zero, so I"ve had to do it myself.
*/
abstract class MesMath
{
public static int div (int a, int b)
{
if (a >= 0)
return a / b;
else
return (a - b + 1) / b;
}
public static int mod (int a, int b)
{
return a - div (a, b) * b;
}
}
標簽: 蒙特利爾
精彩放送:
- []環球新動態:蒙特利爾的麥吉爾大學:計算幾何課程資料
- []全球速訊:視頻編碼中畫面質量控制中最重要的部分——DataRate
- []粒子群算法原理 基于numpy6.2的粒子群算法詳解
- []世界熱議:NSM開發總結 NSM項目的技術培訓
- []世界訊息:什么是驅動程序?驅動程序和光驅有什么區別?
- []今日播報!三星i458怎么樣?報價多少?三星i458市場報價及測評
- []當前最新:mac卡巴斯基激活碼怎么用?免費領取教程來了
- []【天天時快訊】神州行幸??ㄊ鞘裁??神州行幸??ㄔ斍榻榻B
- []天天觀熱點:華僑城西部投資掛牌劍門關華僑城旅游公司2億股股份 底價4.75億元
- []環球快消息!美高梅國際酒店集團2022年度收益凈額6.74億美元 同比減少44%
- []女演員長相有多重要?看《我們的日子》里李小冉和齊歡就知道了
- []美物科技(WNW.US)收到納斯達克股價不合規通知
- []觀速訊丨社?;鶖?萬多什么水平(社?;鶖?000是什么檔次)
- []信用卡2萬額度算高嗎(信用卡2萬額度算高嗎)
- []天天通訊!交強險9月19日新規
- []焦點速遞!港財政司副司長黃偉綸對香港保持國際金融中心地位充滿信心?
- []環球快看:哈工智能重組預案出爐 擬收購宜春鋰企切入新能源業務
- []微速訊:2020年車險保費新政策
- []環球新消息丨出險一次第二年保費怎么算2020
- []天天資訊:關于春天的味道作文600字匯總5篇
- []江蘇自貿區板塊2月8日跌1.42%,連云港領跌,主力資金凈流出9927.05萬元
- []微信昵稱婦女簡單大方 女人簡單大方的微信網名匯總
- []2022教師節手抄報簡單好看字少
- []觀焦點:鄂臺青年武當文化研學營開營
- []當前速讀:有房貸的房子可以拍賣嗎?(房貸沒還完可以拍賣嗎)
- []全球快看點丨單位為什么不給員工上生育險(單位不給男職工交生育險)
- []貝因美大股東權益再現變動 4.44%持股簽署表決權委托協議
- []環球快消息!交通信用卡改還款日期怎么改(交通信用卡怎么改還款日期)
- []全球微頭條丨營業外支出和管理費用的區別(營業外支出和管理費用的區別)
- []頭條焦點:中際旭創:2月7日公司高管王軍減持公司股份合計10000股
- []天天簡訊:澄天偉業:2月7日公司高管景在軍減持公司股份合計14萬股
- []環球今日訊!南網能源:截至2023年1月31日,公司合并普通賬戶和融資融券信用賬戶的持有人數為156,602
- []速訊:北京2023年首場土拍收官:央國企仍是主角,越秀“再北上”59億元拿下兩宗
- []世界熱議:中建智地17.2億元底價拍得北京房山良鄉大學城地塊
- []觀察:科拓生物:2月7日公司高管劉曉軍減持公司股份合計8萬股
- []游族網絡(002174)股東林漓、林芮璟、林小溪合計質押5494.94萬股,占總股本6%
- []電飯鍋做面包的方法
- []焦點播報:合肥泰康人壽保險公司怎么樣(合肥泰康人壽保險公司怎么樣)
- []環球熱資訊!謝楊春等:優質供應帶動,杭州土拍迎“小陽春”
- []世界觀焦點:越秀2023開年狂飆北京,59億激進補倉,石景山死磕中海
- []半數地塊溢價成交 北京第五批集中供地收官
- []大元泵業:2月6日公司高管崔樸樂減持公司股份合計2萬股
- []每日頭條!山西太原:公積金貸款首房首付比例為20%
- []璞泰來:2月8日公司高管陳衛增持公司股份合計9.25萬股
- []越秀以33.12億元+4.5萬平現房銷售面積競得北京昌平回龍觀地塊
- []天馬科技:2月7日公司高管何騰飛、曾麗莉增持公司股份合計3.52萬股
- []當前頭條:力合科技:公司主營業務為環境監測系統研發、生產和銷售及運營服務
- []天天短訊!和醫??ń壎ǖ你y行卡丟了怎么辦理(和醫保卡綁定的銀行卡丟了怎么辦)
- []最資訊丨青島東李世園板塊萬竹云峰項目再流拍 底價已降至3.56億
- []廈門鎢業等三方加強戰略合作 推進大湖塘鎢礦開發
- []世界觀焦點:華僑城調整“18僑城06”票面利率至1.5% 并開啟回售
- []捷安高科:公司暫未涉及ChatGPT的產品和技術
- []快看:外地人在長沙買房怎么交社保(外地人怎么在長沙買房)
- []世界快看點丨職工醫保補繳會返還嗎(補繳醫保有返錢嗎)
- []免費安全工作總結(優選9篇)
- []全球熱點!“20榮盛地產MTN002”持有人會議通過變更債券本息兌付安排等議案
- []重點聚焦!深圳重啟積分入戶 專家預計未來住房需求會明顯增加
- []全球快訊:華大九天:公司主要從事EDA工具軟件的開發、銷售及相關服務
- []茂業商業子公司與關聯方簽合計700萬元物業服務協議
- []杭州網絡零售額首次突破萬億 直播相關企業注冊量全國第一
- []環球關注:紅星美凱龍為常州子公司7.07億債務提供擔保
- []全球信息:中電興發:公司作為智慧城市全面解決方案提供商和運營服務商,被納入深證人工智能(AI)50指數
- []【全球獨家】志特新材:公司的定期報告會對股東總人數進行披露,屆時請關注披露公告
- []今日熱搜:醫??ú辉诩t名單怎么解決(醫??ê诿麊稳绾窝a救)
- []信用卡第一次還款日期怎么算的(信用卡還款日怎么算年限)
- []全球熱資訊!用京醫通掛號醫保卡還能報銷嗎(京醫通綁定社??懿荒軋箐N住院)
- []全球頭條:多少人的信用卡額度達到30萬以上(多少人的信用卡額度達到30萬)
- []全球百事通!新地NOVO LAND 2B期最快下星期上載樓書
- []觀察:石嘴山新型冠狀病毒肺炎疫情:2月8日石嘴山疫情最新消息今天數據統計情況通報
- []環球熱文:雙杰電氣: “東數西算”工程是國家重點發展戰略之一,也會用到12kV的配電設備
- []百盛商業確認租賃四川綿陽科技城新區物業 用作經營購物中心等
- []需要進一步核實及完善 海航投資延期回復深交所關注函
- []新湖中寶:控股股東之一致行動人900萬股解除質押
- []世界熱消息:中國銀河:公司堅持服務國家戰略、服務中小微企業與提高業務核心競爭力相結合
- []【環球快播報】博世科:(1)公司暫無微生物燃料電池相關領域的規劃
- []奮達科技:公司與首航簽署了戰略合作協議,具體請參考以往公告
- []天天快報!多晶硅價格上漲!
- []全球最資訊丨數字信號處理技術論文
- []四連漲!硅料最高價至249元/kg
- []環球視訊!海量項目信息!光伏行業最新報告出爐
- []當前熱訊:再下一城!越秀以33.12億+現房銷售4.5萬平競得昌平信息園二期地塊
- []新動態:西方制裁打擊俄羅斯石油收入的背后:巨額利潤究竟流向了哪?
- []全球焦點!2月8日豪鵬科技漲停分析:鋰電池,動力電池回收,新能源汽車概念熱股
- []環球時訊:建發股份申請發行不超過45億元公司債 獲發改委同意注冊
- []2月8日真視通漲停分析:應急產業,東數西算,軍民融合概念熱股
- []世界快報:溫州城建30億私募債項目狀態更新為“已受理”
- []短訊!哈藥股份:道圣康膜不是本公司生產的產品,黑龍江大眾安泰藥業有限公司不是本公司的下屬企業
- []湖州安吉6宗地均以底價成交 兩山國有控股6.58億連拿2宗
- []“老撕雞防騙”掛羊頭賣狗肉,誤導投資客戶,30億客戶資金打水漂
- []2月8日新綸新材漲停分析:固態電池,鋰電池,寧德時代概念股概念熱股
- []全球熱頭條丨2月8日鴻博股份漲停分析:包裝印刷,小家電,體育產業概念熱股
- []天天微動態丨中原:一手私樓開放式及一房貨尾量比例共占逾三成
- []如何簡單的自制黑椒牛排?自制黑椒牛排的方法步驟?
- []騰訊開心鼠英語是騰訊旗下嗎?騰訊開心鼠英語介紹
- []天天要聞:左立熊小玥還在一起嗎?左立個人資料介紹?
- []環球播報:博大考神職稱計算機軟件破解版,博大考神職稱計算機考試
- []當前頭條:詩曼芬是幾線品牌?詩曼芬品牌資料介紹?
- []綿陽美食有哪些?綿陽美食分享?
- []世界即時看!網易smtp的25端口可以使用ssl端口?操作步驟
- []世界看點:深圳楊梅坑在哪里?楊梅坑好不好玩?
- B站注冊資本增幅400%至5億 目前由陳睿全資持股
- 光源資本出任獨家財務顧問 沐曦集成電路10億元A輪融資宣告完成
- 巨輪智能2021年上半年營收11.24億元 期內研發費用投入增長19.05%
- 紅棗期貨尾盤拉升大漲近6% 目前紅棗市場總庫存約30萬噸
- 嘉銀金科發布2021年Q2財報 期內凈利潤達1.27億元同比增長208%
- 成都銀行2021上半年凈利33.89億元 期內實現營收同比增長17.27億元
- 汽車之家發布2021年第二季度業績 期內新能源汽車品牌收入增長238%
- 中信銀行上半年實現凈利潤290.31億元 期末不良貸款余額706.82億元
- 光伏概念掀起漲停潮交易價格創新高 全天成交額達1.29億元
- 上半年生物藥大增45% 關鍵財務指標好轉營收賬款持續下降
- 環球即時:elasticsearch怎么安裝插件?elasticsearch拼音分詞插件安裝教程
- 資訊推薦:周杰倫演武神的一部電影叫什么名字?講述了什么劇情?
- 從虛擬化安全到容器安全:Docker容器的安全機制與解決方案
- 快報:陋室銘作者是誰?陋室銘作者資料介紹?
- 天天精選!mysql設計總結 基于mysql的bbs設計總結
- 含有狗的諺語有哪些?含有狗的諺語匯總?
- 駱駝精神是什么樣的精神?駱駝精神介紹?
- 全球熱門:12306搶票網站靠譜嗎?火車票候補購票概率有多大?
- 天天新動態:什么貸款能貸10年以上(有什么貸款能貸五年)
- 全球今頭條!攜程:年后跨境航班“量升價跌”,海外酒店錯峰均價降一成
- 每日關注!艾莉森洛曼為什么不火?瑤瑤來為您解答
- 環球動態:貨拉拉推出同城門到門跑腿服務 預計4月正式開放服務和騎手接單
- 上海浦東香楠路城市經典會所二次拍賣流拍 當前價1.08億元
- 每日短訊:香港“高才通計劃”接獲7417宗申請 其中5799宗獲批
- 焦點觀察:c盤臨時文件可以清理嗎?c盤臨時文件可以刪除
- 【全球新要聞】衛生整頓工作總結(通用31篇)
- 豫能控股:公司指定的信息披露媒體有《證券時報》《上海證券報》和巨潮資訊網,相關費用以合同簽訂為準
- 多普達830怎么樣?多普達830手機報價及性能配置介紹
- visit怎么讀的?will和visit讀音相同嗎?
- 全球滾動:《從結婚開始戀愛》什么時候播出?從結婚開始戀愛演員表
- 當前簡訊:福州高新投資1.09億元摘閩侯一宗宅地 將建設拆遷安置房項目
- 快看點丨華為y320怎么樣?華為y320報價及簡評分享
- 今日熱聞!西安空調移機哪家好?西安空調移機廠家技術信得過
- 焦點精選!win10怎么徹底清除win32trojan病毒?win32 trojan病毒清除教程
- 全球看點:尼康鏡頭標識的含義是什么?詳情介紹
- 江河集團:公司在海外的業務主要集中在東南亞地區,暫無在中東地區開展業務的計劃
- 全球最資訊丨春秋航空啟動大規模線下招聘
- 熱點在線丨【linux系統命令大全】免費使用和自由傳播的Unix系統
- matlab軟件實驗原理 matlab中的圖像增強與復原實驗
- 社?;鶖低浬陥?基數自動上調了怎么回事(公積金基數上調了社?;鶖禌]調)
- 環球今日報丨皇家加勒比:2022年Q4營收26億美元,凈虧損5億美元
- 世界觀焦點:java代碼實現二分法查找 二分法的實現
- 世界關注:北京社保外埠農村勞動力(社保為外埠農村勞動力是什么意思)
- 【環球新要聞】Waze將很快獲得Android Auto最酷的新功能之一
- 最新消息:狄拉克:量子場論的研究方法
- 環球速讀:10套極好用的PS繪畫筆刷工具 簡直就是神器
- 環球關注:越秀地產以25.99億競得石景山蘋果園地塊 配建1.5萬平方米現房
- 世界短訊!京山輕機:如公司與其他公司發生達到信息披露標準的重大合作項目,公司將按規定及時履行信息披露義務
- 快訊:拉薩城建投資30億元私募債項目狀態更新為“已反饋”
- 全球快資訊:丹化科技:公司目前沒有這方面的計劃
- 全球熱訊:房企融資分化:有國企兩月授信超千億,出險企業盼借新還舊應對償債高峰
- 華聯控股:華聯南山A區更新項目有關申請報批工作在推進中
- 當前關注:深圳安托山新業花園動工 將提供226套保障性住房
- 環球熱點評!*ST日海:上述資產轉讓的會計影響會體現在2022年年度報告中
- 信息:抖音在杭州成立無限能量房產經紀公司 注冊資本150萬
- 當前視訊!支付寶免費保單啥意思(支付寶免費領保單可以領嗎)
- 徽商銀行怎么交個人社保(我的社保是在安徽合肥徽商銀行交的,怎樣轉到天津社保交費)
- 【全球報資訊】“19泰達01”回售數量234.49萬手 回售金額23.45億元
- 環球快資訊:新華制藥:公司是全球主要解熱鎮痛等類藥物供應商,與眾多制劑生產企業均有業務往來
- 最新資訊:住建部等:啟動公共領域車輛全面電動化先行區試點
- 美的置業10.2億元公司債將于2月13日提前摘牌 利率4.4%
- 商湯-W跌超4% 此前遭軟銀集團減持近3億股
- 世界聚焦:江西:落實16條金融措施 有效防范化解優質頭部房企風險
- 當前頭條:養烏龜的缸圖片大全_養烏龜的缸
- 全球今頭條!聲迅股份:公司高度關注ChatGPT相關的行業化應用,也在探索ChatGPT與公司業務的結合點
- 動態:全城高考的觀后感
- 精彩看點:海得控制:公司在系統解決方案業務中會涉及百度相關技術與產品的應用
- 咨詢公司報告:中國旅游業復蘇路徑評估
- 超過退休年齡可以補交養老金嗎(超過退休年齡可以補交養老金嗎?)
- 【全球播資訊】降低存量房貸利率,可行嗎?
- 世界最資訊丨金科地產:累計完成 285.77 億元有息負債的展期工作
- 廣東發改委:推動國家出臺金融支持橫琴粵澳深度合作區建設政策
- 最資訊丨首進北京!中建東孚14.26億元競得北京朝陽區小紅門地塊
- 環球快報:廣東省發改委:力爭橫琴粵澳深度合作區年內“封關運作”
- 【全球獨家】實益達:公司暫無chatgpt相關技術。關于公司具體業務內容詳見公司披露的定期報告
- 【BT金融分析師】豐田重心偏向混動車型,分析師稱其沒全部押注電動汽車
- 前沿熱點:華夏銀行信用卡賬單日和還款日(華夏銀行信用卡賬單日和還款日)
- 【世界播資訊】萬達商管成功發行3億美元債,消息稱有望二季度重返港股
- 環球觀天下!越秀以25.99億元+1.5萬平現房銷售面積競得北京石景山1宗地塊
- 環球快資訊:奧普光電:公司主營業務為光電測控儀器設備、光學材料和光柵編碼器等產品的研發、生產和銷售
- 中泰證券:強預期與弱現實的博弈 關注低PB房企及央企地產附屬物業公司
- 【全球速看料】富力地產欠稅金額超千萬
- 當前快訊:江陰銀行:財務總監親屬因誤操作導致買賣公司股票構成短線交易
- 深南電路:公司在保障正常生產經營和項目建設資金需要的同時,持續加強資金的統籌管理,實現資金的高效利用
- 銀河證券:房地產基本面仍在筑底 集中供地規則優化
- 環球微頭條丨生源地助學貸款可以異地辦理嗎(生源地助學貸款可以異地辦理嗎)
- 爆冷!亞冠冠軍3-2淘汰南美冠軍進世俱杯決賽 或與皇馬爭冠
- 天天頭條:英大人壽內勤怎么樣(英大人壽內勤好不好干)
- 如何訓練抗干擾能力?訓練抗干擾能力的方法步驟?
- 每日快播:狼和小羊的作者是誰?狼和小羊是出自哪里?
- 天天時訊:《橫琴粵澳深度合作區發展促進條例》公布 自3月1日起施行
- 【東海期貨2月8日產業鏈日報】能化篇:中國需求信心增強,油價大幅上漲
- 天天熱頭條丨潤華生活服務全球發售的穩定價格期于2月8日結束
- 頭條焦點:hm是什么牌子的衣服?HM是什么的簡稱?
- 環球滾動:國信期貨早評:原油高漲,國際油脂震蕩走高,原糖走升
- 每日熱訊!羥脯氨酸怎么測定?羥脯氨酸的測定方法?
- 快播:中信建投期貨2月8日早間交易策略
- 華光環能:預計占推廣項目收入的比例不到5%,相關涉及信息屬于商業性保密信息,不便于披露
- 帶一毛字的成語有多少?帶一毛字的成語匯總?
- 【獨家焦點】什么叫甲基化?甲基化是指什么?
- 世界看熱訊:傳無錫首房首貸利率將降至3.8%!
- 頭條:富貴竹怎么養殖?富貴竹的養殖方法是什么?
- 血紅蛋白正常值是多少_血紅
- 當前速訊:xwd四輪驅動是什么?xwd四輪驅動配置怎么樣?
- 天天亮點!中國新城鎮領漲港股內房股 A股地產板塊早盤走強
- 天天即時看!北京漲價業主越來越多
- 【世界速看料】春日宴主演都有誰?春日宴講述了什么故事?
- 環球觀點:中交地產:截止1月20日收盤的股東人數為68029
- 全球滾動:云南省的簡稱是什么?云南省的面積有多大?
- 焦點熱議:【東海期貨2月8日產業鏈日報】貴金屬篇:鮑威爾發言偏鷹,金銀震蕩