-
Notifications
You must be signed in to change notification settings - Fork 0
/
enginehelpers.ts
141 lines (132 loc) · 6.04 KB
/
enginehelpers.ts
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
/// <reference path="protocols.ts" />
/// <reference path="primitives.ts" />
/// <reference path="mapobject.ts" />
namespace MapEngine {
export function transformedJoints(object:MapObjects.IJoinable,position:MapObjects.Point):MapObjects.Point[] {
let placeable = <MapObjects.IPlaceable><any>object;
let joints = object.joints;
//we have to rebuild these joints based on the position of the object.
let matrix = new DOMMatrix();
matrix.translateSelf(position.x,position.y);
if((<MapObjects.MapObject><any>object).hasFeature(MapObjects.Feature.Rotateable)) {
let rotateable = <MapObjects.IRotateable><any>object;
matrix.translateSelf(rotateable.rotationCenter.x,rotateable.rotationCenter.y);
matrix.rotateSelf(rotateable.rotation * (180/Math.PI));
joints = joints.map((value:MapObjects.Point) => {
let pt = value.subtractPoint(rotateable.rotationCenter);
return pt;
});
}
joints = joints.map((value:MapObjects.Point)=>{
return MapObjects.Point.fromDOMPoint(matrix.transformPoint(value.toDOMPoint()));
});
return joints;
}
export function snapCandidate(a:MapObjects.Point[],b:MapObjects.Point[],scale:number,threshold:number):[MapObjects.Point,MapObjects.Point] {
let result:[MapObjects.Point,MapObjects.Point] = null;
a.forEach(pointA => {
b.forEach(pointB => {
let diff = pointB.subtractPoint(pointA);
let scaledDiff = diff.scale(scale);
if(scaledDiff.length() <= threshold) {
if (result != null) {
let compare = result[0].subtractPoint(result[1]);
if(compare.length() > diff.length()) {
result = [pointA,pointB];
}
} else {
result = [pointA,pointB];
}
}
});
});
return result;
}
export function renderObject(object:MapObjects.MapObject,context:CanvasRenderingContext2D,focused:boolean=false,offscreen:CanvasRenderingContext2D=null) {
context.save();
if(object.hasFeature(MapObjects.Feature.Placeable)) {
let casted = (object as any) as MapObjects.IPlaceable;
context.translate(casted.position.x,casted.position.y);
}
if(object.hasFeature(MapObjects.Feature.Rotateable)) {
let casted = (object as any) as MapObjects.IRotateable;
context.rotate(casted.rotation);
}
object.draw(context,focused,offscreen);
context.restore();
}
export function transformPointForHitTesting(pt:MapObjects.Point,object:MapObjects.MapObject) {
let calculatedPoint = pt;
if(object.hasFeature(MapObjects.Feature.Placeable)) {
/*
this is a placed object. we have to normalize to its location
*/
let casted = <MapObjects.IPlaceable><any>(object);
let matrix = new DOMMatrix();
matrix.translateSelf(casted.position.x,casted.position.y);
matrix.invertSelf();
calculatedPoint = MapObjects.Point.fromDOMPoint(matrix.transformPoint(pt.toDOMPoint()));
}
if(object.hasFeature(MapObjects.Feature.Rotateable)) {
/*
this is a rotateable object. for easier hit testing we
build a rotation matrix and invert it.
that way we can bring the cursor back into a normalized hitbox.
*/
let casted = <MapObjects.IRotateable><any>(object);
let matrix = new DOMMatrix();
//who could've thought that canvas and DOMMatrix use different angle scales
matrix.rotateSelf(casted.rotation * (180/Math.PI));
matrix.invertSelf();
calculatedPoint = MapObjects.Point.fromDOMPoint(matrix.transformPoint(calculatedPoint.toDOMPoint()));
}
return calculatedPoint;
}
export function snapTest(object:MapObjects.MapObject,newPoint:MapObjects.Point,candidates:MapObjects.MapObject[],scaling:number):[MapObjects.Point,MapObjects.MapObject] {
/*
first things first: we have to calculate the current's objects snap points.
therefore we have to check wether it has snap points or not.
*/
if(!object.hasFeature(MapObjects.Feature.Joinable)) {
return null;
}
let joinable = <MapObjects.IJoinable><any>object;
let joints = transformedJoints(joinable,newPoint);
let snapped=false;
let snapTarget:MapObjects.MapObject;
candidates.forEach(candidate => {
if(candidate === object || snapped) {
return;
}
if(candidate.hasFeature(MapObjects.Feature.Joinable)) {
let castedcandidate = <MapObjects.IJoinable><any>candidate;
let pt = new MapObjects.Point(0,0);
if(candidate.hasFeature(MapObjects.Feature.Placeable)) {
let placeable = <MapObjects.IPlaceable><any>candidate;
pt = placeable.position;
}
let candidatejoints = transformedJoints(castedcandidate,pt);
//now we have to find candidates.
let candidates = snapCandidate(joints,candidatejoints,scaling,20);
if(candidates != null) {
let diff = candidates[1].subtractPoint(candidates[0]);
newPoint = newPoint.addPoint(diff);
snapped = true;
snapTarget = candidate;
}
}
});
if(!snapped) {
return null;
}
return [newPoint,snapTarget];
}
export function dataURLtoBlob(dataurl) {
var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
while(n--){
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], {type:mime});
}
}