I want to get nested stencils to work in OpenGL.
I call them masks from now on.
So the stencil buffer is cleared to all 0's.
I make my first mask, the grey area.
Now that have to be all 1's in the stencil buffer, and normal drawing is not allowed to happen outside that mask.
Now I create a second mask which is a child of the first one:
It would only update the stencil buffer if the area is inside the area of the previous mask:
Same if we would make another child mask, we would only increment if it was inside the parent mask.
Now if we would end the last mask (the green one), we can decrement the values where they where equal to the mask depth.
Ok that is the idea, but I have been stuck on getting this to work for weeks (not full time...).
Either masking does not work, or it does not work as expected. Here I provide code that makes the most sense to me.
I use processing, which can be downloaded here https://processing.org/ (download and run, simple as that).
PGL pgl;
Rect current_rect;
void setup() {
size(600, 600, P3D);
noStroke();
// will have a depth of 0, equal to the stencil buffer after a clear
current_rect = make_rect(null, 0, 0, width, height);
}
class Rect {
float x;
float y;
float w;
float h;
ArrayList<Rect> children = new ArrayList<Rect>();
Rect parent;
int mask_depth;
}
Rect make_rect(Rect parent, float x, float y, float w, float h) {
Rect r = new Rect();
r.x = x;
r.y = y;
r.w = w;
r.h = h;
if (parent != null) {
r.parent = parent;
r.mask_depth = parent.mask_depth + 1;
parent.children.add(r);
}
return r;
}
void draw() {
if (frameCount == 2) noLoop();
println();
pgl = beginPGL();
pgl.enable(PGL.STENCIL_TEST);
pgl.clear(PGL.STENCIL_BUFFER_BIT);
background(150, 80, 70);
begin_mask(100, 100, 400, 400);
fill(150);
rect(100, 100, 400, 400);
// SHOULD NOT FALL OUTSIDE OF THE MASK!
debug_text(" A ");
// disabled for now cause the above begin_mask
// goes already wrong
if (false) {
begin_mask(50, 150, 500, 100); // B
fill(100);
rect(50, 150, 500, 100);
debug_text(" B ");
if (true) {
begin_mask(200, 50, 100, 500); // C
fill(50);
rect(200, 50, 100, 500);
debug_text(" C ");
end_mask(); // C
}
end_mask(); // B
// why is the one from above (C) on this one?
// disable this one to see what I mean
if (true) {
begin_mask(50, 350, 500, 100); // D
fill(75);
rect(50, 350, 500, 100);
fill(255);
debug_text(" D ");
end_mask(); // D
}
}
end_mask(); // A
flush();
endPGL();
}
void begin_mask(float x, float y, float w, float h) {
current_rect = make_rect(current_rect, x, y, w, h);
flush();
pgl.colorMask(false, false, false, false);
pgl.depthMask(false);
pgl.stencilOp(PGL.KEEP, PGL.KEEP, PGL.INCR);
println("increment stencil when stencil is equal to: "+current_rect.parent.mask_depth);
pgl.stencilFunc(PGL.EQUAL, current_rect.parent.mask_depth, 0xFF);
// write to stencil buffer
noStroke();
fill(0);
rect(x, y, w, h);
flush();
enable_normal_draw_mode();
}
void enable_normal_draw_mode() {
pgl.stencilMask(0x00);
println("normal write when stencil depth is: "+(current_rect.mask_depth));
pgl.stencilFunc(PGL.GEQUAL, current_rect.mask_depth, 0xff);
pgl.stencilOp(PGL.KEEP, PGL.KEEP, PGL.KEEP);
pgl.colorMask(true, true, true, true);
pgl.depthMask(true);
flush();
println("enable_normal_draw_mode "+current_rect.mask_depth);
}
void end_mask() {
// decrement stencil mask as if we never existed
pgl.stencilMask(0xff);
pgl.stencilFunc(PGL.GEQUAL, current_rect.mask_depth, 0xff);
pgl.stencilOp(PGL.KEEP, PGL.KEEP, PGL.DECR);
pgl.colorMask(false, false, false, false);
pgl.depthMask(false);
noStroke();
fill(0);
rect(current_rect.x, current_rect.y, current_rect.w, current_rect.h);
flush();
current_rect = current_rect.parent;
enable_normal_draw_mode();
}
void debug_text(String s) {
fill(255);
text(xxx(s), 0, 0);
}
String xxx(String to_repeat) {
String result = "";
String line = "";
for (int x = 0; x < 50; x++) {
line += to_repeat;
}
for (int y = 0; y < 50; y++) {
result += line + "\n";
}
return result;
}
Related
I am currently working with the Ani Library in Processing.
I'm trying to fade in some rectangles one by one (on a PGraphics). To let the rectangles appear I use a loop - whose size I can control with MouseX. So one rectangle appears after the other. But I also want to fade in each one individually using the Ani Library. Currently I can only get the fade in to affect all of them at once.
So I tried to make an object out of the rectangle and put the FadeIn inside the object. But it behaves similar here – all fade in at once.
How can I solve this? Do I need an Array or an Array List for this?
This is my Code without an object:
import de.looksgood.ani.*;
PGraphics pg;
float alpha;
void setup() {
size(1920, 1920);
rectMode(CENTER);
Ani.init(this);
pg = createGraphics(width, height);
}
void draw() {
drawPG();
image(pg, 0,0);
}
void drawPG () {
pg.beginDraw();
pg.pushMatrix();
pg.translate(100, height/4.75);
pg.background(255);
pg.noStroke();
Ani.to(this, 10, "alpha", 255, Ani.EXPO_OUT);
color black = color(0, 0, 0, alpha);
pg.fill(black);
int blocks = int(map(mouseX, 0, width, 0, 7));
for (int i = 0; i < blocks; i++) {
pg.pushMatrix();
pg.translate(i * 200 + i*100, 0);
pg.rect(0, 0, 200, 200);
pg.popMatrix();
}
pg.popMatrix();
pg.endDraw();
}
And this is my tried out version with an Object:
import de.looksgood.ani.*;
PGraphics pg;
Rectangle myRect;
void setup() {
size(1920, 1920);
rectMode(CENTER);
myRect = new Rectangle();
Ani.init(this);
pg = createGraphics(width, height);
}
void draw() {
drawPG();
image(pg, 0,0);
}
void drawPG () {
pg.beginDraw();
pg.pushMatrix();
pg.translate(100, height/4.75);
pg.background(255);
pg.noStroke();
int blocks = int(map(mouseX, 0, width, 0, 7));
for (int i = 0; i < blocks; i++) {
pg.pushMatrix();
pg.translate(i * 200 + i*100, 0);
myRect.display(pg);
pg.popMatrix();
}
pg.popMatrix();
pg.endDraw();
}
class Rectangle {
float alpha;
Rectangle() {
}
void display(PGraphics pg) {
Ani.to(this, 10, "alpha", 255, Ani.EXPO_OUT);
color black = color(0, 0, 0, alpha);
pg.fill(black);
pg.rect(0, 0, 200, 200);
}
}
+++ EDIT +++
Found a solution here:
https://processing.org/tutorials/objects
And this gave me the hint to really work with an array. And then it says for the PGraphic:
void drawPG () {
pg.beginDraw();
pg.pushMatrix();
pg.translate(100, height/4.75);
pg.background(255);
pg.noStroke();
int blocks = int(map(mouseX, 0, width, 0, rectangles.length));
for (int i = 0; i < blocks; i++) {
pg.pushMatrix();
pg.translate(i * 200 + i*100, 0);
rectangles[i].display(pg);
pg.popMatrix();
}
pg.popMatrix();
pg.endDraw();
}
Above setup();
Rectangle[] rectangles = new Rectangle[7];
And in setup();
for (int i = 0; i < rectangles.length; i++) {
rectangles[i] = new Rectangle();
}
I was steered over to this forum when I asked my lecturer for advice on a piece of code for a group project. The general idea is that there are two images on top of each other, the user can wipe the top image away to reveal the one underneath.
Using some other projects from this forum, I have managed to get the basics running, however I am struggling to get the code to the starting point once the user lets go of the mouse.
I would also appreciate any advice regarding how to convert this to using a touch screen. I have looked at the multitouch code within the processing app, however it does not allow me to add images to this, and if I try and use the computer software it does not seem to like the multitouch. Is there any way around this?
The code I currently have is below, I will be greatful so any advice or input- thanks in advance!
PImage img, front;
int xstart, ystart, xend, yend;
int ray;
void setup()
{
size(961, 534);
img = loadImage("back.jpg");
front = loadImage("front.jpg");
xstart = 0;
ystart = 0;
xend = img.width;
yend = img.height;
ray = 50;
}
void draw()
{
{
img.loadPixels();
front.loadPixels();
// loop over image pixels
for (int x = xstart; x < xend; x++)
{
for (int y = ystart; y < yend; y++ )
{
int loc = x + y*img.width;
float dd = dist(mouseX, mouseY, x, y);
// pixels distance less than ray
if (mousePressed && dd < 50)
{
// set equal pixel
front.pixels[loc] = img.pixels[loc];
}
else
{
if (!mousePressed)
{
// reset- this is what I have not been able to work as of yet
front.pixels[loc] = ;
}
}
}
}
img.updatePixels();
front.updatePixels();
// show front image
image(front, 0, 0);
}
}
I recommend to use a mask instead of changing the pixels of the image. Create an empty image and associated it as mask to the the image:
img = loadImage("back.jpg");
front = loadImage("front.jpg");
mask = createImage(img.width, img.height, RGB);
img.mask(mask);
If you now draw both images, then you can only "see" the front image:
image(front, 0, 0);
image(img, 0, 0);
Set the color of the mask (255, 255, 255) instead of changing the pixel of front:
mask.pixels[loc] = color(255, 255, 255);
and reapply the mask to the image
img.mask(mask);
When the mouse button is released, the pixels of the mask have to be changed back to (0, 0, 0) or simply create a new and empty mask:
mask = createImage(img.width, img.height, RGB);
See the example where I applied the suggestions to your original code:
PImage img, front, mask;
int xstart, ystart, xend, yend;
int ray;
void setup() {
size(961, 534);
img = loadImage("back.jpg");
front = loadImage("front.jpg");
mask = createImage(img.width, img.height, RGB);
img.mask(mask);
xstart = 0;
ystart = 0;
xend = img.width;
yend = img.height;
ray = 50;
}
void draw() {
img.loadPixels();
front.loadPixels();
// loop over image pixels
for (int x = xstart; x < xend; x++) {
for (int y = ystart; y < yend; y++ ) {
int loc = x + y*img.width;
float dd = dist(mouseX, mouseY, x, y);
if (mousePressed && dd < 50) {
mask.pixels[loc] = color(255, 255, 255);
}
else {
if (!mousePressed) {
//mask = createImage(img.width, img.height, RGB);
mask.pixels[loc] = color(0, 0, 0);
}
}
}
}
mask.updatePixels();
img.mask(mask);
// show front image
image(front, 0, 0);
image(img, 0, 0);
}
I'm trying to understand how the JavaFX canvas setStroke method works. It doesn't set the color of pixels to the desired value.
No problem with the setFill method though.
Canvas canvas = new Canvas(500, 500);
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.setFill(Color.RED);
gc.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
int x = 10;
int y = 10;
printPixelRGB(x, y); // displays: red=255, green=0, blue=0 -> OK
// scenario 1: using fill-methods
gc.setStroke(Color.WHITE);
gc.strokeRect(x, y, 200, 200);
printPixelRGB(x, y); // displays: red=255, green=191, blue=191 -> ????
// scenario 2: using stroke-methods
gc.setFill(Color.WHITE);
gc.fillRect(x, y, 200, 200);
printPixelRGB(x, y); // displays: red=255, green=255, blue=255 -> OK
private void printPixelRGB(int x, int y) {
WritableImage snap = gc.getCanvas().snapshot(null, null);
int color = snap.getPixelReader().getArgb(x, y);
int red = (color >> 16) & 0xff;
int green = (color >> 8) & 0xff;
int blue = color & 0xff;
System.out.printf("red=%-3d, green=%-3d, blue=%-3d \n", red, green, blue);
} // printPixelRGB()
The result from scenario 2 is as expected.
The result from scenario 1 on the other hand is very strange: the pixel is not completely white!! How come?
How can I fix this?
Thank you
This is the effect of anti-aliasing.
The pixel is not 100% covered by the line. Therefore the resulting color is interpolated between the pixel color before the drawing operation and the stroke color.
The following code allows you to observe the effect:
#Override
public void start(Stage primaryStage) {
Canvas canvas = new Canvas(800, 300);
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.setFill(Color.RED);
gc.fillRect(0, 0, 800, 300);
gc.setStroke(Color.WHITE);
final double dx = 0;
for (double x = 10, s = 1; x < (800 - 40); s++, x += 50) {
gc.setLineWidth(s);
gc.strokeLine(x + dx, 0, x + dx, 300);
}
WritableImage snap = canvas.snapshot(null, null);
PixelReader reader = snap.getPixelReader();
for (int x = 10; x < (800 - 40); x += 50) {
Color color = reader.getColor(x, 150);
System.out.printf("red=%-3d, green=%-3d, blue=%-3d\n", (int) (color.getRed() * 0xFF),
(int) (color.getGreen() * 0xFF), (int) (color.getBlue() * 0xFF));
}
primaryStage.setScene(new Scene(new StackPane(canvas)));
primaryStage.show();
}
The first line drawn covers the x interval of [10-lineWidth/2, 10+lineWidth/2] = [9.5, 10.5]. The column with index 10 is only half covered which is why the resulting color is ((255, 0, 0) + (255, 255, 255)) / 2 = (255, 127, 127) (already rounded to integral values)
Modify dx to cover the column completely and you get the stroke color:
final double dx = 0.5;
The pixel is covered completely by the rectangle you fill in your code and the color is set to the fill color for this reason. Add 0.5 to one of the starting coordinates of the rectangle you fill, and you'll observe a similar effect as with the stroke.
I am trying to figure out why is my Array not showing any lines, I am trying to make it draw 10 different lines, now println of x and z does show 10 coordinates but they are identical and it seems as if they are being passed down to the object line itself but not drawn. It seems quite simple but I know I've made a mistake somewhere but not sure where.
lines[] mylinesArray = new lines[10];
void setup() {
size(1028, 768, P3D);
translate(width/2, height/2, 0);
rotateX(radians(180));
float prp = -2000;
float vrp = 50;
float z = random(prp, 2000);
float x = random(vrp, 500);
for (int i = 0; i<mylinesArray.length; i++) {
mylinesArray[i] = new lines(z, 0, x);
println(z);
println(x);
}
}
void draw() {
background(255);
for (int i=0; i<mylinesArray.length; i++) {
// println(i);
mylinesArray[i].drawlines();
//println("still doing it");
}
}
class lines {
color fillColor;
color strokeColor;
PVector dot1;
PVector dot2;
lines(float xpos, float ypos, float zpos) {
dot1 = new PVector(500+xpos, 200, 700+zpos);
dot2 = new PVector(500+xpos, -400, 700+zpos);
fillColor = strokeColor = color(random(255), random(255), random(255));
}
void drawlines() {
pushMatrix();
line (dot1.x, dot1.y, dot1.z, dot2.x, dot2.y, dot2.z);
stroke(0);
// println("new");
popMatrix();
}
void whatever() {
// println("please");
}
}
I am writing a java application using swing in which I need to draw a grid above a square. In order to do so, I am using the drawLine(...) method provided by the Graphics class.
Everything works fine except that it takes a lot of time to draw each line (more than 20 sec for 50 lines...). I can even see the lines being drawn in real time. One weird thing is that the horizontal lines are drawn way faster than the vertical lines (almost instantly).
I might be doing something wrong. Here is the code for the grid.
public void drawGrid(Graphics g){
g.setColor(new Color(255, 255, 255, 20));
int width = getWidth();
int height = (int) (width * Utils.PLATE_RATIO);
int step = pixelSize*gridSpacing;
Color bright = new Color(255, 255, 255, 100);
Color transparent = new Color(255, 255, 255, 20);
for(int ix = insets.left + step;
ix < width; ix += step){
if(((ix - insets.left) / step) % 10 == 0){
g.setColor(bright);
}
else{
g.setColor(transparent);
}
g.drawLine(ix, insets.top, ix, height+insets.top);
}
for(int iy = insets.top+step;
iy < (insets.top + height); iy += step){
if(((iy - insets.top) / step) % 10 == 0){
g.setColor(bright);
}
else{
g.setColor(transparent);
}
g.drawLine(insets.left, iy, width + insets.left, iy);
}
}
The code you have posted is fine, there is no problems in it.
Here is a working example of a component using your method (a bit simplified):
public static class MyGrid extends JComponent
{
private int step = 10;
public MyGrid ()
{
super ();
}
public Dimension getPreferredSize ()
{
return new Dimension ( 500, 500 );
}
protected void paintComponent ( Graphics g )
{
super.paintComponent ( g );
drawGrid ( g );
}
public void drawGrid ( Graphics g )
{
int width = getWidth ();
int height = getHeight ();
Color bright = new Color ( 255, 255, 255, 200 );
Color transparent = new Color ( 255, 255, 255, 100 );
for ( int ix = step; ix < width; ix += step )
{
if ( ( ix / step ) % 10 == 0 )
{
g.setColor ( bright );
}
else
{
g.setColor ( transparent );
}
g.drawLine ( ix, 0, ix, height );
}
for ( int iy = step; iy < height; iy += step )
{
if ( ( iy / step ) % 10 == 0 )
{
g.setColor ( bright );
}
else
{
g.setColor ( transparent );
}
g.drawLine ( 0, iy, width, iy );
}
}
}
I guess there is some problem outside that piece of code.
P.S. A bit an offtopic but...
I suggest you to calculate the visible part of the painting area (using either JComponent's getVisibleRect () method or Graphics g.getClip ().getBounds () method) and limit your paintings with only that area.
That small optimization could speedup component's painting in times if it is really large (for example with 10000x10000 pixels component's area).
Here is how I solved the problem using Double-Buffering, as advised by #sureshKumar. I am simply drawing on an offscreen image and simply calling drawImage() when the drawing is over. This seems to do the trick.
Edit: this seems to be useful only if you want to call your painting methods (in my case drawGrid()) from outside of the paintComponent(...) method.
Here is the code:
private Graphics bufferGraphics;
private Image offScreen;
private Dimension dim;
//other attributes not shown...
public CentralPanel(){
//Some initialization... (not shown)
//I added this listener so that the size of my rectangle
//and of my grid changes with the frame size
this.addComponentListener(new ComponentListener() {
#Override
public void componentResized(ComponentEvent e) {
dim = getVisibleRect().getSize();
offScreen = createImage(dim.width, dim.height);
bufferGraphics = offScreen.getGraphics();
repaint();
revalidate();
}
//other methods of ComponentListener not shown
});
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(offScreen, 0, 0, null);
}
public void drawGrid(){
int width = dim.width - insets.left - insets.right;
width -= (width % plateSize.getXSize());
int height = (int) (width*Utils.PLATE_RATIO);
height -= (height % plateSize.getYSize());
int step = pixelSize*gridSpacing;
Color bright = new Color(255, 255, 255, 100);
Color transparent = new Color(255, 255, 255, 20);
for(int ix = insets.left + step;
ix < (width+insets.left); ix += step){
if(((ix - insets.left) / step) % 10 == 0){
bufferGraphics.setColor(bright);
}
else{
bufferGraphics.setColor(transparent);
}
//I am now drawing on bufferGraphics instead
//of the component Graphics
bufferGraphics.drawLine(ix, insets.top, ix, height+insets.top);
}
step *= Utils.PLATE_RATIO;
for(int iy = insets.top+step;
iy < (insets.top + height); iy += step){
if(((iy - insets.top) / step) % 10 == 0){
bufferGraphics.setColor(bright);
}
else{
bufferGraphics.setColor(transparent);
}
bufferGraphics.drawLine(insets.left, iy, width + insets.left, iy);
}
}
P.S.: If this should be added as an edit to my question, please tell me and I'll do it.