Skip to content

Commit 914ffd6

Browse files
committedMar 19, 2015
Automatic parsing of animation labels
1 parent 68f1cec commit 914ffd6

File tree

4 files changed

+187
-27
lines changed

4 files changed

+187
-27
lines changed
 

‎examples/convert.html

+3
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ <h3>Input</h3>
5353
<p class="drop-target-title" id="input_file_size">
5454
File loaded (0 kB).
5555
</p>
56+
<p class="drop-target-title" id="input_animations">
57+
Animations loaded (0).
58+
</p>
5659
</div>
5760
</div>
5861
</div>

‎examples/convert.ts

+59-27
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
/// <reference path="../external/jquery/jquery.d.ts" />
33
/// <reference path="./threejs-renderer.ts" />
44
/// <reference path="./convert-options.ts" />
5+
/// <reference path="./parse-animations.ts" />
56
/// <reference path="./stringify.ts" />
67

78
// ----------------------------------------------------------------------------
@@ -16,18 +17,20 @@ var renderer: ThreejsRenderer;
1617
interface i_conversion_data {
1718
stage: number;
1819
exception: boolean;
19-
s0_source: string; // Stage 0: raw file string
20-
s1_xml: Document; // Stage 1: XML document
21-
s2_loaded: COLLADA.Loader.Document; // Stage 2: COLLADA document
22-
s3_converted: COLLADA.Converter.Document; // Stage 3: Converted document
23-
s4_exported_custom: COLLADA.Exporter.Document; // Stage 4: JSON + binary
24-
s5_exported_threejs: any; // Stage 5: JSON
20+
s0_source: string; // Stage 0: raw file string
21+
s0_animations: parseAnimations.AnimationLabel[]; // Stage 0: animation setup
22+
s1_xml: Document; // Stage 1: XML document
23+
s2_loaded: COLLADA.Loader.Document; // Stage 2: COLLADA document
24+
s3_converted: COLLADA.Converter.Document; // Stage 3: Converted document
25+
s4_exported_custom: COLLADA.Exporter.Document; // Stage 4: JSON + binary
26+
s5_exported_threejs: any; // Stage 5: JSON
2527
}
2628

2729
var conversion_data: i_conversion_data = {
2830
stage: null,
2931
exception: null,
3032
s0_source: null,
33+
s0_animations: null,
3134
s1_xml: null,
3235
s2_loaded: null,
3336
s3_converted: null,
@@ -115,7 +118,8 @@ function reset() {
115118
}
116119

117120
function resetInput() {
118-
conversion_data.s0_source = ""
121+
conversion_data.s0_source = "";
122+
conversion_data.s0_animations = null;
119123

120124
updateUIInput();
121125
}
@@ -215,6 +219,13 @@ function updateUIInput() {
215219
$("#drop-target-instructions").removeClass("hidden");
216220
$("#convert").attr("disabled", "disabled");
217221
}
222+
223+
if (conversion_data.s0_animations) {
224+
$("#input_animations").removeClass("hidden");
225+
$("#input_animations").text("Animation labels loaded (" + conversion_data.s0_animations.length + ")");
226+
} else {
227+
$("#input_animations").addClass("hidden");
228+
}
218229
}
219230

