java 0x2d_如何在Java中进行2D阴影投射?

你的实际问题并不完全清楚.在进行这种图形编程时,您经常需要一些操作,而Java2D的内置功能在这里相当简陋.你可以创建Point2D和Line2D对象,并且基本上可以使用你需要的所有结构,但是有些计算是……不方便(至少可以说),有些则根本没有得到适当的支持.例如,您只能检查两个Line2D对象是否相交.但是没有内置的方法来检查它们相交的位置.

然而,当我看到你链接的网站时,我想,“嘿,这可能很有趣”.

它很有趣:-)

我猜你可能有的大部分问题都是由下面的代码隐含地回答的(对不起,如果评论不充分 – 但可以随意提出一个关于不清楚的部分的更集中的问题).

由于上述原因,我开始创建一个“常用几何操作实用程序”的小型库.此库中的某些类部分包含在下面的示例中,因此它是一个独立的示例.

package stackoverflow.shadows;

import java.awt.AlphaComposite;

import java.awt.BasicStroke;

import java.awt.Color;

import java.awt.Graphics;

import java.awt.Graphics2D;

import java.awt.Rectangle;

import java.awt.RenderingHints;

import java.awt.Shape;

import java.awt.event.ComponentAdapter;

import java.awt.event.ComponentEvent;

import java.awt.event.MouseEvent;

import java.awt.event.MouseMotionListener;

import java.awt.geom.AffineTransform;

import java.awt.geom.Arc2D;

import java.awt.geom.Ellipse2D;

import java.awt.geom.FlatteningPathIterator;

import java.awt.geom.Line2D;

import java.awt.geom.Path2D;

import java.awt.geom.PathIterator;

import java.awt.geom.Point2D;

import java.awt.geom.Rectangle2D;

import java.awt.image.BufferedImage;

import java.util.ArrayList;

import java.util.Collections;

import java.util.Comparator;

import java.util.List;

import javax.swing.JFrame;

import javax.swing.JPanel;

import javax.swing.SwingUtilities;

public class ShadowsTest

{

public static void main(String[] args)

{

SwingUtilities.invokeLater(new Runnable()

{

@Override

public void run()

{

createAndShowGUI();

}

});

}

private static void createAndShowGUI()

{

JFrame f = new JFrame();

f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

f.getContentPane().add(new ShadowsTestPanel());

f.setSize(500,500);

f.setLocationRelativeTo(null);

f.setVisible(true);

}

}

class ShadowsTestPanel extends JPanel

implements MouseMotionListener

