-
Notifications
You must be signed in to change notification settings - Fork 0
/
map.js
executable file
·219 lines (206 loc) · 6.38 KB
/
map.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
"use strict";
/*
creating the mapping data
with a transform of the coordinates
that is: (i,j) are translated and scaled to give (x,y)
additional boolean for distinguishing valid/invalid positions
*/
function Map(fastFunction){
this.width=0;
this.height=0;
this.transform=new Transform(fastFunction);
this.imagePositionX=[]; // for each pixel a pixel position on the input image
this.imagePositionY=[]; // for each pixel a pixel position on the input image
this.colorPositionX=[];
this.colorPositionY=[];
this.positionValid=[];
this.isValid=false; // flag, to request remapping
}
/*
set the size, update the data array, fill with new MapOutput objects
increases only the data array
only called if size changes
*/
Map.prototype.setSize=function(width,height){
var oldLength,newLength;
var oldWidth,oldHeight;
oldWidth=this.width;
oldHeight=this.height;
width=Math.round(width);
height=Math.round(height);
this.width=width;
this.height=height;
this.isValid=false; // presumable size changes and map has to be redone
// update scale and offset
if (this.imagePositionX.length!=0){ // update to new dimensions
this.transform.shiftX*=width/oldWidth;
this.transform.shiftY*=height/oldHeight;
this.transform.scale*=Math.sqrt((oldWidth*oldWidth+oldHeight*oldHeight)/
(width*width+height*height));
}
// increase data size if needed, do not shrink
oldLength=this.imagePositionX.length;
newLength=width*height;
if (oldLength<newLength){
this.imagePositionX.length=newLength;
this.imagePositionY.length=newLength;
this.colorPositionX.length=newLength;
this.colorPositionY.length=newLength;
this.positionValid.length=newLength;
}
}
/*
set the initial relative origin - that is the shift of the transform
the size of the map has to be initialized
*/
Map.prototype.setRelativeOrigin=function(x,y){
this.transform.setShift(-x*this.width,-y*this.height);
}
/*
initialization: set the intervall length (range) for coordinates assuming a square canvas/map
if relative origin =(0,0) then pixel (i,j)=(0,0) lies at (0,0) and inexistant pixel (width,width) would lie at (range,range)
*/
Map.prototype.setRange=function(range){
this.transform.setScale(range/this.width);
}
/*
make the mapp based on supplied map method(inputImagePosition,colorPosition,spacePosition,canvasPosition)
includes efficient offset and scaling
for limited range given by corner components in map pixel space
mapMethod sets inputImagePosition and colorPosition
returns true if all ok , false if position not valid
=========================================================
*/
Map.prototype.makeMapRegion=function(mapMethod,xMin,yMin,xMax,yMax){
this.isValid=true;
var h;
xMin=Math.min(Math.max(0,xMin),this.width-1);
xMax=Math.min(Math.max(0,xMax),this.width-1);
if (xMax<xMin){
h=xMax;
xMax=xMin;
xMin=h;
}
yMin=Math.min(Math.max(0,yMin),this.height-1);
yMax=Math.min(Math.max(0,yMax),this.height-1);
if (yMax<yMin){
h=yMax;
yMax=yMin;
yMin=h;
}
var transform=this.transform;
var height=this.height;
var width=this.width;
var iWidth=1.0/(width-1);
var iHeight=1.0/(height-1);
var spacePositionX,spacePositionY;
var canvasPositionX,canvasPositionY;
var spacePosition=new Vector2(); // pixel position in virtual space
var canvasPosition=new Vector2(); //relative pixel position on canvas (0,0)....(1,1), independent of offset
var imagePositionX=this.imagePositionX;
var imagePositionY=this.imagePositionY;
var imagePosition=new Vector2();
var colorPositionX=this.colorPositionX;
var colorPositionY=this.colorPositionY;
var colorPosition=new Vector2();
var positionValid=this.positionValid;
var scale=this.transform.scale;
var scaleShiftX=scale*this.transform.shiftX;
var scaleShiftY=scale*this.transform.shiftY;
canvasPositionY=(yMin+0.5)*iHeight;
spacePositionY=scaleShiftY+yMin*scale;
var i,j;
var index=0;
for (j=yMin;j<=yMax;j++){
canvasPositionX=(xMin+0.5)*iWidth;
spacePositionX=scaleShiftX+xMin*scale;
index=j*width+xMin;
for (i=xMin;i<=xMax;i++){
spacePosition.x=spacePositionX;
spacePosition.y=spacePositionY;
spacePosition.valid=true
canvasPosition.x=canvasPositionX;
canvasPosition.y=canvasPositionY;
positionValid[index]=mapMethod(imagePosition,colorPosition,spacePosition,canvasPosition);
imagePositionX[index]=imagePosition.x;
imagePositionY[index]=imagePosition.y;
colorPositionX[index]=colorPosition.x;
colorPositionY[index]=colorPosition.y;
index++;
canvasPositionX+=iWidth;
spacePositionX+=scale;
}
canvasPositionY+=iHeight;
spacePositionY+=scale;
}
}
/*
make the entire map
*/
Map.prototype.make=function(mapMethod){
this.makeMapRegion(mapMethod,0,0,this.width-1,this.height-1);
}
/*
trivial test method: identity
*/
Map.prototype.identity=function(inputImagePosition,colorPosition,spacePosition,canvasPosition){
inputImagePosition.x=spacePosition.x;
inputImagePosition.y=spacePosition.y;
inputImagePosition.valid=true;
}
/*
look up map at position
using linear interpolation/extrapolation to get intervall [0,range) with some safety margin upwards
taking care that indices i=0...width-1, j=0...height-1
return true if position is inside, false if outside
*/
Map.prototype.imagePosition=function(position){
var transform=this.transform;
var x,y;
var epsilon=0.01;
var h,k;
var dx,dy;
var i00, i01, i10, i11;
var f00, f01, f10, f11;
var imagePositionX=this.imagePositionX;
var imagePositionY=this.imagePositionY;
// untransform to pixel coordinates and get base index, check if is inside
x=position.x/transform.scale-transform.shiftX;
if ((x > this.width+epsilon)||(x<-epsilon)){
return false;
}
y=position.y/transform.scale-transform.shiftY;
if ((y>this.height+epsilon)||(y<-epsilon)){
return false;
}
h=Math.floor(x);
console.log(h);
if (h<0){
h=0;
}
else if (h>=this.width-1){
h=this.width-2;
}
dx=x-h;
k=Math.floor(y);
if (k<0){
k=0;
}
else if (k>=this.height-1){
k=height-2;
}
dy=y-k;
// the indices to pixel data
i00 = h+this.width*k;
i01 = i00+this.width;
i10 = i00+1;
i11 = i01+1;
// the weights
f00 = (1 - dx) * (1 - dy);
f01 = (1 - dx) * dy;
f10 = dx * (1 - dy);
f11 = dy * dx;
position.x=f00*imagePositionX[i00]+f01*imagePositionX[i01]+f10*imagePositionX[i10]+f11*imagePositionX[i11];
position.y=f00*imagePositionY[i00]+f01*imagePositionY[i01]+f10*imagePositionY[i10]+f11*imagePositionY[i11];
return true;
}