How can I draw two shapes in a time sequence using Gloss? - haskell

I have a simple window displaying two shapes like so:
import Graphics.Gloss
circles = pictures [Translate 80 0 one, Translate (-80) 0 two]
main = display (InWindow "t" (400,400) (800,0)) white circles
one = Color red $ Circle 80
two = Color blue $ Circle 50
I am new to Gloss, but from what I gather "display" just displays a static image once main (i.e. my module) is run so you cant make an animation using "display" right?
What I want to do is to run my program with these shapes, but instead of displaying them both at once, I want to first display one circle, the next second the other circle like some kind of animation.
So far I can only do something static and display both circles at once immediately when the program is run. But I want them to appear after each other like Run the program -> (0 sec) Blank screen -> (1 sec) One of the circles is drawn -> (2 sec) the other circle is drawn -> The window now displays circles until I close it.
This should be so simple using the "animate" function but I can't figure it out. If anyone is out there with knowledge, please consider helping! It would really make my day.

You use animate in order to draw a picture depending on the animation time:
main = animate (InWindow "t" (400,400) (800,0)) white draw
draw :: Float -> Picture
draw t
| t <= 1 = blank -- in the first second
| t <= 2 = pictures [Translate 80 0 one] -- between 1s and 2s
| otherwise = pictures [Translate 80 0 one, Translate (-80) 0 two] -- afterwards

Related

Python 3 Tkinter canvas

I am trying to draw a clock that works. I am using a 600x600 form. I cant' figure out how to place the oval in the center of the form or how to add the minutes or the seconds tick marks inside the oval. I tried dash but couldn't get it to look right. Any suggestions. Thanks in advance.
This is what I have done so far:
from tkinter import *
canvas_width = 600
canvas_height = 600
master = Tk()
w = Canvas(master, width = canvas_width, height = canvas_height)
w.pack()
oval = w.create_oval(75,75,500,500)
minline = w.create_line(150,150,300,300)
mainloop()
The center of a drawn shape is the middle of the two points specified when it is drawn. Currently, the middle point of your shape (draw from 75, 75 to 500, 500) is 237.5, so, if you want the middle of it to be the middle of your page, and keep the 75, 75 coordinate, you would have to make the other one 525, 525 to completely mirror the first.
As for drawing the shape, you'll need some math in python, so I would first suggest doing an image as the background for the clock, so that less objects are drawn. But, if you must do it without other images, you must first import the math library.
import math
Now, for a mathematic principle: Any point on the circle of radius r can be expressed as the point (r*cosθ), (r*sinθ), where θ is the angle from the center to the point. The reason this is important is that you want each line on the side of the clock face to be pointing towards the center of the circle. To do that, we need the two points to draw the line on that together point towards the center, and fortunately for us this means that both points on the line are on different circles (our circle and one within it) but are at the same angle from the center.
Since we want 12 hour points around the circle, and 4 minute points between each of those (so 60 points in total), and 360 degrees in a circle (so 1 point for every 6 degrees), we will need a for loop that goes through that.
for angle in range(0, 360, 6):
Then we'll want 3 constants: One for the radius of the exterior circle (for the points to begin from), one for an interior circle (for the minute points to add at), and one for an even more interior circle (for the hour points to end at). We'll also want it to choose the more interior radius only every 30 degrees (because it appears every 5 points, and there are 6 degrees between them).
radius_out = 225
radius_in = 0 #temporary value
if (angle % 30) == 0: #the % symbol checks for remainder
radius_in = 210
else:
radius_in = 220
Now, for the conversion into radians (As math in python needs radians for sin and cos):
radians = (angle / 180) * math.pi
Next off, assigning the coordinates to variables so it's easier to read.
x_out = (radius_out * math.cos(radians)) + 300
y_out = (radius_out * math.sin(radians)) + 300
x_in = (radius_in * math.cos(radians)) + 300
y_in = (radius_in * math.sin(radians)) + 300
#the (+ 300) moves each point from a relative center of 0,0 to 300,300
And finally we assign it to a list so we can access it later if we need to. Make sure to define this list earlier outside of the for loop.
coords.append( w.create_line(x_out, y_out, x_in, y_in) )
This should give you your clock lines.
NOTE: Due to the way tkinter assigns x and y coordinates, this will draw lines from the 3 hour line clockwise back to it.
Hope this was helpful! If there is anything you don't understand, comment it below.