{

private final List shapes;

private final Point2D lightPosition;

private final List borderLineSegments;

private final List> shapesLineSegments;

private final BufferedImage smileyImage;

private final BufferedImage skullImage;

private final BufferedImage blendedImage;

ShadowsTestPanel()

{

addMouseMotionListener(this);

shapes = new ArrayList();

shapes.add(new Rectangle2D.Double(160, 70, 80, 50));

shapes.add(new Ellipse2D.Double(290, 120, 50, 30));

AffineTransform at0 =

AffineTransform.getRotateInstance(

Math.toRadians(45), 320, 290);

shapes.add(

at0.createTransformedShape(

new Rectangle2D.Double(300, 270, 40, 40)));

shapes.add(new Ellipse2D.Double(60, 240, 80, 110));

shapesLineSegments = new ArrayList>();

for (Shape shape : shapes)

{

shapesLineSegments.add(Shapes.computeLineSegments(shape, 1.0));

}

borderLineSegments = new ArrayList();

shapesLineSegments.add(borderLineSegments);

lightPosition = new Point2D.Double();

addComponentListener(new ComponentAdapter()

{

@Override

public void componentResized(ComponentEvent e)

{

borderLineSegments.clear();

borderLineSegments.add(

new Line2D.Double(0,0,getWidth(),0));

borderLineSegments.add(

new Line2D.Double(getWidth(),0,getWidth(),getHeight()));

borderLineSegments.add(

new Line2D.Double(getWidth(),getHeight(),0,getHeight()));

borderLineSegments.add(

new Line2D.Double(0,getHeight(),0,0));

}

});

smileyImage = createSmileyImage();

skullImage = createSkullImage();

blendedImage = createSmileyImage();

}

private static BufferedImage createSmileyImage()

{

BufferedImage image =

new BufferedImage(150, 150, BufferedImage.TYPE_INT_ARGB);

Graphics2D g = image.createGraphics();

g.setRenderingHint(

RenderingHints.KEY_ANTIALIASING,

RenderingHints.VALUE_ANTIALIAS_ON);

g.setStroke(new BasicStroke(5));

g.setColor(Color.YELLOW);

g.fill(new Ellipse2D.Double(5, 5, 140, 140));

g.setColor(Color.BLACK);

g.draw(new Ellipse2D.Double(5, 5, 140, 140));

g.fill(new Ellipse2D.Double( 50-15, 50-15, 30, 30));

g.fill(new Ellipse2D.Double(100-15, 50-15, 30, 30));

g.draw(new Arc2D.Double(25, 25, 100, 100, 190, 160, Arc2D.OPEN));

g.dispose();

return image;

}

private static BufferedImage createSkullImage()

{

BufferedImage image =

new BufferedImage(150, 150, BufferedImage.TYPE_INT_ARGB);

Graphics2D g = image.createGraphics();

g.setRenderingHint(

RenderingHints.KEY_ANTIALIASING,

RenderingHints.VALUE_ANTIALIAS_ON);

g.setStroke(new BasicStroke(5));

g.setColor(Color.WHITE);

g.fill(new Ellipse2D.Double(5, 5, 140, 140));

g.setColor(Color.BLACK);

g.draw(new Ellipse2D.Double(5, 5, 140, 140));

g.fill(new Ellipse2D.Double( 50-15, 50-15, 30, 30));

g.fill(new Ellipse2D.Double(100-15, 50-15, 30, 30));

Shape mouth =

new Arc2D.Double(25, 25, 100, 100, 190, 160, Arc2D.OPEN);

List lineSegments = Shapes.computeLineSegments(mouth, 2);

for (int i=0; i

{

Line2D line = lineSegments.get(i);

Rectangle b = line.getBounds();

Rectangle r = new Rectangle(b.x, b.y-8, b.width, 16);

g.setColor(Color.WHITE);

g.fill(r);

g.setColor(Color.BLACK);

g.draw(r);

}

g.dispose();

return image;

}

@Override

protected void paintComponent(Graphics gr)

{

super.paintComponent(gr);

Graphics2D g = (Graphics2D)gr;

g.setColor(new Color(0,0,0,200));

g.fillRect(0,0,getWidth(),getHeight());

g.setRenderingHint(

RenderingHints.KEY_ANTIALIASING,

RenderingHints.VALUE_ANTIALIAS_ON);

g.setColor(Color.BLACK);

for (Shape shape : shapes)

{

g.draw(shape);

}

List rays = createRays(lightPosition);

//paintRays(g, rays);

List closestIntersections =

computeClosestIntersections(rays);

Collections.sort(closestIntersections,

Points.byAngleComparator(lightPosition));

//paintClosestIntersections(g, closestIntersections);

//paintLinesToIntersections(g, closestIntersections);

Shape lightShape = createLightShape(closestIntersections);

g.setColor(Color.WHITE);

g.fill(lightShape);

g.drawImage(smileyImage, 150, 150, null);

blend(skullImage, 150, 150, lightShape, blendedImage);

g.drawImage(blendedImage, 150, 150, null);

g.setColor(Color.YELLOW);

double r = 10;

g.fill(new Ellipse2D.Double(

lightPosition.getX()-r, lightPosition.getY()-r,

r+r, r+r));

}

private static void blend(

BufferedImage image, int x, int y,

Shape lightShape, BufferedImage result)

{

int w = image.getWidth();

int h = image.getHeight();

Graphics2D g = result.createGraphics();

g.setComposite(AlphaComposite.SrcOver);

g.setColor(new Color(0,0,0,0));

g.fillRect(0,0,w,h);

g.drawImage(image, 0, 0, null);

g.translate(-x, -y);

g.setComposite(AlphaComposite.SrcOut);

g.fill(lightShape);

g.dispose();

}

private Shape createLightShape(

List closestIntersections)

{

Path2D shadowShape = new Path2D.Double();

for (int i=0; i

{

Point2D p = closestIntersections.get(i);

double x = p.getX();

double y = p.getY();

if (i == 0)

{

shadowShape.moveTo(x, y);

}

else

{

shadowShape.lineTo(x, y);

}

}

shadowShape.closePath();

return shadowShape;

}

private void paintRays(Graphics2D g, List rays)

{

g.setColor(Color.YELLOW);

for (Line2D ray : rays)

{

g.draw(ray);

}

}

private void paintClosestIntersections(Graphics2D g,

List closestIntersections)

{

g.setColor(Color.RED);

double r = 3;

for (Point2D p : closestIntersections)

{

g.fill(new Ellipse2D.Double(

p.getX()-r, p.getY()-r, r+r, r+r));

}

}

private void paintLinesToIntersections(Graphics2D g,

List closestIntersections)

{

g.setColor(Color.RED);

for (Point2D p : closestIntersections)

{

g.draw(new Line2D.Double(lightPosition, p));

}

}

private List computeClosestIntersections(List rays)

{

List closestIntersections = new ArrayList();

for (Line2D ray : rays)

{

Point2D closestIntersection =

computeClosestIntersection(ray);

if (closestIntersection != null)

{

closestIntersections.add(closestIntersection);

}

}

return closestIntersections;

}

private List createRays(Point2D lightPosition)

{

final double deltaRad = 0.0001;

List rays = new ArrayList();

for (List shapeLineSegments : shapesLineSegments)

{

for (Line2D line : shapeLineSegments)

{

Line2D ray0 = new Line2D.Double(lightPosition, line.getP1());

Line2D ray1 = new Line2D.Double(lightPosition, line.getP2());

rays.add(ray0);

rays.add(ray1);

rays.add(Lines.rotate(ray0, +deltaRad, null));

rays.add(Lines.rotate(ray0, -deltaRad, null));

rays.add(Lines.rotate(ray1, +deltaRad, null));

rays.add(Lines.rotate(ray1, -deltaRad, null));

}

}

return rays;

}

private Point2D computeClosestIntersection(Line2D ray)

{

final double EPSILON = 1e-6;

Point2D relativeLocation = new Point2D.Double();

Point2D absoluteLocation = new Point2D.Double();

Point2D closestIntersection = null;

double minRelativeDistance = Double.MAX_VALUE;

for (List lineSegments : shapesLineSegments)

{

for (Line2D lineSegment : lineSegments)

{

boolean intersect =

Intersection.intersectLineLine(

ray, lineSegment, relativeLocation, absoluteLocation);

if (intersect)

{

if (relativeLocation.getY() >= -EPSILON &&

relativeLocation.getY() <= 1+EPSILON)

{

if (relativeLocation.getX() >= -EPSILON &&

relativeLocation.getX() < minRelativeDistance)

{

minRelativeDistance =

relativeLocation.getX();

closestIntersection =

new Point2D.Double(

absoluteLocation.getX(),

absoluteLocation.getY());

}

}

}

}

}

return closestIntersection;

}

@Override

public void mouseMoved(MouseEvent e)

{

lightPosition.setLocation(e.getPoint());

repaint();

}

@Override

public void mouseDragged(MouseEvent e)

{

}

}

class Points

{

/**

* Creates a comparator that compares points by the

* angle of the line between the point and the given

* center

*

* @param center The center

* @return The comparator

*/

public static Comparator byAngleComparator(

final Point2D center)

{

return new Comparator()

{

@Override

public int compare(Point2D p0, Point2D p1)

{

double dx0 = p0.getX() - center.getX();

double dy0 = p0.getY() - center.getY();

double dx1 = p1.getX() - center.getX();

double dy1 = p1.getY() - center.getY();

double angle0 = Math.atan2(dy0, dx0);

double angle1 = Math.atan2(dy1, dx1);

return Double.compare(angle0, angle1);

}

};

}

}

class Lines

{

/**

* Rotate the given line around its starting point, by

* the given angle, and stores the result in the given

* result line. If the result line is null,

* then a new line will be created and returned.

*

* @param line The line

* @param angleRad The rotation angle

* @param The result line

* @return The result line

*/

static Line2D rotate(Line2D line, double angleRad, Line2D result)

{

double x0 = line.getX1();

double y0 = line.getY1();

double x1 = line.getX2();

double y1 = line.getY2();

double dx = x1 - x0;;

double dy = y1 - y0;

double sa = Math.sin(angleRad);

double ca = Math.cos(angleRad);

double nx = ca * dx - sa * dy;

double ny = sa * dx + ca * dy;

if (result == null)

{

result = new Line2D.Double();

}

result.setLine(x0, y0, x0+nx, y0+ny);

return result;

}

}

class Intersection

{

/**

* Epsilon for floating point computations

*/

private static final double EPSILON = 1e-6;

/**

* Computes the intersection of the given lines.

*

* @param line0 The first line

* @param line1 The second line

* @param relativeLocation Optional location that stores the

* relative location of the intersection point on

* the given line segments

* @param absoluteLocation Optional location that stores the

* absolute location of the intersection point

* @return Whether the lines intersect

*/

public static boolean intersectLineLine(

Line2D line0, Line2D line1,

Point2D relativeLocation,

Point2D absoluteLocation)

{

return intersectLineLine(

line0.getX1(), line0.getY1(),

line0.getX2(), line0.getY2(),

line1.getX1(), line1.getY1(),

line1.getX2(), line1.getY2(),

relativeLocation, absoluteLocation);

}

/**

* Computes the intersection of the specified lines.

*

* Ported from

* http://www.geometrictools.com/LibMathematics/Intersection/

* Wm5IntrSegment2Segment2.cpp

*

* @param s0x0 x-coordinate of point 0 of line segment 0

* @param s0y0 y-coordinate of point 0 of line segment 0

* @param s0x1 x-coordinate of point 1 of line segment 0

* @param s0y1 y-coordinate of point 1 of line segment 0

* @param s1x0 x-coordinate of point 0 of line segment 1

* @param s1y0 y-coordinate of point 0 of line segment 1

* @param s1x1 x-coordinate of point 1 of line segment 1

* @param s1y1 y-coordinate of point 1 of line segment 1

* @param relativeLocation Optional location that stores the

* relative location of the intersection point on

* the given line segments

* @param absoluteLocation Optional location that stores the

* absolute location of the intersection point

* @return Whether the lines intersect

*/

public static boolean intersectLineLine(

double s0x0, double s0y0,

double s0x1, double s0y1,

double s1x0, double s1y0,

double s1x1, double s1y1,

Point2D relativeLocation,

Point2D absoluteLocation)

{

double dx0 = s0x1 - s0x0;

double dy0 = s0y1 - s0y0;

double dx1 = s1x1 - s1x0;

double dy1 = s1y1 - s1y0;

double invLen0 = 1.0 / Math.sqrt(dx0*dx0+dy0*dy0);

double invLen1 = 1.0 / Math.sqrt(dx1*dx1+dy1*dy1);

double dir0x = dx0 * invLen0;

double dir0y = dy0 * invLen0;

double dir1x = dx1 * invLen1;

double dir1y = dy1 * invLen1;

double c0x = s0x0 + dx0 * 0.5;

double c0y = s0y0 + dy0 * 0.5;

double c1x = s1x0 + dx1 * 0.5;

double c1y = s1y0 + dy1 * 0.5;

double cdx = c1x - c0x;

double cdy = c1y - c0y;

double dot = dotPerp(dir0x, dir0y, dir1x, dir1y);

if (Math.abs(dot) > EPSILON)

{

if (relativeLocation != null || absoluteLocation != null)

{

double dot0 = dotPerp(cdx, cdy, dir0x, dir0y);

double dot1 = dotPerp(cdx, cdy, dir1x, dir1y);

double invDot = 1.0/dot;

double s0 = dot1*invDot;

double s1 = dot0*invDot;

if (relativeLocation != null)

{

double n0 = (s0 * invLen0) + 0.5;

double n1 = (s1 * invLen1) + 0.5;

relativeLocation.setLocation(n0, n1);

}

if (absoluteLocation != null)

{

double x = c0x + s0 * dir0x;

double y = c0y + s0 * dir0y;

absoluteLocation.setLocation(x, y);

}

}

return true;

}

return false;

}

/**

* Returns the perpendicular dot product, i.e. the length

* of the vector (x0,y0,0)x(x1,y1,0).

*

* @param x0 Coordinate x0

* @param y0 Coordinate y0

* @param x1 Coordinate x1

* @param y1 Coordinate y1

* @return The length of the cross product vector

*/

private static double dotPerp(double x0, double y0, double x1, double y1)

{

return x0*y1 - y0*x1;

}

}

class Shapes

{

/**

* Create a list containing line segments that approximate the given

* shape.

*

* @param shape The shape

* @param flatness The allowed flatness

* @return The list of line segments

*/

static List computeLineSegments(Shape shape, double flatness)

{

List result = new ArrayList();

PathIterator pi =

new FlatteningPathIterator(

shape.getPathIterator(null), flatness);

double[] coords = new double[6];

double previous[] = new double[2];

double first[] = new double[2];

while (!pi.isDone())

{

int segment = pi.currentSegment(coords);

switch (segment)

{

case PathIterator.SEG_MOVETO:

previous[0] = coords[0];

previous[1] = coords[1];

first[0] = coords[0];

first[1] = coords[1];

break;

case PathIterator.SEG_CLOSE:

result.add(new Line2D.Double(

previous[0], previous[1],

first[0], first[1]));

previous[0] = first[0];

previous[1] = first[1];

break;

case PathIterator.SEG_LINETO:

result.add(new Line2D.Double(

previous[0], previous[1],

coords[0], coords[1]));

previous[0] = coords[0];

previous[1] = coords[1];

break;

case PathIterator.SEG_QUADTO:

// Should never occur

throw new AssertionError(

"SEG_QUADTO in flattened path!");

case PathIterator.SEG_CUBICTO:

// Should never occur

throw new AssertionError(

"SEG_CUBICTO in flattened path!");

}

pi.next();

}

return result;

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值