- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我目前正尝试按照本教程在 Java 中实现 2D 阴影转换方法: http://ncase.me/sight-and-light/
我想坚持使用 Line2D 和 Polygon 对象。到目前为止,这是我的代码的主要部分:
for (Polygon p : Quads.polygons) {
for (int i = 0; i < p.npoints; i++) {
osgCtx.setStroke(new BasicStroke(0.1f));
Line2D line = new Line2D.Double(mousePos.getX(), mousePos.getY(), p.xpoints[i], p.ypoints[i]);
osgCtx.draw(line);
}
osgCtx.setStroke(new BasicStroke(1.0f));
osgCtx.draw(p);
}
这给出了这样的结果:
在构建线条的参数形式时,我感到很困惑。我不知道如何用 Java 的方法实现数学。有人可以在代码方面为我指出正确的方向来实现这一点吗?
最佳答案
目前还不完全清楚您的实际问题是什么。在进行这种图形编程时,有很多操作是您经常需要的,而 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<Shape> shapes;
private final Point2D lightPosition;
private final List<Line2D> borderLineSegments;
private final List<List<Line2D>> shapesLineSegments;
private final BufferedImage smileyImage;
private final BufferedImage skullImage;
private final BufferedImage blendedImage;
ShadowsTestPanel()
{
addMouseMotionListener(this);
shapes = new ArrayList<Shape>();
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<List<Line2D>>();
for (Shape shape : shapes)
{
shapesLineSegments.add(Shapes.computeLineSegments(shape, 1.0));
}
borderLineSegments = new ArrayList<Line2D>();
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<Line2D> lineSegments = Shapes.computeLineSegments(mouth, 2);
for (int i=0; i<lineSegments.size(); 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<Line2D> rays = createRays(lightPosition);
//paintRays(g, rays);
List<Point2D> 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<Point2D> closestIntersections)
{
Path2D shadowShape = new Path2D.Double();
for (int i=0; i<closestIntersections.size(); 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<Line2D> rays)
{
g.setColor(Color.YELLOW);
for (Line2D ray : rays)
{
g.draw(ray);
}
}
private void paintClosestIntersections(Graphics2D g,
List<Point2D> 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<Point2D> closestIntersections)
{
g.setColor(Color.RED);
for (Point2D p : closestIntersections)
{
g.draw(new Line2D.Double(lightPosition, p));
}
}
private List<Point2D> computeClosestIntersections(List<Line2D> rays)
{
List<Point2D> closestIntersections = new ArrayList<Point2D>();
for (Line2D ray : rays)
{
Point2D closestIntersection =
computeClosestIntersection(ray);
if (closestIntersection != null)
{
closestIntersections.add(closestIntersection);
}
}
return closestIntersections;
}
private List<Line2D> createRays(Point2D lightPosition)
{
final double deltaRad = 0.0001;
List<Line2D> rays = new ArrayList<Line2D>();
for (List<Line2D> 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<Line2D> 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<Point2D> byAngleComparator(
final Point2D center)
{
return new Comparator<Point2D>()
{
@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 <code>null</code>,
* 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<Line2D> computeLineSegments(Shape shape, double flatness)
{
List<Line2D> result = new ArrayList<Line2D>();
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;
}
}
关于java - 如何在 Java 中进行 2D 阴影转换?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23966914/
如何在 XNA 中用图元(线条)制作的矩形周围制作阴影效果?我目前正在制作我的矩形,方法是将图元放入我制作的批处理中,然后添加纹理作为它们的背景。这些矩形应该象征着“ window ”。 我希望它们也
我刚刚在引擎中安装了照明系统。在以下屏幕截图中,您可以看到一个亮起的指示灯(黄色方框): 考虑到在照亮场景的光线之上,它还实现了FOV,该FOV将遮挡视场之外的任何物体。这就是为什么阴影的左侧部分看起
我是不熟悉Gradle并尝试编译我的项目的新手,但是也“阴影化”(就像在maven中一样)本地jar文件。 我正在尝试使用gradle shadow插件,但是当我运行“shadowJar”时,它并没有
用 JavaScript 编写解析器,对于任何语言,显然都使用 Map 来存储名称到变量的映射。 大多数语言允许以某种方式或内部作用域中的另一个变量遮蔽外部作用域中的变量。实现这一点的理想数据结构是功
// Shadowing #include using namespace std; const int MNAME = 30; const int M = 13; class Pers
我想设置我的导航栏和状态栏,就像下面链接中图片中的示例一样。我怎么能这样做 see the example 最佳答案 您可以通过像这样设置样式属性来简单地做到这一点: se
我以为我理解阴影的概念。但是这段代码让我想知道: public class Counter { int count = 0; public void inc() { count++; } pu
尝试遵循概述的方法 here为我的 UINavigationController 添加阴影。但是,该方法似乎不起作用。 这是我使用的代码: - (void)viewDidLoad { [sup
如何给 UITableViewCell 上下两边添加阴影? 我试过这个: cell.layer.shadowOpacity = 0.75f; cell.layer.shadowRadius = 5.0
我有一个博客,我为它制作了自定义光标,但它们并不是我想要的样子。使用一些免费的光标编辑程序制作光标,它们看起来只有一种颜色,没有边框、黑色或阴影。即使以图片形式上传后,在线.png 文件看起来也没有阴
我能得到像这张附图那样的带阴影的饼图吗? 如果是,怎么办? 这是饼图的代码: 最佳答案 遗憾的是,Kendo UI 饼图不支持任何 3D 元素。您可以在 Telerik 论坛中阅读相关信息 he
我一直试图获得给定的 curl 阴影效果 here对于 box-shadow:inset 但没有得到任何东西。任何onw有任何线索我可以让它工作吗? 一直试图让它在这个 上工作jsfiddle 将其更
我正在构建一个类似商店的东西,所以我有产品,侧面有圆形按钮,当我点击它们时,它会改变产品的颜色。一切都很完美,但我希望当我单击按钮时,按钮保持高亮状态。代码: CSS: #but1 { posi
我想创建一个底部边框下方有框显示的 H2 这是我的“基本”代码: My H2 #toto{ box-shadow: 0 4px 2px -2px gray; } 但我想
我有一个包含卡片列表的模板: --> {{event.eventTitle}} {{event.eve
CSS 阴影有问题。我不知道如何摆脱这里的顶部阴影:http://i.imgur.com/5FX62Fx.png 我得到的: box-shadow: 0 -3px 4px -6px #777, 0 3
在我的应用程序中,我想保留状态栏但使其背景与主屏幕相同。 所以我创建了一个自定义主题来设置应用程序背景: @drawable/background_shelf true
滚动 ListView 时如何去除阴影。 我在列表的顶部和底部出现了阴影。 最佳答案 关于android ListView 阴影,我们在Stack Overflow上找到一个类似的问题: https
我想在我的 Swift 4 中使用 MaterialComponents 为我的 View 添加阴影,但我不明白如何使用阴影电梯。我创建了一个名为 ShadowedView 的类,类似于 docume
给定以下 CAShapeLayer 是否可以在提示处添加如下图所示的阴影? 我正在使用 UIBezierPath 来绘制条形图。 - (CAShapeLayer *)gaugeCircleLayer
我是一名优秀的程序员,十分优秀!