How to draw concentric hexagons?

I need to draw concentric hexagons (4 to 10 in each set) randomly using Python turtle graphics (see image). I can draw random hexagons but can not make concentric ones:
import turtle
from random import randint
window = turtle.Screen()
window.bgcolor("yellow")
brad= turtle.Turtle()
brad.color("blue")
window.colormode(255)
def drawPoly(sideLen, noOfsides):
for i in range(noOfsides):
brad.forward(sideLen)
brad.left(360/noOfsides)
for i in range(20):
sideLen = randint(20,150)
xpos = randint(-200,200)
ypos = randint(-200,200)
brad.pensize(randint(1,3))
brad.pencolor(randint(1,255),randint(1,255),randint(1,255))
brad.penup()
brad.setpos(xpos,ypos)
brad.pendown()
drawPoly(sideLen,6)
window.exitonclick()
Here's a link to image
How can I make hexagons within a hexagon for 4 to 10 times and then move to the next hexagon?
The concept of an mcve applies to development as well as posting here. Start with a simple function or block of code that makes concentric hexagons without worrying about color, thickness, or style of the lines.
The key thing about hexagons is that the 'radius' (center to corners) is the same as the edge length (corner to corner). (Concentric pentagons, for instance, are harder, requiring some trig.) Assume you want two corners on the x axis. If the center is x,y, start at x+e,y at a rotation of 120 (where 0 is facing right). Given x, y, a list edges of radii-edge lengths, and turtle t in a pen-up state, the following should work.
for edge in edges:
t.setpos(x+e, y)
t.setheading(120)
t.pendown()
for i in range(6):
t.forward(e)
t.left(60)
t.penup()
EDIT: replace setangle with setheading, thanks to comment by cdlane.

Prevent to patch if the area is already filled

