- 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/
我喜欢 smartcase,也喜欢 * 和 # 搜索命令。但我更希望 * 和 # 搜索命令区分大小写,而/和 ?搜索命令遵循 smartcase 启发式。 是否有隐藏在某个地方我还没有找到的设置?我宁
关闭。这个问题是off-topic .它目前不接受答案。 想改进这个问题? Update the question所以它是on-topic对于堆栈溢出。 10年前关闭。 Improve this qu
从以下网站,我找到了执行java AD身份验证的代码。 http://java2db.com/jndi-ldap-programming/solution-to-sslhandshakeexcepti
似乎 melt 会使用 id 列和堆叠的测量变量 reshape 您的数据框,然后通过转换让您执行聚合。 ddply,从 plyr 包看起来非常相似..你给它一个数据框,几个用于分组的列变量和一个聚合
我的问题是关于 memcached。 Facebook 使用 memcached 作为其结构化数据的缓存,以减少用户的延迟。他们在 Linux 上使用 UDP 优化了 memcached 的性能。 h
在 Camel route ,我正在使用 exec 组件通过 grep 进行 curl ,但使用 ${HOSTNAME} 的 grep 无法正常工作,下面是我的 Camel 路线。请在这方面寻求帮助。
我正在尝试执行相当复杂的查询,在其中我可以排除与特定条件集匹配的项目。这是一个 super 简化的模型来解释我的困境: class Thing(models.Model) user = mod
我正在尝试执行相当复杂的查询,我可以在其中排除符合特定条件集的项目。这里有一个 super 简化的模型来解释我的困境: class Thing(models.Model) user = mod
我发现了很多嵌入/内容项目的旧方法,并且我遵循了在这里找到的最新方法(我假设):https://blog.angular-university.io/angular-ng-content/ 我正在尝试
我正在寻找如何使用 fastify-nextjs 启动 fastify-cli 的建议 我曾尝试将代码简单地添加到建议的位置,但它不起作用。 'use strict' const path = req
我正在尝试将振幅 js 与 React 和 Gatsby 集成。做 gatsby developer 时一切看起来都不错,因为它发生在浏览器中,但是当我尝试 gatsby build 时,我收到以下错
我试图避免过度执行空值检查,但同时我想在需要使代码健壮的时候进行空值检查。但有时我觉得它开始变得如此防御,因为我没有实现 API。然后我避免了一些空检查,但是当我开始单元测试时,它开始总是等待运行时异
尝试进行包含一些 NOT 的 Kibana 搜索,但获得包含 NOT 的结果,因此猜测我的语法不正确: "chocolate" AND "milk" AND NOT "cow" AND NOT "tr
我正在使用开源代码共享包在 iOS 中进行 facebook 集成,但收到错误“FT_Load_Glyph failed: glyph 65535: error 6”。我在另一台 mac 机器上尝试了
我正在尝试估计一个标准的 tobit 模型,该模型被审查为零。 变量是 因变量 : 幸福 自变量 : 城市(芝加哥,纽约), 性别(男,女), 就业(0=失业,1=就业), 工作类型(失业,蓝色,白色
我有一个像这样的项目布局 样本/ 一种/ 源/ 主要的/ java / java 资源/ .jpg 乙/ 源/ 主要的/ java / B.java 资源/ B.jpg 构建.gradle 设置.gr
如何循环遍历数组中的多个属性以及如何使用map函数将数组中的多个属性显示到网页 import React, { Component } from 'react'; import './App.css'
我有一个 JavaScript 函数,它进行 AJAX 调用以返回一些数据,该调用是在选择列表更改事件上触发的。 我尝试了多种方法来在等待时显示加载程序,因为它当前暂停了选择列表,从客户的 Angul
可能以前问过,但找不到。 我正在用以下形式写很多语句: if (bar.getFoo() != null) { this.foo = bar.getFoo(); } 我想到了三元运算符,但我认
我有一个表单,在将其发送到 PHP 之前我正在执行一些验证 JavaScript,验证后的 JavaScript 函数会发布用户在 中输入的文本。页面底部的标签;然而,此消息显示短暂,然后消失...
我是一名优秀的程序员,十分优秀!