220231
function updateUIOutput() {
@@ -294,51 +305,71 @@ function onFileDrag(ev: JQueryEventObject) {
294305
}
295306

296307
function onFileDrop(ev: JQueryEventObject) {
297-
writeProgress("Something dropped.");
308+
console.log("Something dropped.");
309+
ev.stopPropagation();
298310
ev.preventDefault();
299311
var dt = (<any>ev.originalEvent).dataTransfer;
300312
if (!dt) {
301313
writeProgress("Your browser does not support drag&drop for files (?).");
302314
return;
303315
}
304-
var files = dt.files;
305-
if (files.length == 0) {
306-
writeProgress("You did not drop a file. Try dragging and dropping a file instead.");
307-
return;
308-
}
309-
if (files.length > 1) {
310-
writeProgress("You dropped multiple files. Please only drop a single file.");
311-
return;
316+
var filelist: FileList = dt.files;
317+
var files: File[] = [];
318+
for (var i = 0; i < filelist.length; ++i) {
319+
files.push(filelist[i]);
312320
}
313321

314-
onFileLoad(files[0]);
315-
}
316-
322+
files = files.sort((a, b) => b.size - a.size);
317323

318-
function onFileLoad(file: File) {
319-
// Reset all data
320-
reset();
324+
switch (files.length) {
325+
case 0: writeProgress("You did not drop a file. Try dragging and dropping a file instead."); break;
326+
case 1: onFileLoad(files[0]); break;
327+
case 2: onFileLoad(files[0], files[1]); break;
328+
default: writeProgress("You dropped too many files. Please only drop a single file.");
329+
};
330+
}
321331

332+
function readTextFile(file: File, name: string, callback: (result: string) => void) {
322333
// File reader
323334
var reader = new FileReader();
324335
reader.onload = () => {
325-
timeEnd("Reading file");
336+
timeEnd("Reading " + name);
326337
var result: string = reader.result;
327-
convertSetup(result);
338+
callback(result);
328339
};
329340
reader.onerror = () => {
330-
writeProgress("Error reading file.");
341+
writeProgress("Error reading " + name + ".");
331342
};
332-
timeStart("Reading file");
343+
timeStart("Reading " + name);
333344

334345
// Read
335346
reader.readAsText(file);
336347
}
337348

349+
350+
function onFileLoad(file: File, animations?: File) {
351+
// Reset all data
352+
reset();
353+
354+
// Read the
355+
if (animations) {
356+
readTextFile(animations, "animations",(result) => {
357+
animationSetup(result);
358+
readTextFile(file, "file",(result2) => convertSetup(result2));
359+
});
360+
} else {
361+
readTextFile(file, "file",(result) => convertSetup(result));
362+
}
363+
}
364+
338365
// ----------------------------------------------------------------------------
339366
// Conversion
340367
// ----------------------------------------------------------------------------
341368

369+
function animationSetup(src: string) {
370+
conversion_data.s0_animations = parseAnimations.parse(src);
371+
}
372+
342373
function convertSetup(src: string) {
343374
// Set the source data
344375
conversion_data.s0_source = src;
@@ -407,6 +438,8 @@ function convertConvert() {
407438
var converterlog = converter.log = new COLLADA.LogCallback;
408439
converterlog.onmessage = (message: string, level: COLLADA.LogLevel) => { writeLog("converter", message, level); }
409440
converter.options = options;
441+
converter.options.animationLabels.value = conversion_data.s0_animations;
442+
converter.options.useAnimationLabels.value = conversion_data.s0_animations != null;
410443

411444
// Convert
412445
timeStart("COLLADA conversion");
@@ -495,7 +528,6 @@ function init() {
495528
// Register events
496529
$("#drop-target").on("dragover", onFileDrag);
497530
$("#drop-target").on("drop", onFileDrop);
498-
$("#drop-target").on("drop", onFileDrop);
499531
$("#convert").click(onConvertClick);
500532

501533
$("#output-custom-json .output-download").click(() =>

‎examples/parse-animations.ts

+119
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
module parseAnimations {
2+
3+
export class AnimationLabel {
4+
name: string;
5+
begin: number;
6+
end: number;
7+
fps: number;
8+
9+
constructor() {
10+
this.name = null;
11+
this.begin = null;
12+
this.begin = null;
13+
this.fps = null;
14+
}
15+
}
16+
17+
function isNumber(str: string): boolean {
18+
return !isNaN(parseInt(str, 10));
19+
}
20+
21+
function isString(str: string): boolean {
22+
return !isNumber(str);
23+
}
24+
25+
/** Count number of elements for which the callback returns true */
26+
function count<T>(data: T[], callback: (t: T) => boolean): number {
27+
var result = 0;
28+
data.forEach((t) => {
29+
if (callback(t)) {
30+
result++;
31+
}
32+
});
33+
return result;
34+
}
35+
36+
/** Returns true if all elements of data have the same content at the given index */
37+
function sameContent<T>(data: T[][], indexFn: (element:T[])=>number): boolean {
38+
if (data.length < 2) {
39+
return false;
40+
}
41+
var contents = data.map((line) => {
42+
var index = indexFn(line);
43+
if (index < 0 || line.length <= index) {
44+
return null;
45+
} else {
46+
return line[index];
47+
}
48+
});
49+
return contents.every((content) => {
50+
return content !== null && content === contents[0];
51+
});
52+
}
53+
54+
function guessLabel(line: string[]): AnimationLabel {
55+
var numbers = line.filter(isNumber).map((str) => parseInt(str, 10));
56+
var strings = line.filter(isString);
57+
58+
if (numbers.length < 2 || strings.length < 1) {
59+
return null;
60+
}
61+
62+
var result = new AnimationLabel;
63+
result.name = strings.join(" ");
64+
result.begin = numbers.reduce((prev, cur) => Math.min(prev, cur), Infinity);
65+
result.end = numbers.reduce((prev, cur) => Math.max(prev, cur), -Infinity);
66+
return result;
67+
}
68+
69+
export function parse(source: string): AnimationLabel[]{
70+
71+
var lines = source.split(/\r\n|[\n\v\f\r\x85\u2028\u2029]/);
72+
73+
// Remove trailing whitespace
74+
lines = lines.map((line) => line.trim());
75+
76+
// Split lines
77+
var parts = lines.map((line) => line.split(/[\s-:;,]+/));
78+
79+
// Remove invalid lines
80+
var parts = parts.filter((line) => {
81+
if (line.length < 3) return false;
82+
if (count(line, isNumber) < 2) return false;
83+
if (count(line, isString) < 1) return false;
84+
return true;
85+
});
86+
87+
// Longest line
88+
var maxLength = parts.reduce((prev, cur) => Math.max(prev, cur.length), 0);
89+
90+
// Remove parts that are the same on each line (from the beginning of each line)
91+
var i = 0;
92+
while (i < maxLength) {
93+
if (sameContent(parts,() => i)) {
94+
parts = parts.map((line) => line.filter((value, index) => index !== i));
95+
maxLength--;
96+
} else {
97+
i++;
98+
}
99+
}
100+
101+
// Remove parts that are the same on each line (from the end of each line)
102+
i = 0;
103+
while (i < maxLength) {
104+
if (sameContent(parts,(str) => str.length - i - 1)) {
105+
parts = parts.filter((value, index) => index !== i);
106+
maxLength--;
107+
} else {
108+
i++;
109+
}
110+
}
111+
112+
// Extract labels from each line
113+
var labels = parts.map((line) => guessLabel(line));
114+
115+
return labels.filter((label) => label !== null);
116+
}
117+
118+
119+
}

‎visual_studio/convert.csproj

+6
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@
2424
<TypeScriptCompile Include="..\examples\convert-options.ts">
2525
<Link>convert-options.ts</Link>
2626
</TypeScriptCompile>
27+
<TypeScriptCompile Include="..\examples\stringify.ts">
28+
<Link>stringify.ts</Link>
29+
</TypeScriptCompile>
30+
<TypeScriptCompile Include="..\examples\parse-animations.ts">
31+
<Link>parse-animations.ts</Link>
32+
</TypeScriptCompile>
2733
<TypeScriptCompile Include="..\examples\threejs-renderer.ts">
2834
<Link>threejs-renderer.ts</Link>
2935
</TypeScriptCompile>

0 commit comments

Comments
 (0)
Please sign in to comment.