This is quite a tough challenge I have with my code. First of all the code I am putting here is not runnable because I am using an Excel sheet (but I am happy to email it if people want to try using my code).
What I have is an Excel sheet with data on cross-sectional fibres in a microscopic image I took. The information is basically: location of the section, area, angle of rotation.
From that I calculate the angle of orientation Phi, and Gamma. After that I use the scatter function to plot a dot of different colors for each Phi angle value. I use a constant color for a range of 10 degrees. Which gives me a picture like this:
Now my aim to is calculate the area of each homogeneous region. So I look for a way to plot let's say all the dots within the -10 +10 region (I'm doing 20 degrees for now, but will do 10 after). I used a look and I get a picture like this:
The white corresponds where the dots are within the range I selected. After that I use the toolbox in MATLAB to convert each dot into a pixel. So I'll get a black background with loads of white pixels, then I use imdilate to make circles, fill holes and isolate each region with a specific color. Finally I use the functions boundary and patch, to create each boundary and fill them with a color. And I get a picture like this:
Which is what I want and I can get the area of each region and the total area (I used a threshold to discard the small areas). Then I run the code several time for each region, and I use imfuse to put them back together and see what it looks like.
THE PROBLEM is, they overlap quite a lot, and that is because there are some errors in my data, and therefore some blue dots will be in the red and so on.
So I want to run the code once, then when I rerun it with another range, it does the same thing but doesn't take into account value when there's already something plotted before.
I tried to do that by, after running once, saving the matrix bw4 and adding a condition when plotting the black and white pic, by saying if Phi is in my range AND there no white here then you can put white, otherwise it's black. But it doesn't seem to work.
I understand this is quite a complicated thing to explain, but I would appreciate any ideas, and open to chat via email or otherwise. I am putting the full code now, and I can send you my Excel sheet if you want to run it on your computer and see for yourself.
clearvars -except data colheaders bw4
close all
clc
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% CHANGE DATA FOR EACH SAMPLE %%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
cd 'C:\Users\dkarta\Desktop\Sample 12\12.6'
data=xlsread('Sample12_6res.xlsx');
cd 'C:\Users\dkarta\Documents\MATLAB'
%data=Sample121res; % Data name
imax=length(data); % Numbers of rows in data sheet
y=11900; % Number of pixels in the y on image j
%%
data(:,15)=data(:,9)*pi/180; % Convers Column 9 (angle of rotation) in rads
data(:,16)=y-data(:,6); % Reset the Y coordinate axis to bottom left
delta = 0 : 0.01 : 2*pi; % Angle in paramteric equations
theta=45*pi/180; % Sample cutting angle in rads
%AA=[data(:,5)' data(:,16)' phi']
% Define colors
beta=acos(data(1:imax,8)./data(1:imax,7));%./acos(0);
phi=atan(sin(beta).*cos(data(1:imax,15))./(sin(theta)*sin(beta).*sin(data(1:imax,15))+cos(theta)*cos(beta)))/(pi/2);
phi2=phi/2+1/2; % Scales in plane angle phi between 0 and 1
gamma=atan((cos(theta)*sin(beta).*sin(data(1:imax,15))-sin(theta)*cos(beta))./...
(sin(theta)*sin(beta).*sin(data(1:imax,15))+cos(theta)*cos(beta)))/(pi/2);
gamma2=gamma+1/2; % Scales out of plane angle gamma between 0 and 1
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% MESHGRID AND COLOURMAP %%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
x1=data(1:imax,5);
y1=data(1:imax,16);
z1=phi*90;
z2=gamma*90;
n=300;
%Create regular grid across data space
[X,Y] = meshgrid(linspace(min(x1),max(x1),n), linspace(min(y1),max(y1),n));
% Creating a colormap with 10 degree constant colors
map4=[0 0 1;0 1/3 1;0 2/3 1; 0 1 1;0 1 2/3;0 1 1/3;0 1 0;1/3 1 0;2/3 1 0;1 1 0;1 0.75 0;1 0.5 0;1 0.25 0;1 0 0;0.75 0 0.25;0.5 0 0.5;0.25 0 0.75; 0 0 1];
Colormap4=colormap(map4);
h=colorbar;
caxis([-90 90])
set(h, 'YTick', [-90:10:90])
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% PLOT USING SCATTER - ISOLATE SOME REGIONS %%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
a=-10; % Lower boundary for angle interval
b=10; % Upper boundary for angle interval
c=z1>a & z1 < b;
c=c.*1;
%j=1;
y1=(y1-min(y1)+1);
y2=max(y1)-y1+1;
[X1,Y1]=meshgrid(1:500,1:500);
griddata(x1,y2,c,X1,Y1);
clear c1
for i=1:imax
if z1(i)< b && z1(i)> a %&& bw4(round(y1(i)),round(x1(i))) == 0
c(i) = 1;
c1(round(y2(i)),round(x1(i)))=1;
else
c(i)= 0;
c1(round(y2(i)),round(x1(i)))=0;
end
end
C=[c c c];
%c(find(c==0)) = NaN;
%contourf(X,Y,griddata(x1,y1,c,X,Y),100,'EdgeColor', 'None')
figure(1), scatter(x1,y1,3,z1,'filled');
axis equal
axis ([0 8000 0 12000])
axis off
figure(2), scatter(x1,y1,3,C,'filled');
axis equal
axis ([0 8000 0 12000])
axis off
se=strel('disk',50,8);
bw2=imdilate(c1,se);
bw4=bwlabel(bw2);
bw3=imfill(bw4,'holes');
max(bw4(:));
figure(3),imshow(c1,'InitialMagnification', 10);
figure(4), imshow(bw2,'InitialMagnification', 10);
figure(5), imshow(bw3,'InitialMagnification', 10);
figure(6),imshow(label2rgb(bw4),'InitialMagnification', 10);
k=ones(max(bw4(:)),1);
clear bw5
for i=1:length(x1)
if bw3(round(y2(i)),round(x1(i))) ~= 0
m=bw3(round(y2(i)),round(x1(i)));
bw5{m}(k(m),1)=x1(i); bw5{m}(k(m),2)=y2(i);
k(m)=k(m)+1;
end
end
figure(7), imshow(~c1,'InitialMagnification', 10);
hold on
for i=1:max(bw4(:))
%scatter(bw5{i}(:,1),bw5{i}(:,2))
j = boundary(bw5{i}(:,1),bw5{i}(:,2),0.5);
%poly=convhull(bw5{i}(:,1),bw5{i}(:,2));
%plot(bw5{i}(poly,1),bw5{i}(poly,2)), title('convhull')
if polyarea(bw5{i}(j,1),bw5{i}(j,2))> 10^5;
patch(bw5{i}(j,1),bw5{i}(j,2),'r'), title('boundary')
indexminy(i)=find(min(bw5{i}(:,2)) == bw5{i}(:,2));
indexminx(i)=find(min(bw5{i}(:,1)) == bw5{i}(:,1));
indexmaxy(i)=find(max(bw5{i}(:,2)) == bw5{i}(:,2));
indexmaxx(i)=find(max(bw5{i}(:,1)) == bw5{i}(:,1));
%xmin = bw5{i}(indexminx); xmax = bw5{i}(indexmaxx);
%ymin = bw5{i}(indexminy); ymax = bw5{i}(indexmaxy);
str=[(indexminx(i)+indexmaxx(i))/2,(indexminy(i)+indexmaxy(i))/2,'Region no.',num2str(i)];
text((min(x1(i))+max(x1(i)))/2,(min(y1(i))+max(y1(i)))/2,str)
polya(i)=polyarea(bw5{i}(j,1),bw5{i}(j,2));
end
end
spolya=sum(polya(:))
print -dpng -r500 B
Just to show you more pictures of when I fuse several of them:
And when I fuse:
As you can see they overlap, which I don't want, so I want each image that I create to 'know' what I'm doing on the previous runs so that it doesn't overlap. I want to get the percentage area of each region and if they overlap I cannot use the actual total area of my sample and the results are wrong.
I dont have my matlab working but here is what you need to do.
For the first run make an array of zeros equal to your image size
already_taken = zeros(size(bw3));
Then on each run, you can fill up the regions taken by this iteration. So at the end of your code, where you save the output to a png, read it back into something like
this_png = rgb2gray(imread(current_png_path))>threshold;
Convert this into a logical array by doing some thresholding and add these values into already taken. So at the end of the code, do a
already_taken = already_taken | this_png; % You might need to check if you need a single | or a double ||
So now you have an image of already taken pixels, ill bake sure I don't allow bw2 to take these values at first place
bw2(already_taken) = 0;
And at the end of the code when I want to write my png, my smart boundary creation might again have entered into already_taken area so there again I'll have to put some check. As far as I understand, this boundary is being created based upon your bw5. So where ever you fill this matrix, try putting a similar check as I did above for bw2.
I hope this helps.

Determine if tap is within circular area

In my app I am currently able to work out whether the user's tap is within a rectangular area simply by checking all of the following conditions are true:
Finger X > rectangle X
Finger Y > rectangle Y
Finger X < rectangle X + rectangle Width
Finger Y < rectangle Y + rectangle Height
However, I now have to determine if the user taps within a circular area. Currently I have a circular shape on screen and have resorted to just checking it's bounding rectangle, which works but obviously isn't great.
Any help would be appreciated.
The distance between two points in two dimensions is defined as
dist = sqrt((x2-x1)^2 + (y2-y1)^2)
To check if your tap point is inside a circle, take the centre of your circle as (x1,y1), and the 'tap location' as (x2,y2), and check if
sqrt((x2-x1)^2 + (y2-y1)^2) < R
With R being the radius of your circle.
Edit:
As John mentioned, from a computational point of view it is more interesting to compare vs R^2, to avoid the sqrt for every tap. So the condition becomes:
(x2-x1)^2 + (y2-y1)^2 < R^2

How to find custom shape speicific area?

please , see following image, here you can see blue rectangle is custom shape bounds and custom shape is shoe , i want to find area of a portion written in image and i want that area in form of rectangle
do is there any path iterator concept ?
Note
custom shape i derived from image of the same size.
I would do it like this:
1.create table for all bounding box-rect perimeter lines
each value in it will represent the empty space length form border line to shape
something like this:
the values are found by simple image scanning until first non space color found
2.now bruteforce find the biggest rectangle area
x,y = top left corner
for xs = 1 to bounding box width
now scan the max valid height of rectangle from x to x + xs (x grows to the right)
// it should be the min y0[x..x+xs]
remember the biggest valid area/size combination
do this for all 4 combinations (star from the other corners)
I now Brute-force is slow but
you can divide perimeter lines not by pixels but with some step instead
also I am sure this can be optimized somehow
for example by derivation of perimeter find the extremes and check from them backwards
when the size will start shrinking then stop ...
of course take in mind that on complicated shapes this optimization will not work ...

Resources