the inner shadow disappears during the transition it looks like the inner shadow is also scaled.
public void showTowerRange(int x0, int y0, double range) {
Circle circle = new Circle(
x0 * MAP_CELLS_WIDTH + MAP_CELLS_WIDTH / 2,
y0 * MAP_CELLS_HEIGHT + MAP_CELLS_HEIGHT / 2,
1,
Color.RED
);
circle.setOpacity(0.5);
gameRoot.getChildren().add(circle);
ScaleTransition scl = new ScaleTransition(Duration.millis(SHOW_RANGE_EFFECT_DURATION),circle);
scl.setByX(range * MAP_CELLS_WIDTH);
scl.setByY(range * MAP_CELLS_HEIGHT);
FadeTransition fd = new FadeTransition(Duration.millis(SHOW_RANGE_EFFECT_DURATION),circle);
fd.setByValue(-.3);
circle.setEffect(new InnerShadow(BlurType.GAUSSIAN, Color.GREEN, 4, 1, 0, 0));
circle.setEffect(new DropShadow(BlurType.GAUSSIAN, Color.WHITE, 2, 0, 0, 0));
ParallelTransition prl = new ParallelTransition(scl,fd);
prl.play();
}
Well, I have good news and bad news for you. The good one - scale transition does not ruin inner shadow. The bad one - it is you ruining inner shadow:)
The point is, setEffect() method does not add another effect, it just set effect property of the Node, replacing previous value. What you need to do is to chain you effects (see example here). So instead of this:
circle.setEffect(new InnerShadow(BlurType.GAUSSIAN, Color.GREEN, 4, 1, 0, 0));
circle.setEffect(new DropShadow(BlurType.GAUSSIAN, Color.WHITE, 2, 0, 0, 0));
You must do this:
InnerShadow is = new InnerShadow(BlurType.GAUSSIAN, Color.GREEN, 4, 1, 0, 0);
DropShadow ds = new DropShadow(BlurType.GAUSSIAN, Color.WHITE, 2, 0, 0, 0);
ds.setInput(is);
circle.setEffect(ds);
Related
How add a few chart in loop .
I try this but I have only one:
for (int x = 1; x < 5; x++)
{
var chart = worksheet.Drawings.AddChart(zaklad, OfficeOpenXml.Drawing.Chart.eChartType.ColumnClustered);
chart.Title.Text = "Total";
chart.SetPosition(wiersz, 14, wiersz * x, 25);
chart.SetSize(100, 100*x);
worksheet.Cells[wiersz, 14, wiersz + 1, 25].Style.Fill.PatternType = ExcelFillStyle.Solid;
ExcelAddress valueAddress = new ExcelAddress(wiersz, 14 , wiersz + 1, 25);
chart.Legend.Border.LineStyle = eLineStyle.Solid;
chart.Legend.Border.Fill.Style = eFillStyle.SolidFill;
chart.Legend.Border.Fill.Color = Color.DarkBlue;
}
My guess is you're drawing the last one (and the biggest according to your code) above the others.
Try the following:
// left, top, width, height
chart.SetPosition(0, 0, 100 * x, 100);
chart.SetSize(100, 100);
I can't seem to wrap my head around these shadow map matrices. It works great when light angles are about a 45 degree lookat position to 0,0,0. For example, if the sun angle is high(y) vs x,z. The shadows don't line up with the models. A light position of (-7,-10,7) or (-9,-10,0) works fine. (-1,-10,1) is skewed. Here is my code example. -x is left, -y is up and -z is far.
public Vector3f cameraPosition = new Vector3f(0f, -10f, 7f);
public float[] lightPosition = new float[]{-7f, -10.0f, 7f}
draw objects to shadow map texture
Matrix.setLookAtM(sunMatrix, 0, -lightPosition[0], lightPosition[1], lightPosition[2],
0, 0, 0,
0f, 1f, 0f);
//projection matrix plus sun
float width = (globals.glScreenSize/2);
float height = (globals.glScreenSize/2);
Matrix.orthoM(projectionMatrix, 0, -width, width,
-height, height,
-1f, 100f);
tempMatrix = new float[16];
Matrix.multiplyMM(tempMatrix, 0, projectionMatrix, 0, sunMatrix, 0);//add camera matrix to perspective
projectionMatrix = tempMatrix.clone();
//translate
Matrix.setIdentityM(modelMatrix, 0);//set to 0
//translate
Matrix.translateM(modelMatrix, 0, modelPos.location.x,
-(modelPos.location.y),
modelPos.location.z);//move
//rotate
Matrix.rotateM(modelMatrix, 0, modelPos.angles.x, 1f, 0f, 0f);
Matrix.rotateM(modelMatrix, 0, -modelPos.angles.y, 0f, 1f, 0f);
Matrix.rotateM(modelMatrix, 0, -modelPos.angles.z, 0f, 0f, 1f);
//scale
Matrix.scaleM(modelMatrix, 0, modelPos.scales.x,
modelPos.scales.y,
modelPos.scales.z);//scale
Matrix.multiplyMM(viewProjMatrix, 0, projectionMatrix, 0, modelMatrix, 0);
//Matrix.multiplyMM(projectionMatrix, 0, globals.viewProjMatrix, 0, modelMatrix, 0);//perspective/model/view projection matrix
finalMatrix = viewProjMatrix.clone();//final matrix created
rotate around y axis and draw shadow map to screen
Matrix.setLookAtM(modelMatrix, 0, 0f, 0f, 0f,
globals.lightPosition[0], 0f, -globals.lightPosition[2],
0f, 1f, 0f);
//scale
Matrix.scaleM(modelMatrix, 0, scale.x, scale.y, scale.y);//scale
Matrix.multiplyMM(projectionMatrix, 0, globals.viewProjMatrix, 0, modelMatrix, 0);//projection matrix
finalMatrix = projectionMatrix.clone();//final matrix created
enter image description here
Thanks for any help,
Norm
I changed,
float height = (globals.glScreenSize/2) * Math.abs(lightPos.y);
Shadows all line up with the models now. If you are looking for a simple shadow map and obj loader example, check out https://github.com/Normfly/myOpenglES20
Happy coding,
Norm
I'm making a particle-attractor-thing in Processing 3.0, and I'm having difficulty when adding multiples of what I've deemed 'Nodes'; objects that automatically attract or repel particles without mouse input. Normally left-clicking the mouse attracts these particles and right-clicking repels them. I made Nodes so that I could have multiple locations attracting/repelling at once.
The main problem I'm having atm is when I add more than one Node, only the last-added one actually works; all others do nothing.
Here is all my code including two other classes (Particle and Node). It's a lot of code, but I'd really appreciate it if someone could point out my error(s).
http://pastebin.com/iKELuVJ7
I believe the trouble is in the Particle class in the Node for loop where I am setting acc = d, but I don't know a good workaround.
As an additional question, does anyone know a good way to make the strength of the attraction scale inversely with distance from the attracting object? I tried a backwards map() function with arbitrary upper/lower limits which works for now, but I'd prefer not to hardcode it. Any other general improvements/recommendations are appreciated.
That's a nice looking sketch. You are totally right about acc = d.
This part:
for (Node n: nodes) {
PVector d = PVector.sub(n.loc, loc);
d.normalize();
d.mult(n.strength);
acc = d;
}
means you're overwriting the acc vector which d.
Since you run loop, in the end acc will be equal to the last node in your list.
If you want each node to have a certain influence, simply add the node based vector to acc:
for (Node n: nodes) {
PVector d = PVector.sub(n.loc, loc);
d.normalize();
d.mult(n.strength);
acc.add(d);
}
You did this already on the mouse interaction.
In terms of other recommendations, you could try to cache PVector instances more often. Bellow I supply a basic example of how to cache the mousePosition (as a single top level variable, rather than multiple instances, per particle, per frame). mag() uses sqrt() which can be costly. Try using magSq() instead (and using squared ranges instead). You can spot these this in chapter 6.15 A Few Last Notes: Optimization Tricks of Nature of Code by Daniel Shiffman. I warmly recommend this book.
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
PVector m = new PVector(mouseX, mouseY);
ArrayList<Particle> parts = new ArrayList<Particle>();
ArrayList<Node> nodes = new ArrayList<Node>();
float speed = 2;
float grav = 4;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void setup() {
size(800, 800, P2D);
// fullScreen(P2D);
noStroke();
smooth(4);
blendMode(ADD);
frameRate(60);
for (int i = 0; i < 1000; i++) {
parts.add(new Particle(new PVector(random(width), random(height)), (int)random(1, 25)));
}
nodes.add(new Node(new PVector(200, 400), 0.75));
nodes.add(new Node(new PVector(400, 400), 0.50));
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void draw() {
background(0);
if(mousePressed){
m.set(mouseX, mouseY);
}
for (Node n: nodes) {
n.draw();
}
for (int i = 0; i < parts.size(); i++) {
Particle p = (Particle) parts.get(i);
p.draw();
p.boundary();
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class Particle {
PVector loc, vel, acc;
int size = 5;
Particle(PVector loc, int size) {
this.loc = loc;
vel = new PVector();
acc = new PVector();
this.size = size;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void draw() {
acc.mult(0);
for (Node n: nodes) {
PVector d = PVector.sub(n.loc, loc);
d.normalize();
d.mult(n.strength);
//acc = d;
acc.add(d);
}
if (mousePressed && mouseButton == LEFT) {
PVector d = PVector.sub(m, loc);
//d.normalize();
d.setMag(map(d.mag(), 0, 2200, speed, 0));
d.mult(speed);
//acc = d;
acc.add(d);
} else if (mousePressed && mouseButton == RIGHT) {
PVector d = PVector.sub(m, loc);
d.normalize();
d.mult(-1*speed);
//acc = d;
acc.add(d);
}
acc.set(acc);
acc.mult(acc.mag());
vel.add(acc);
//vel.add(new PVector(0, grav));
loc.add(vel);
acc.mult(0);
//vel.mult(pow(0.90, 0.1*size));
vel.mult(0.97);
colorScheme(0);//"RedYellow"
ellipse(loc.x, loc.y, size, size);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void boundary() {
if(loc.x < 0)
vel.x *= -1;
if(loc.x > width)
vel.x *= -1;
if(loc.y < 0)
vel.y *= -1;
if(loc.y > height)
vel.y *= -1;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
String[] patterns = {"RedYellow","RedMagenta","GreenYellow","GreenCyan","BlueMagenta","BlueCyan","Cyclic","BWCyclic","PROTOTYPE"};
//void colorScheme(String pattern) {
void colorScheme(int pattern) {
//compute this once, rather than multiple times per case
//also check out magSq() https://processing.org/reference/PVector_magSq_.html
float mag = vel.mag();
switch (pattern) {
case 0:
fill(255, map(mag, 0, 20, 0, 255), map(mag - 20, 0, 20, 0, 255)); //Red Yellow White
break;
case 1:
fill(255, map(mag - 20, 0, 20, 0, 255), map(mag, 0, 20, 0, 255)); //Red Magenta White
break;
case 2:
fill(map(mag, 0, 20, 0, 255), 255, map(mag - 20, 0, 20, 0, 255)); //Green Yellow White
break;
case 3:
fill(map(mag - 20, 0, 20, 0, 255), 255, map(mag, 0, 20, 0, 255)); //Green Cyan White
break;
case 4:
fill(map(mag, 0, 20, 0, 255), map(mag - 20, 0, 20, 0, 255), 255); //Blue Magenta White
break;
case 5:
fill(map(mag - 20, 0, 20, 0, 255), map(mag, 0, 20, 0, 255), 255); //Blue Cyan White
break;
case 6:
fill(cos(map(mag, 0, 20, 0, 360))*255, cos(map(mag + 20/3, 0, 20, 0, 360))*255, cos(map(mag + 40/3, 0, 20, 0, 360))*255); //Cyclic
break;
case 7:
fill(cos(map(mag, 0, 20, 0, 360))*255, cos(map(mag, 0, 20, 0, 360))*255, cos(map(mag, 0, 20, 0, 360))*255); //B&W Cyclic
break;
case 8:
fill(0, 0, 0); //Cyclic
break;
default:
stroke(255, 255, 255);
fill(255, 255, 255);
break;
}
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class Node {
PVector loc;
float strength;
Node(PVector loc, float strength) {
this.loc = loc;
this.strength = strength;
}
void draw() {
fill(255, 255, 255,64);
ellipse(loc.x, loc.y, 50, 50);
}
}
I have the following test code, where I try to clip a MeshView with a circle.
I also tried putting the meshView into a group then clipping that, but this result in a black circle.
Is there a way to clip a MeshView, preferably without putting it into a group?
import scalafx.application.JFXApp
import scalafx.application.JFXApp.PrimaryStage
import scalafx.scene.image.Image
import scalafx.scene.paint.{Color, PhongMaterial}
import scalafx.scene.shape.{TriangleMesh, Circle, MeshView}
import scalafx.scene.{Group, PerspectiveCamera, Scene, SceneAntialiasing}
object Test4 extends JFXApp {
stage = new PrimaryStage {
scene = new Scene(500, 500, true, SceneAntialiasing.Balanced) {
fill = Color.LightGray
val clipCircle = Circle(150.0)
val meshView = new MeshView(new RectangleMesh(500,500)) {
// takes a while to load
material = new PhongMaterial(Color.White, new Image("https://peach.blender.org/wp-content/uploads/bbb-splash.png"), null, null, null)
}
// val meshGroup = new Group(meshView)
meshView.setClip(clipCircle)
root = new Group {children = meshView; translateX = 250.0; translateY = 250.0; translateZ = 560.0}
camera = new PerspectiveCamera(false)
}
}
}
class RectangleMesh(Width: Float, Height: Float) extends TriangleMesh {
points = Array(
-Width / 2, Height / 2, 0,
-Width / 2, -Height / 2, 0,
Width / 2, Height / 2, 0,
Width / 2, -Height / 2, 0
)
texCoords = Array(
1, 1,
1, 0,
0, 1,
0, 0
)
faces = Array(
2, 2, 1, 1, 0, 0,
2, 2, 3, 3, 1, 1
)
The clippling actually works fine over the MeshView wrapped around a Group.
If you check JavaDoc for setClip():
There is a known limitation of mixing Clip with a 3D Transform. Clipping is essentially a 2D image operation. The result of a Clip set on a Group node with 3D transformed children will cause its children to be rendered in order without Z-buffering applied between those children.
As a result of this:
Group meshGroup = new Group(meshView);
meshGroup.setClip(clipCircle);
you will have a 2D image, and it seems Material is not applied. However you can check there's a mesh, by seting this:
meshView.setDrawMode(DrawMode.LINE);
So in your case, adjusting dimensions:
#Override
public void start(Stage primaryStage) {
Circle clipCircle = new Circle(220.0);
MeshView meshView = new MeshView(new RectangleMesh(400,400));
meshView.setDrawMode(DrawMode.LINE);
Group meshGroup = new Group(meshView);
meshGroup.setClip(clipCircle);
PerspectiveCamera camera = new PerspectiveCamera(false);
StackPane root = new StackPane();
final Circle circle = new Circle(220.0);
circle.setFill(Color.TRANSPARENT);
circle.setStroke(Color.RED);
root.getChildren().addAll(meshGroup, circle);
Scene scene = new Scene(root, 500, 500, true, SceneAntialiasing.BALANCED);
scene.setCamera(camera);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
will give this:
In the end, clipping doesn't make sense with 3D shapes. For that you can use just 2D shape to get the result you want.
If you want 3D clipping have a look at CSG operations. Check this question for a JavaFX based solution.
I have a gradient, e. g. green to red which ranges from 0 to 100. I need to lookup a color out of that gradient for any given value. Currently I'm painting a line on a canvas, fill it, take a snapshot and use a pixelreader to get the color. Does anyone know a better way? It seems like overkill to me.
A simple version of the code:
private Color getColor( double value) {
Canvas canvas = new Canvas(100, 1);
GraphicsContext gc = canvas.getGraphicsContext2D();
Stop[] stops = new Stop[] { new Stop(0, Color.GREEN), new Stop(1, Color.RED)};
LinearGradient linearGradient = new LinearGradient(0, 0, 1, 0, true, CycleMethod.NO_CYCLE, stops);
gc.setFill(linearGradient);
gc.rect( 0, 0, canvas.getWidth(), canvas.getHeight());
gc.fill();
WritableImage image = new WritableImage((int) canvas.getWidth(), (int) canvas.getHeight());
image = canvas.snapshot(null, image);
PixelReader imageReader = image.getPixelReader();
Color imageColor = imageReader.getColor( (int) value, 0);
}
Thank you very much!
You can interpolate the color yourself:
Color imageColor = Color.GREEN.interpolate(Color.RED, value / 100.0);