given an array of points my program should in theory, Find the two furthest points from each other. Then calculate the angle that those two points make with the x axis. Then in rotate all the points in the array around the averaged center of all the points by that angle. For some reason my translation function to rotate all the points around the center is not working it is giving me unexpected values. I am fairly sure the math I am using to do this is accurate since I tested the formula I am using using wolfram alpha and plotted the points on desmos. I am not sure what's wrong with my code because it keeps giving me unexpected output. Any help would greatly be appreciated.
This is the code to translate the array:
def translation(array,centerArray):
array1=array
maxDistance=0
point1=[]
point2=[]
global angle
for i in range(len(array1)):
for idx in range(len(array1)):
if(maxDistance<math.sqrt(((array1[i][0]-array1[idx][0])**2)+((array1[i][1]-array1[idx][1])**2)+((array1[i][2]-array1[idx][2])**2))):
maxDistance=math.sqrt(((array1[i][0]-array1[idx][0])**2)+((array1[i][1]-array1[idx][1])**2)+((array1[i][2]-array1[idx][2])**2))
point1 = array1[i]
point2 = array1[idx]
angle=math.atan2(point1[1]-point2[1],point1[0]-point2[0]) #gets the angle between two furthest points and xaxis
for i in range(len(array1)): #this is the problem here
array1[i][0]=((array[i][0]-centerArray[0])*math.cos(angle)-(array[i][1]-centerArray[1])*math.sin(angle))+centerArray[0] #rotate x cordiate around center of all points
array1[i][1]=((array[i][1]-centerArray[1])*math.cos(angle)+(array[i][0]-centerArray[0])*math.sin(angle))+centerArray[1] #rotate y cordiate around center of all points
return array1
This is the code I am using to test it. tortose is what I set turtle graphics name as
tortose.color("violet")
testarray=[[200,400,9],[200,-100,9]] #array of 2 3d points but don't worry about z axis it will not be used for in function translation
print("testsarray",testarray)
for i in range(len(testarray)): #graph points in testarray
tortose.setposition(testarray[i][0],testarray[i][1])
tortose.dot()
testcenter=findCenter(testarray) # array of 1 point in the center of all the points format center=[x,y,z] but again don't worry about z
print("center",testcenter)
translatedTest=translation(testarray,testcenter) # array of points after they have been translated same format and size of testarray
print("translatedarray",translatedTest) #should give the output [[-50,150,9]] as first point but instead give output of [-50,-99.999999997,9] not sure why
tortose.color("green")
for i in range(len(testarray)): #graphs rotated points
tortose.setposition(translatedTest[i][0],translatedTest[i][1])
tortose.dot()
print(angle*180/3.14) #checks to make sure angle is 90 degrees because it should be in this case this is working fine
tortose.color("red")
tortose.setposition(testcenter[0],testcenter[1])
tortose.dot()
find center code finds the center of all points in array don't worry about z axis since it is not used in translation:
def findCenter(array):
sumX = 0
sumY = 0
sumZ = 0
for i in range(len(array)):
sumX += array[i][0]
sumY += array[i][1]
sumZ += array[i][2]
centerX= sumX/len(array)
centerY= sumY/len(array)
centerZ= sumZ/len(array)
#print(centerX)
#print(centerY)
#print(centerZ)
centerArray=[centerX,centerY,centerZ]
return centerArray
import math
import turtle
tortose = turtle.Turtle()
tortose.penup()
my expected output should be a point at (-50,150) but it is giving me a point at (-50,-99.99999999999997)
This is a common mistake when doing in-place rotations:
array1[i][0]= ...
array1[i][1]= ... array[i][0] ...
First you update array1[i][0]. Then you update array1[i][1], but you use the new value when you should use the old value. Instead, temporarily store the old value:
x = array1[i][0]
array1[i][0]=((array[i][0]-centerArray[0])*math.cos(angle)-(array[i][1]-centerArray[1])*math.sin(angle))+centerArray[0] #rotate x cordiate around center of all points
array1[i][1]=((array[i][1]-centerArray[1])*math.cos(angle)+(x-centerArray[0])*math.sin(angle))+centerArray[1] #rotate y cordiate around center of all points
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.
I have a graph with zoom features. My main observation was that the x-axis updated its scale based on my current zoom level. I wanted the y-axis to do this too, so enabled zoom.y(y) , the undesired side affect being that now the user can zoom out in all directions, even into negative values "below" the graph.
http://jsfiddle.net/ericps/xJ3Ke/5/
var zoom = d3.behavior.zoom().scaleExtent([0.2, 5])
.on("zoom", draw); doesn't seem to really take the y-axis into account. And the user can still drag the chart anywhere in any direction to infinity.
One idea I thought of was independent of having zoom.y(y) enabled, and simply requires redrawing the y-axis based on what it is in the currently visible range. Like some kind of redraw based on the position of the X axis only. I don't want up and down scrolling at all now, only left and right
aside from commenting out //zoom.y(y) how would this be done? Insight appreciated.
All you need to do is update the y scale domain in your draw method.
The zoom function will modify the associated scales and set their domain to simulate a zoom. So you can get your x visible data bounds by doing x.invert(0) and x.invert(width), for example. If you converted your data to use Date's instead of strings then this is what I would suggest you use to filter, it woudl probably be more efficient.
As it is though, you can still use the x scale to filter to your visible data, find the y-axis extents of those values, and set your y scales domain to match accordingly. And in fact you can do all this in just a few lines (in your zoom update callback):
var yExtent = d3.extent(data.filter(function(d) {
var dt = x(d.date);
return dt > 0 && dt < width;
}), function(d) { return d.value; });
y.domain(yExtent).nice();
You can try it out here
To better explain what is going on:
The zoom behaviour listens to mouse events and modifies the range of the associated scales.
The scales are used by the axes which draw them as lines with ticks, and the scales are also used by the data associated with your paths and areas as you've set them up in callbacks.
So when the zoom changes it fires a callback and the basic method is what you had:
svg.select("g.x.axis").call(xAxis);
svg.select("g.y.axis").call(yAxis);
svg.select("path.area").attr("d", area);
svg.select("path.line").attr("d", line);
we redraw the x- and y- axes with the newly updated domains and we redraw (recompute) the area and the line - also with the newly domained x- and y- scales.
So to get the behaviour you wanted we take away the default zoom behaviour on the y scale and instead we will modify the y scales domain ourselves whenever we get a zoom or pan: conveniently we already have a callback for those actions because of the zoom behaviour.
The first step to compute our y scale's domain is to figure out which data values are visible. The x axis has been configured to output to a range of 0 to width and the zoom behaviour has updated the x scale's domain so that only a subset of the original domain outputs to this range. So we use the javascript array's filter method to pull out only those data objects whose mapping puts them in our visible range:
data.filter(function(d) {
var dt = x(d.date);
return dt > 0 && dt < width;
}
Then we use the handy d3 extent method to return the min and max values in an array. But because our array is all objects we need an accessor function so that the extents method has some numbers to actually compare (this is a common pattern in D3)
d3.extents(filteredData, function(d) { return d.value; });
So now we know the min and max values for all the data points that are drawn given our current x scale. The last bit is then just to set the y scale's domain and continue as normal!
y.domain(yExtent).nice();
The nice method I found in the api because it's the kind of thing you want a scale to do and d3 often does things for you that you want to do.
A great tutorial for figuring out some of this stuff is: http://alignedleft.com/tutorials/
It is worth stepping through even the parts you think you know already.
Given a starting hex code, I would like to know the maths to calculate the linear values of lightness in ascending and descending order. Same for Hue and Saturation.
It's kinda difficult for me to describe exactly what i want, forutnately i've found this page which make use of the exact algorithms i need:
http://www.workwithcolor.com/hsl-color-schemer-01.htm
If you checked the page you noticed that the last 3 redio buttons read: Linear by Hue, Linear by Saturation, Linear by Lightness. Each, gives you a list of hex codes in ascending order that correspond to the original hex code.
For example, for the lightness they give the following list (from color FFCE2E):
FFCE2E FFDA61 FFE694 FFF2C7 FFFEFA
I need the formulas, please.
Thanks in advance.
You can mash this up from multiple places. In a nutshell you need:
The HSL value of your picked color. Maybe this is obtained by converting an RGB to HSL (How do you get the hue of a #xxxxxx colour?) or on the website you just pick it on a palette
Now you have the 3 component (H, S, and L) and depending on which checkbox you choose, you start to decrement the component by the % value given in the edit box.
You'll obtain a list of values during this decrement and you'll now do a reverse conversion from the HSL value to the RGB (HSL to RGB color conversion).
// I gonna use rgbToHsl and hslToRgb from https://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion
var initialRGB = [ir, ig, ib];
var initialHSL = rgbToHsl(initialRGB[0], initialRGB[1], initialRGB[2]);
var howManyVariants = 4;
var decrementPercent = 0.1; // 10%
// This example is for hue change
var decrement = initialHSL[0] * decrementPercent;
for (var i = 0; i < howManyVariants; i++) {
// Linear decrementation
var nextHue = initialHSL[0] - i * decrement;
var nextColor = hslToRgb(nextHue, initialHSL[1], initialHSL[2]);
// visualize somehow
}
Similarly, if you want to have a set of variation by saturation then you decrement only the second parameter/component, and if you want vary luminescence, you vary the 3rd parameter.
Hope this is clear.
Given a contiguous drawing of arbitrary pixels (e.g. on an HTML5 Canvas) is there any algorithm for finding the axis-aligned bounding box that is more efficient than simply looking at every pixel and recording the min/max x/y values?
Just scanline from top left to right and down to get y top,and similar algorithm with different directions for the rest.
Edit by Phrogz:
Here's a pseudo-code implementation. An included optimization ensures that each scan line does not look at pixels covered by an earlier pass:
function boundingBox()
w = getWidth() # Assuming graphics address goes from [0,w)
h = getHeight() # Assuming graphics address goes from [0,h)
for y=h-1 to 0 by -1 # Iterate from last row upwards
for x=w-1 to 0 by -1 # Iterate across the entire row
if pxAt(x,y) then
maxY=y
break # Break out of both loops
if maxY===undefined then # No pixels, no bounding box
return
for x=w-1 to 0 by -1 # Iterate from last column to first
for y=0 to maxY # Iterate down the column, up to maxY
if pxAt(x,y) then
maxX=x
break # Break out of both loops
for x=0 to maxX # Iterate from first column to maxX
for y=0 to maxY # Iterate down the column, up to maxY
if pxAt(x,y) then
minX=x
break # Break out of both loops
for y=0 to maxY # Iterate down the rows, up to maxY
for x=0 to maxX # Iterate across the row, up to maxX
if pxAt(x,y) then
minY=y
break # Break out of both loops
return minX, minY, maxX, maxY
The result (in practice) performs about the same as the brute-force algorithm for a single pixel, and significantly better as the object gets larger.
Demo: http://phrogz.net/tmp/canvas_bounding_box2.html
For fun, here's a visual representation of how this algorithm works:
It doesn't matter in what order you choose to do the sides, you just have to make sure that you take the previous results into account so that you are not double-scanning the corners.
You might be able to use some kind of binary search, or sample on a coarse grid then a successively finer grid. The correctness of this method depends on if 'holes' are allowed in your drawing.
I dislike the current answer. Here's my code that I plugged into OP website. It's much faster in firefox and chrome.
The idea is check all pixels on x axis to see if there's a hit on the Y axis. If so update Y and increase X so we can scan for max X
function contextBoundingBox(ctx,alphaThreshold){
if (alphaThreshold===undefined) alphaThreshold = 15;
var w=ctx.canvas.width,h=ctx.canvas.height;
var data = ctx.getImageData(0,0,w,h).data;
let minX=w;
let maxX=0
let minY=h
let maxY=0
for(let y=0; y<h; y++)
{
for(let x=0; x<w; x++)
{
if (data[y*w*4 + x*4+3])
{
minX = Math.min(minX, x);
maxX = Math.max(maxX, x);
minY = Math.min(minY, y);
maxY = y;
x=maxX
}
}
}
return {x:minX,y:minY,maxX:maxX,maxY:maxY,w:maxX-minX,h:maxY-minY};
}