What's error in line draw code below?
line1 draw vertical line correctly
line2 draw horizontal line correctly
line3 and line4 draw diagonal line instead of horizontal line and
vertical line
void GxDrawLine(HWND wnd, INT x0, INT y0, INT x1, INT y1, UINT line_thickness, UINT col) {
COLORREF color = (COLORREF) col;
HPEN pen = NULL;
if (line_thickness == 1) {
SetDCPenColor(GetDC(wnd), color);
} else {
pen = CreatePen(PS_SOLID, line_thickness, color);
SelectObject(GetDC(wnd), pen);
}
MoveToEx(GetDC(wnd), x0, y0, NULL);
LineTo(GetDC(wnd), x1, y1);
if (pen) {
SelectObject(GetDC(wnd), GetStockObject(DC_PEN));
DeleteObject(pen);
}
}
HWND wnd1 = CreateWindowExW(0, wc1.lpszClassName, L "Button",
WS_TABSTOP | WS_CHILD | WS_VISIBLE, 10, 10,
60, 60, wnd, NULL, wc.hInstance, & a); //NULL);
GxDrawLine(wnd1, 0, 0, 0, 48, 1, 0xf5f5f5);
GxDrawLine(wnd1, 0, 0, 48, 0, 1, 0xf5f5f5);
GxDrawLine(wnd1, 48, 0, 48, 48, 1, 0xf5f5f5);
GxDrawLine(wnd1, 0, 48, 48, 48, 1, 0xf5f5f5);
You must not call GetDC(wnd) n times without releasing the DC => Memory leak
Fixed code =>
void GxDrawLine(HWND wnd, INT x0, INT y0, INT x1, INT y1, UINT line_thickness, UINT col)
{
HPEN hPenOld = NULL ;
COLORREF crColorOld = NULL;
HDC hDC = GetDC(wnd);
HPEN pen = NULL;
if (line_thickness == 1)
{
hPenOld = (HPEN)SelectObject(hDC, (HGDIOBJ)GetStockObject(DC_PEN));
COLORREF crColorOld = SetDCPenColor(hDC, col);
}
else
{
pen = CreatePen(PS_SOLID, line_thickness, col);
hPenOld = (HPEN)SelectObject(hDC, pen);
}
MoveToEx(hDC, x0, y0, NULL);
LineTo(hDC, x1, y1);
SetDCPenColor(hDC, crColorOld);
SelectObject(hDC, hPenOld);
if (pen)
DeleteObject(pen);
ReleaseDC(wnd, hDC);
}
Related
I'm trying to create layers of 3d boxes in Processing. I want them to appear solid, so that you can't see the boxes "behind" other boxes, but the way they're displaying makes them seem transparent; you can see the stroke of boxes behind other boxes. How do I make them appear solid?
// number of boxes
int numBox = 300;
// width of each box
int boxWidth = 30;
// number of boxes per row
float numPerRow;
void setup() {
size(800, 800, P3D);
pixelDensity(1);
colorMode(HSB, 360, 100, 100, 100);
background(40, 6, 85);
stroke(216, 0, 55);
smooth(4);
fill(0, 0, 90, 100);
numPerRow = width / boxWidth;
}
void draw() {
background(40, 6, 85);
translate((boxWidth / 2), 100);
rotateX(-PI/6);
rotateY(PI/8);
for (int i = 0; i < numBox; i++) {
drawBox(i);
if (i == numBox - 1) {
noLoop();
}
}
}
void drawBox(int i) {
if ((i % 2) == 0) {
pushMatrix();
translate(((boxWidth / 2) * i) % width, 20 * floor(i / (2 * numPerRow)));
translate(0, -((i % 30) / 2));
box(boxWidth, i % 30, boxWidth);
popMatrix();
};
}
Close-up of how the boxes are being displayed:
The issue is that the boxes are intersecting and the strokes of these intersecting boxes are what give the appearance of "see through".
I'm noticing you are using x and y translation, but not z.
If you don't plan to increase x, y spacing to avoid intersections, you can easily offset rows on the z axis so rows of boxes appear in front of each other.
Here's a slightly modified version of your code illustrating this idea:
// number of boxes
int numBox = 300;
// width of each box
int boxWidth = 30;
// number of boxes per row
float numPerRow;
void setup() {
size(800, 800, P3D);
pixelDensity(1);
colorMode(HSB, 360, 100, 100, 100);
background(40, 6, 85);
stroke(216, 0, 55);
smooth(4);
fill(0, 0, 90, 100);
numPerRow = width / boxWidth;
}
void draw() {
background(40, 6, 85);
translate((boxWidth / 2), 100);
if(mousePressed){
rotateX(map(mouseY, 0, height, -PI, PI));
rotateY(map(mouseX, 0, width, PI, -PI));
}else{
rotateX(-PI/6);
rotateY(PI/8);
}
for (int i = 0; i < numBox; i++) {
drawBox(i);
//if (i == numBox - 1) {
// noLoop();
//}
}
}
void drawBox(int i) {
if ((i % 2) == 0) {
pushMatrix();
float x = ((boxWidth / 2) * i) % width;
float y = 20 * floor(i / (2 * numPerRow));
float z = y * 1.5;
translate(x, y, z);
translate(0, -((i % 30) / 2));
box(boxWidth, i % 30, boxWidth);
popMatrix();
};
}
(Click and drag to rotate and observe the z offset.
Feel free to make z as interestersting as you need it it.
Nice composition and colours!
(framing (window size) could use some iteration/tweaking, but I'm guessing this is WIP))
Just another question! I'm trying to make the circle bounce around, but it's not working I even tried the most basic way, of just adding a value (from a 'step' int) to the circle x, but it's not working. What's the approach I should follow?
I know it's a basic question, but I'm knew to this :)
float time;
PFont font1;
/*float posX, posY, velX, velY, raio;
int dirX = 1;
int dirY = -1;*/
int passo = 2;
color c1 = color (253, 196, 80, 40);
color c2 = color(254, 127, 168, 40);
color c3 = color (53, 63, 114, 80);
color c4 = color (206, 186, 221, 80);
void setup() {
size(600, 800);
smooth();
background (#F6C4C7);
ellipseMode(RADIUS);
noStroke();
time = 17;
}
//make gradient
void desenhar_grad(float posX, float posY, int raio, color color1, color color2) {
pushStyle();
noStroke();
for (int r = raio; r > 0; r--) {
int tom = lerpColor(color1, color2, map(r, 0, raio, 0.0, 1.0)); // os últimos dois valores são as cores. o primeiro é o centro, o segundo é o exterior
fill(tom);
circle(posX, posY, r * 2);
}
popStyle();
}
/*void move() {
posY+=velY*dirY;
if (posY>height-raio || posY<raio)
dirY*=-1;
posX+=velX*dirX;
if (posX>width-raio || posX<raio)
dirX*=-1;
}*/
void draw () {
smooth();
for (int linha = 0; linha < 3; linha++) {
for (int coluna = 0; coluna < 3; coluna++) {
if (time <= 19) {
desenhar_grad(150 + coluna * 150, 200 + linha * 150, 30, c1, c2);
} else
desenhar_grad(150 + coluna * 150, 200 + linha * 150, 30, c4, c3);
}
}
}
} ```
Also, should I create a class for the circles in order to optimize the code?
Thank you!
I see your attempt with using the move() function (and related variables).
Again, close, but there are a few gotchas:
the values used in move() should be initialised: otherwise they'll default to 0 and any number multiplied by 0 is 0 which will result in no movement at all
once you have computed the correct posX, posY you could use those to translate() everything (i.e. the gradients): once everything is translated the 150, 200 offsets could be removed (and used as posX, posY initial values)
it's unclear with the "pivot" of the 3x3 gradient grid should be at the centre or the top left corner of the grid. Let's start with the simpler top left option. This can easily be changed later to centre simply by adding had the grid size to posX and posY
Here's a modified version of your sketch using the notes above:
float time;
// initialise movement variables
float posX = 150, posY = 200, velX = 1, velY = 1;
int raio = 30;
int dirX = 1;
int dirY = -1;
color c1 = color (253, 196, 80, 40);
color c2 = color(254, 127, 168, 40);
color c3 = color (53, 63, 114, 80);
color c4 = color (206, 186, 221, 80);
void setup() {
size(600, 800);
smooth();
ellipseMode(RADIUS);
smooth();
noStroke();
time = 17;
}
//make gradient
void desenhar_grad(float posX, float posY, int raio, color color1, color color2) {
pushStyle();
noStroke();
for (int r = raio; r > 0; r--) {
int tom = lerpColor(color1, color2, map(r, 0, raio, 0.0, 1.0)); // os últimos dois valores são as cores. o primeiro é o centro, o segundo é o exterior
fill(tom);
circle(posX, posY, r * 2);
}
popStyle();
}
void move() {
posY += velY * dirY;
if (posY > height - raio || posY < raio)
dirY *= -1;
posX += velX * dirX;
if (posX > width - raio || posX < raio)
dirX *= -1;
// for testing only:
println("posX",posX, "width", width, "posY", posY, "height", height);
}
void draw () {
if(!mousePressed) background (#F6C4C7);
// update posX, posY taking sketch borders into account
move();
// translate everything to the updated position
translate(posX, posY);
for (int linha = 0; linha < 3; linha++) {
for (int coluna = 0; coluna < 3; coluna++) {
if (time <= 19) {
desenhar_grad(coluna * 150, linha * 150, raio, c1, c2);
} else
desenhar_grad(coluna * 150, linha * 150, raio, c4, c3);
}
}
}
I've removed unused variables for clarity and added a few comments.
There are still a few confusing, perhaps unrelated items:
should the screen be cleared or should the grid leave trails ? (for now you can leave trails by holding the mouse pressed, but you can easily choose when to call background() based on the look you're going for)
how should the time variable be updated ? Currently it's set to 17 in setup() and doesn't change making the if/else condition inside the nested for loops redundant. Perhaps you meant to update in draw() based on some conditions ?
should the grid move as a whole or should each gradient move on its own ? my assumption is you're trying move the grid altogether however if you want to move each gradient on its own bare in mind you will need to use an array for each variable used in move() so it can be updated independently for each gradient (e.g. float[] posX, posY, velX, velY).)
Side note: If the movement is this simple you could get away with pos and
vel variables and not use dir variables:
void move() {
posY += velY;
if (posY > height - raio || posY < raio)
velY *= -1;
posX += velX;
if (posX > width - raio || posX < raio)
velY *= -1;
}
Manually updating each x,y variable is a great way to learn.
At a later date you might find PVector useful for movement.
I want to extract the edge of the raindrop.
This is raindrop's photo.
I divide the picture into 8*8 blocks and extract the edges using sobel and canny. Now I can get a rough edge.
This is the edge I got.
I can't get the fuzzy edge of the raindrop.
This fuzzy edge I can't get
//sobel
Mat SobelProcess(Mat src)
{
Mat Output;
Mat grad_x, grad_y, abs_grad_x, abs_grad_y, SobelImage;
Sobel(src, grad_x, CV_16S, 1, 0, CV_SCHARR, 1, 1, BORDER_DEFAULT);
Sobel(src, grad_y, CV_16S, 0, 1, CV_SCHARR, 1, 1, BORDER_DEFAULT);
convertScaleAbs(grad_x, abs_grad_x);
convertScaleAbs(grad_y, abs_grad_y);
addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0, Output);
//subtract(grad_x, grad_y, SobelImage);
//convertScaleAbs(SobelImage, Output);
return Output;
}
int main()
{
Mat Src;
Src = imread("rain.bmp",0)
imshow("src", Src);
Mat Gauss;
GaussianBlur(Src, Src, Size(5, 5), 0.5);
imshow("Gauss", Src);
//M * N = 8 * 8
int OtsuThresh[M * N];
vector<Mat>tempThresh = ImageSegment(Src);
for (int i = 0; i < M * N; i++)
{
OtsuThresh[i] = Otsu(tempThresh[i]); //get Otsu Threshold
}
vector<Mat>temp;
temp = ImageSegment(Src);//ImageSegment() is a function to divide the picture into 8*8 blocks
for (int i = 0; i < M * N; i++)
{
temp[i] = SobelProcess(temp[i]);
GaussianBlur(temp[i], temp[i], Size(3, 3), 0.5);
Canny(temp[i], temp[i], OtsuThresh[i] / 3, OtsuThresh[i]);
}
Mat Tem;
Tem = ImageMerge(temp);//ImageMerge() is a function to merge the blocks
imshow("Tem", Tem);
}
Then I use watershed. But I can't use it get an ideal result.
I'm trying to generate a rainbow with 15 different colors with (runnable code here):
size(360,100);
colorMode(HSB, 360, 100, 100); // Hue in degrees in [0, 360],
// saturation/brightness in [0, 100]
// like in Photoshop
noStroke();
for (int i = 0; i < 15; i++)
{
fill(i*24, 100, 100); // 24*15 = 360
rect(i*24, 0, 25, 100);
}
but it doesn't produce a rich 15 rainbow-color palette, instead some colors are missing (vivid yellow for example).
Is there a well known algorithm to produce a vivid rainbow color palette?
To understand what's going on, try creating a program that shows a line for each value 0-360:
size(360,100);
colorMode(HSB, 360, 100, 100);
noStroke();
for (int i = 0; i < 360; i++)
{
fill(i, 100, 100);
rect(i, 0, 1, 100);
}
You'll see this:
Notice that the "vivid yellow" band is much more narrow than, for example, the green or blue bands. That's why simply sampling every X values doesn't generate a yellow color.
The yellow color is around value 60, so you could modify your increment so it lands on 60. Drawing 12 rectangles with a width of 30 lets you land on the yellow:
size(360,100);
colorMode(HSB, 360, 100, 100);
noStroke();
for (int i = 0; i < 360; i++)
{
fill(i*30, 100, 100);
rect(i*30, 0, 30, 100);
}
Or you could come up with the values you want ahead of time and put them in an array instead of using an even distribution:
int[] hueValues = {0, 15, 30, 60, 90, 120, 150, 180, 210, 225, 240, 270, 300, 330, 360};
size(360,100);
colorMode(HSB, 360, 100, 100);
noStroke();
for (int index = 0; index < hueValues.length; index++)
{
float rectWidth = width/hueValues.length;
fill(hueValues[index], 100, 100);
rect(index*rectWidth, 0, rectWidth, height);
}
I created a function that generates N colors (rainbow) and outputs a list of strings (Hex values). This is in C# but logic can be converted. In order to understand what's going on I graphed the red, blue, and green values vs n. Doing that you'll see the three graphs each are piecewise functions with points of interest at n=0, n=1/4, n=1/2 and n=3/4.
List<string> GenerateRainbowPalette(int numColors)
{
var toRet = new List<SKColor>();
var n = (float)numColors;
for(var i = 0; i< numColors; i++)
{
int red = 255;
int green = 0;
int blue = 0;
//red: (first quarter)
if (i <= n / 4)
{
red = 255;
green = (int)(255 / (n / 4) * i);
blue = 0;
}
else if (i <= n / 2) //2nd quarter
{
red = (int)((-255)/(n/4)*i + 255 * 2);
green = 255;
blue = 0;
}
else if (i <= (.75)*n)
{ // 3rd quarter
red = 0;
green = 255;
blue = (int)(255 / (n / 4) * i + (-255 * 2));
}
else if(i > (.75)*n)
{
red = 0;
green = (int)(-255 * i / (n / 4) + (255 * 4));
blue = 255;
}
//generate hex string:
var redHex = red.ToString("X2");
var greenHex = green.ToString("X2");
var blueHex = blue.ToString("X2");
var color = $"#{redHex}{greenHex}{blueHex}";
toRet.Add(color);
}
return toRet;
}
I have created a small Raphael app to showcase my struggle.
I created four handles which can be moved. A 'sheet' is covering the entire screen except for the square between the 4 handles.
Whenever the handles are dragged the sheet is placed accordingly.
What ends up happening is that in certain situations, the sheet folds on itself.
It's best if you just see the fiddle. You'll get what I'm talking about
http://jsfiddle.net/8qtffq0s/
How can I avoid this?
Notice: The screen is white. The black part is the sheet, and the white part is a gap in the sheet and not the other way around.
//raphael object
var paper = Raphael(0, 0, 600, 600)
//create 4 handles
h1 = paper.circle(50, 50, 10).attr("fill","green")
h2 = paper.circle(300, 50, 10).attr("fill", "blue")
h3 = paper.circle(300, 300, 10).attr("fill", "yellow")
h4 = paper.circle(50, 300, 10).attr("fill", "red")
//create covering sheet
path = ["M", 0, 0, "L", 600, 0, 600, 600, 0, 600, 'z', "M", h1.attrs.cx, h1.attrs.cy,"L", h4.attrs.cx, h4.attrs.cy, h3.attrs.cx, h3.attrs.cy, h2.attrs.cx, h2.attrs.cy,'z']
sheet = paper.path(path).attr({ "fill": "black", "stroke": "white" }).toBack()
//keep starting position of each handle on dragStart
var startX,startY
function getPos(handle) {
startX= handle.attrs.cx
startY = handle.attrs.cy
}
//Redraw the sheet to match the new handle placing
function reDrawSheet() {
path = ["M", 0, 0, "L", 600, 0, 600, 600, 0, 600, 'z', "M", h1.attrs.cx, h1.attrs.cy, "L", h4.attrs.cx, h4.attrs.cy, h3.attrs.cx, h3.attrs.cy, h2.attrs.cx, h2.attrs.cy, 'z']
sheet.attr("path",path)
}
//enable handle dragging
h1.drag(function (dx, dy) {
this.attr("cx", startX + dx)
this.attr("cy", startY + dy)
reDrawSheet()
},
function () {
getPos(this)
})
h2.drag(function (dx, dy) {
this.attr("cx", startX + dx)
this.attr("cy", startY + dy)
reDrawSheet()
},
function () {
getPos(this)
})
h3.drag(function (dx, dy) {
this.attr("cx", startX + dx)
this.attr("cy", startY + dy)
reDrawSheet()
},
function () {
getPos(this)
})
h4.drag(function (dx, dy) {
this.attr("cx", startX + dx)
this.attr("cy", startY + dy)
reDrawSheet()
},
function () {
getPos(this)
})
Update: I improved the function "reDrawSheet" so now it can classify the points on the strings as top left, bottom left, bottom right, and top right
This solved many of my problems, but in some cases the sheet still folds on it self.
new fiddle: http://jsfiddle.net/1kj06co4/
new code:
function reDrawSheet() {
//c stands for coordinates
c = [{ x: h1.attrs.cx, y: h1.attrs.cy }, { x: h4.attrs.cx, y: h4.attrs.cy }, { x: h3.attrs.cx, y: h3.attrs.cy }, { x: h2.attrs.cx, y: h2.attrs.cy }]
//arrange the 4 points by height
c.sort(function (a, b) {
return a.y - b.y
})
//keep top 2 points
cTop = [c[0], c[1]]
//arrange them from left to right
cTop.sort(function (a, b) {
return a.x - b.x
})
//keep bottom 2 points
cBottom = [c[2], c[3]]
//arrange them from left to right
cBottom.sort(function (a, b) {
return a.x - b.x
})
//top left most point
tl = cTop[0]
//bottom left most point
bl = cBottom[0]
//top right most point
tr = cTop[1]
//bottom right most point
br = cBottom[1]
path = ["M", 0, 0, "L", 600, 0, 600, 600, 0, 600, 'z', "M", tl.x,tl.y, "L", bl.x,bl.y, br.x,br.y, tr.x,tr.y, 'z']
sheet.attr("path",path)
}
To make things super clear, this is what I'm trying to avoid:
Update 2:
I was able to avoid the vertices from crossing by checking which path out of the three possible paths is the shortest and choosing it.
To do so, I added a function that checks the distance between two points
function distance(a, b) {
return Math.sqrt(Math.pow(b.x - a.x, 2) + (Math.pow(b.y - a.y, 2)))
}
And altered the code like so:
function reDrawSheet() {
//c stands for coordinates
c = [{ x: h1.attrs.cx, y: h1.attrs.cy }, { x: h4.attrs.cx, y: h4.attrs.cy }, { x: h3.attrs.cx, y: h3.attrs.cy }, { x: h2.attrs.cx, y: h2.attrs.cy }]
//d stands for distance
d=distance
//get the distance of all possible paths
d1 = d(c[0], c[1]) + d(c[1], c[2]) + d(c[2], c[3]) + d(c[3], c[0])
d2 = d(c[0], c[2]) + d(c[2], c[3]) + d(c[3], c[1]) + d(c[1], c[0])
d3 = d(c[0], c[2]) + d(c[2], c[1]) + d(c[1], c[3]) + d(c[3], c[0])
//choose the shortest distance
if (d1 <= Math.min(d2, d3)) {
tl = c[0]
bl = c[1]
br = c[2]
tr = c[3]
}
else if (d2 <= Math.min(d1, d3)) {
tl = c[0]
bl = c[2]
br = c[3]
tr = c[1]
}
else if (d3 <= Math.min(d1, d2)) {
tl = c[0]
bl = c[2]
br = c[1]
tr = c[3]
}
path = ["M", 0, 0, "L", 600, 0, 600, 600, 0, 600, 'z', "M", tl.x,tl.y, "L", bl.x,bl.y, br.x,br.y, tr.x,tr.y, 'z']
sheet.attr("path",path)
}
Now the line does not cross itself like the image I attached about, but the sheet "flips" so everything turns black.
You can see the path is drawn correctly to connect the for points by the white stroke, but it does not leave a gap
new fiddle: http://jsfiddle.net/1kj06co4/1/
Picture of problem:
So... the trouble is to tell the inside from the outside.
You need the following functions:
function sub(a, b) {
return { x: a.x - b.x , y: a.y - b.y };
}
function neg(a) {
return { x: -a.x , y: -a.y };
}
function cross_prod(a, b) {
// 2D vecs, so z==0.
// Therefore, x and y components are 0.
// Return the only important result, z.
return (a.x*b.y - a.y*b.x);
}
And then you need to do the following once you've found tl,tr,br, and bl:
tlr = sub(tr,tl);
tbl = sub(bl,tl);
brl = sub(bl,br);
btr = sub(tr,br);
cropTL = cross_prod( tbl, tlr );
cropTR = cross_prod(neg(tlr),neg(btr));
cropBR = cross_prod( btr, brl );
cropBL = cross_prod(neg(brl),neg(tbl));
cwTL = cropTL > 0;
cwTR = cropTR > 0;
cwBR = cropBR > 0;
cwBL = cropBL > 0;
if (cwTL) {
tmp = tr;
tr = bl;
bl = tmp;
}
if (cwTR == cwBR && cwBR == cwBL && cwTR!= cwTL) {
tmp = tr;
tr = bl;
bl = tmp;
}
My version of the fiddle is here. :) http://jsfiddle.net/1kj06co4/39/